├── .clang-format ├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── cli ├── CMakeLists.txt └── Main.cpp ├── lib ├── CMakeBackend.cpp ├── CMakeBackend.h ├── CMakeLists.txt ├── CMakeTargetMachine.cpp ├── CMakeTargetMachine.h ├── CheckNeedGotoPass.cpp ├── CheckNeedGotoPass.h └── LLVMBuild.txt └── testcase ├── Fib.cpp ├── Test.cmake ├── Test.ll ├── TestCpp.cpp ├── TestRust.rs └── VirtualTest.cpp /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | AllowShortBlocksOnASingleLine: false 3 | AllowShortCaseLabelsOnASingleLine: false 4 | AllowShortFunctionsOnASingleLine: None 5 | BreakBeforeBraces: Custom 6 | BraceWrapping: 7 | AfterClass: true 8 | AfterControlStatement: true 9 | AfterEnum: true 10 | AfterFunction: true 11 | AfterNamespace: true 12 | AfterObjCDeclaration: true 13 | AfterStruct: true 14 | AfterUnion: true 15 | AfterExternBlock: true 16 | BeforeCatch: true 17 | BeforeElse: true 18 | IndentBraces: false 19 | SplitEmptyFunction: true 20 | SplitEmptyRecord: true 21 | SplitEmptyNamespace: true 22 | Cpp11BracedListStyle: false 23 | FixNamespaceComments: true 24 | IndentPPDirectives: AfterHash 25 | Language: Cpp 26 | NamespaceIndentation: All 27 | SpaceAfterCStyleCast: true 28 | Standard: Cpp11 29 | UseTab: ForIndentation 30 | TabWidth: 2 31 | PointerAlignment: Left 32 | ColumnLimit: 100 33 | AlwaysBreakTemplateDeclarations: Yes 34 | 35 | ... 36 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | build 3 | testcase/*.cmake 4 | testcase/TestCpp.ll 5 | testcase/TestRust.ll 6 | testcase/VirtualTest.ll 7 | testcase/Fib.ll 8 | _LLVM_CMAKE_* 9 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | 3 | project(LLVMCMakeBackend CXX) 4 | 5 | find_package(LLVM REQUIRED CONFIG) 6 | include_directories(SYSTEM ${LLVM_INCLUDE_DIRS}) 7 | add_definitions(${LLVM_DEFINITIONS}) 8 | 9 | list(APPEND CMAKE_MODULE_PATH ${LLVM_CMAKE_DIR}) 10 | include(AddLLVM) 11 | 12 | add_subdirectory(lib) 13 | add_subdirectory(cli) 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Natsu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | LLVMCMakeBackend 2 | ==== 3 | 4 | An LLVM backend to CMake, you can compile C++/Rust into CMake now! 5 | 6 | Well, this is a stupid project, but implementing it is a funny experience, and it is a challenge that implements LLVM IR in a restricted environment. 7 | 8 | Building 9 | ---- 10 | 11 | This project depends on LLVM 13. So installing it first is needed, by `apt install llvm-13-dev` (add [llvm apt source](https://apt.llvm.org/) first) or building them yourself. Then run 12 | 13 | ``` 14 | mkdir build && cd build 15 | cmake .. && cmake --build . --parallel 16 | ``` 17 | 18 | Usage 19 | ---- 20 | 21 | Run `./LLVMCMakeBackend Test.ll`, and if there are no errors, Test.ll.cmake will be generated. It can be run by `cmake -P Test.ll.cmake`, and can be included in some CMake script. 22 | 23 | Hacking 24 | ---- 25 | The most of codes you may be interested in are located in lib folder. The codes in cli folder belong to the cli part, and not very valuable. 26 | -------------------------------------------------------------------------------- /cli/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | file(GLOB_RECURSE SOURCE_FILES CONFIGURE_DEPENDS 2 | *.cpp) 3 | 4 | add_executable(LLVMCMakeBackend ${SOURCE_FILES}) 5 | 6 | llvm_map_components_to_libnames(libs 7 | CMakeBackendCodeGen 8 | ) 9 | 10 | target_link_libraries(LLVMCMakeBackend PRIVATE LLVM ${libs}) 11 | target_include_directories(LLVMCMakeBackend PRIVATE ${CMAKE_SOURCE_DIR}/lib) 12 | -------------------------------------------------------------------------------- /cli/Main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | using namespace llvm; 13 | 14 | int main(int argc, char** argv) 15 | { 16 | if (argc != 2) 17 | { 18 | std::cerr << "Wrong arg." << std::endl; 19 | std::cerr << "Usage: " << argv[0] << " SomeModule.ll" << std::endl; 20 | return 1; 21 | } 22 | 23 | llvm_shutdown_obj llvm_shutdown; 24 | 25 | LLVMInitializeCMakeBackendTarget(); 26 | LLVMInitializeCMakeBackendTargetInfo(); 27 | LLVMInitializeCMakeBackendTargetMC(); 28 | 29 | LLVMContext context; 30 | 31 | const std::string inputFile = argv[1]; 32 | SMDiagnostic err; 33 | const auto mod = parseIRFile(inputFile, err, context); 34 | if (!mod) 35 | { 36 | err.print(argv[0], errs()); 37 | return 1; 38 | } 39 | 40 | Triple triple{ sys::getDefaultTargetTriple() }; 41 | 42 | std::string errStr; 43 | const auto target = TargetRegistry::lookupTarget("cmake", triple, errStr); 44 | if (!target) 45 | { 46 | std::cerr << errStr << std::endl; 47 | return 1; 48 | } 49 | 50 | TargetOptions options{}; 51 | const auto targetMachine = target->createTargetMachine(triple.getTriple(), "", "", options, None); 52 | if (!targetMachine) 53 | { 54 | std::cerr << "Failed to create target machine" << std::endl; 55 | return 1; 56 | } 57 | 58 | std::error_code errc; 59 | ToolOutputFile out(inputFile + ".cmake", errc, sys::fs::F_Text); 60 | if (errc) 61 | { 62 | std::cerr << errc.message() << std::endl; 63 | return 1; 64 | } 65 | 66 | legacy::PassManager pm; 67 | if (targetMachine->addPassesToEmitFile(pm, out.os(), nullptr, CodeGenFileType::CGFT_AssemblyFile, 68 | true, nullptr)) 69 | { 70 | std::cerr << "Emit file error" << std::endl; 71 | } 72 | 73 | pm.run(*mod); 74 | 75 | out.keep(); 76 | } 77 | -------------------------------------------------------------------------------- /lib/CMakeBackend.cpp: -------------------------------------------------------------------------------- 1 | #include "CMakeBackend.h" 2 | 3 | #include "CheckNeedGotoPass.h" 4 | 5 | #include 6 | 7 | #include 8 | 9 | using namespace llvm; 10 | using namespace LLVMCMakeBackend; 11 | 12 | // 所有以 _LLVM_CMAKE_ 开头的标识符都为本实现保留,用户不得使用,因此可以假设此类命名不会和用户冲突 13 | // 定义这类标识符为 丑命名 14 | 15 | namespace 16 | { 17 | // NOTE: 18 | // 涉及指针的操作时,所有内部实体名称必须为丑命名,因为间接访问时可能获得的是函数内实体而不是外部的 19 | 20 | // 指针可能为胖指针,其他情况都为引用实体的名称 21 | 22 | // 指针的定义如下: 23 | // pointer = fat-pointer | identifier 24 | // fat-pointer = pointer-head, property-list, info-list 25 | // pointer-head = "_LLVM_CMAKE_PTR" 26 | // property-list = { ".", property } 27 | // property = "NUL" | "GEP" | "EXT" 28 | // info-list = { ":", info } 29 | // info = 30 | 31 | // 目前包含的属性有 NUL 表示空指针, GEP 表示 gep 而来的指针,以及 EXT 表示引用外部实体的指针 32 | // info 按照 property 顺序排列 33 | // 对于 NUL,没有 info 34 | // 对于 GEP,info 有 2 个,按顺序为偏移和引用的实体名称 35 | // 对于 EXT,若指针不为 GEP,info 有 1 个,是引用的实体的名称,否则仅作为标记,不包含 info 36 | 37 | // 当前的实现偷懒,并未完全实现上述描述( 38 | 39 | constexpr const char CMakeIntrinsics[] = 40 | R"CMakeIntrinsics(# Start of LLVM CMake intrinsics 41 | 42 | set(_LLVM_CMAKE_CURRENT_DEPTH "0") 43 | 44 | function(_LLVM_CMAKE_EVAL) 45 | cmake_parse_arguments(ARGUMENT "NO_LOCK" "" "PATH;CONTENT" ${ARGN}) 46 | if(NOT DEFINED ARGUMENT_CONTENT) 47 | message(FATAL_ERROR "No content provided") 48 | endif() 49 | if(NOT DEFINED ARGUMENT_PATH) 50 | string(MD5 CONTENT_HASH "${ARGUMENT_CONTENT}") 51 | set(ARGUMENT_PATH "${CMAKE_CURRENT_BINARY_DIR}/_LLVM_CMAKE_EvalCache/${CONTENT_HASH}.cmake") 52 | endif() 53 | if(NOT ARGUMENT_NO_LOCK) 54 | file(LOCK "${ARGUMENT_PATH}.lock" GUARD FUNCTION RESULT_VARIABLE _lock_result) 55 | if(NOT _lock_result EQUAL 0) 56 | message(FATAL_ERROR "Cannot lock file \"${ARGUMENT_PATH}.lock\", result code is ${_lock_result}") 57 | endif() 58 | endif() 59 | file(WRITE ${ARGUMENT_PATH} "${ARGUMENT_CONTENT}") 60 | include(${ARGUMENT_PATH}) 61 | endfunction() 62 | 63 | macro(_LLVM_CMAKE_PROPAGATE_FUNCTION_RESULT _LLVM_CMAKE_PROPAGATE_FUNCTION_RESULT_FUNC_NAME) 64 | set(_LLVM_CMAKE_RETURN_VALUE ${_LLVM_CMAKE_RETURN_VALUE} PARENT_SCOPE) 65 | set(_LLVM_CMAKE_RETURN_MODIFIED_EXTERNAL_VARIABLE_LIST ${_LLVM_CMAKE_RETURN_MODIFIED_EXTERNAL_VARIABLE_LIST} PARENT_SCOPE) 66 | endmacro() 67 | 68 | function(_LLVM_CMAKE_INVOKE_FUNCTION_PTR _LLVM_CMAKE_INVOKE_FUNCTION_PTR_FUNC_PTR) 69 | if(_LLVM_CMAKE_INVOKE_FUNCTION_PTR_FUNC_PTR STREQUAL "_LLVM_CMAKE_PTR.NUL") 70 | message(FATAL_ERROR "Function pointer is null") 71 | endif() 72 | _LLVM_CMAKE_EVAL(CONTENT "${_LLVM_CMAKE_INVOKE_FUNCTION_PTR_FUNC_PTR}(${ARGN}) 73 | _LLVM_CMAKE_PROPAGATE_FUNCTION_RESULT(${_LLVM_CMAKE_INVOKE_FUNCTION_PTR_FUNC_PTR})") 74 | _LLVM_CMAKE_PROPAGATE_FUNCTION_RESULT(${_LLVM_CMAKE_INVOKE_FUNCTION_PTR_FUNC_PTR}) 75 | endfunction() 76 | 77 | function(_LLVM_CMAKE_CONSTRUCT_GEP _LLVM_CMAKE_CONSTRUCT_GEP_OFFSET _LLVM_CMAKE_CONSTRUCT_GEP_ENTITY_PTR _LLVM_CMAKE_CONSTRUCT_GEP_RESULT_NAME) 78 | if(_LLVM_CMAKE_CONSTRUCT_GEP_ENTITY_PTR MATCHES "^_LLVM_CMAKE_PTR.*\\.NUL.*$") 79 | message(FATAL_ERROR "Trying to construct gep from a null pointer") 80 | elseif(_LLVM_CMAKE_CONSTRUCT_GEP_ENTITY_PTR MATCHES "^_LLVM_CMAKE_PTR.*\\.GEP.*:([0-9]+):(.+)$") 81 | set(_LLVM_CMAKE_CONSTRUCT_GEP_ORIGIN_OFFSET ${CMAKE_MATCH_1}) 82 | set(_LLVM_CMAKE_CONSTRUCT_GEP_REF_ENTITY ${CMAKE_MATCH_2}) 83 | math(EXPR _LLVM_CMAKE_CONSTRUCT_GEP_OFFSET "${_LLVM_CMAKE_CONSTRUCT_GEP_OFFSET} + ${_LLVM_CMAKE_CONSTRUCT_GEP_ORIGIN_OFFSET}") 84 | set(_LLVM_CMAKE_CONSTRUCT_GEP_RESULT "_LLVM_CMAKE_PTR.GEP.EXT:${_LLVM_CMAKE_CONSTRUCT_GEP_OFFSET}:${_LLVM_CMAKE_CONSTRUCT_GEP_REF_ENTITY}") 85 | elseif(_LLVM_CMAKE_CONSTRUCT_GEP_ENTITY_PTR MATCHES "^_LLVM_CMAKE_PTR.*\\.EXT.*:(.+)$") 86 | set(_LLVM_CMAKE_CONSTRUCT_GEP_REAL_PTR ${CMAKE_MATCH_1}) 87 | set(_LLVM_CMAKE_CONSTRUCT_GEP_RESULT "_LLVM_CMAKE_PTR.GEP.EXT:${_LLVM_CMAKE_CONSTRUCT_GEP_OFFSET}:${_LLVM_CMAKE_CONSTRUCT_GEP_REAL_PTR}") 88 | else() 89 | set(_LLVM_CMAKE_CONSTRUCT_GEP_RESULT "_LLVM_CMAKE_PTR.GEP:${_LLVM_CMAKE_CONSTRUCT_GEP_OFFSET}:${_LLVM_CMAKE_CONSTRUCT_GEP_ENTITY_PTR}") 90 | endif() 91 | set(${_LLVM_CMAKE_CONSTRUCT_GEP_RESULT_NAME} ${_LLVM_CMAKE_CONSTRUCT_GEP_RESULT} PARENT_SCOPE) 92 | endfunction() 93 | 94 | function(_LLVM_CMAKE_LOAD _LLVM_CMAKE_LOAD_PTR _LLVM_CMAKE_LOAD_RESULT_NAME) 95 | if(_LLVM_CMAKE_LOAD_PTR MATCHES "^_LLVM_CMAKE_PTR.*\\.NUL.*$") 96 | message(FATAL_ERROR "Trying to dereference a null pointer") 97 | elseif(_LLVM_CMAKE_LOAD_PTR MATCHES "^_LLVM_CMAKE_PTR.*\\.GEP.*:([0-9]+):(.+)$") 98 | set(_LLVM_CMAKE_LOAD_OFFSET ${CMAKE_MATCH_1}) 99 | set(_LLVM_CMAKE_LOAD_REF_ENTITY ${CMAKE_MATCH_2}) 100 | list(GET ${_LLVM_CMAKE_LOAD_REF_ENTITY} ${_LLVM_CMAKE_LOAD_OFFSET} _LLVM_CMAKE_LOAD_RESULT) 101 | elseif(_LLVM_CMAKE_LOAD_PTR MATCHES "^_LLVM_CMAKE_PTR.*\\.EXT.*:(.+)$") 102 | set(_LLVM_CMAKE_LOAD_REAL_PTR ${CMAKE_MATCH_1}) 103 | set(_LLVM_CMAKE_LOAD_RESULT ${${_LLVM_CMAKE_LOAD_REAL_PTR}}) 104 | else() 105 | set(_LLVM_CMAKE_LOAD_RESULT ${${_LLVM_CMAKE_LOAD_PTR}}) 106 | endif() 107 | set(${_LLVM_CMAKE_LOAD_RESULT_NAME} ${_LLVM_CMAKE_LOAD_RESULT} PARENT_SCOPE) 108 | endfunction() 109 | 110 | function(_LLVM_CMAKE_STORE _LLVM_CMAKE_STORE_FUNC_MODLIST _LLVM_CMAKE_STORE_PTR) 111 | if(_LLVM_CMAKE_STORE_PTR MATCHES "^_LLVM_CMAKE_PTR.*\\.NUL.*$") 112 | message(FATAL_ERROR "Trying to write to a null pointer") 113 | elseif(_LLVM_CMAKE_STORE_PTR MATCHES "^_LLVM_CMAKE_PTR.*\\.GEP.*:([0-9]+):(.+)$") 114 | set(_LLVM_CMAKE_STORE_OFFSET ${CMAKE_MATCH_1}) 115 | set(_LLVM_CMAKE_STORE_REF_ENTITY ${CMAKE_MATCH_2}) 116 | list(REMOVE_AT ${_LLVM_CMAKE_STORE_REF_ENTITY} ${_LLVM_CMAKE_STORE_OFFSET}) 117 | list(INSERT ${_LLVM_CMAKE_STORE_REF_ENTITY} ${_LLVM_CMAKE_STORE_OFFSET} "${ARGN}") 118 | set(_LLVM_CMAKE_STORE_MODIFIED_ENTITY ${_LLVM_CMAKE_STORE_REF_ENTITY}) 119 | if(${_LLVM_CMAKE_STORE_PTR} MATCHES "^_LLVM_CMAKE_PTR.*\\.EXT.*$") 120 | list(APPEND ${_LLVM_CMAKE_STORE_FUNC_MODLIST} ${_LLVM_CMAKE_STORE_REF_ENTITY}) 121 | endif() 122 | elseif(_LLVM_CMAKE_STORE_PTR MATCHES "^_LLVM_CMAKE_PTR.*\\.EXT.*:(.+)$") 123 | set(_LLVM_CMAKE_STORE_REAL_PTR ${CMAKE_MATCH_1}) 124 | set(${_LLVM_CMAKE_STORE_REAL_PTR} "${ARGN}") 125 | set(_LLVM_CMAKE_STORE_MODIFIED_ENTITY ${_LLVM_CMAKE_STORE_REAL_PTR}) 126 | list(APPEND ${_LLVM_CMAKE_STORE_FUNC_MODLIST} ${_LLVM_CMAKE_STORE_REAL_PTR}) 127 | else() 128 | set(${_LLVM_CMAKE_STORE_PTR} "${ARGN}") 129 | set(_LLVM_CMAKE_STORE_MODIFIED_ENTITY ${_LLVM_CMAKE_STORE_PTR}) 130 | endif() 131 | set(${_LLVM_CMAKE_STORE_MODIFIED_ENTITY} ${${_LLVM_CMAKE_STORE_MODIFIED_ENTITY}} PARENT_SCOPE) 132 | set(${_LLVM_CMAKE_STORE_FUNC_MODLIST} ${${_LLVM_CMAKE_STORE_FUNC_MODLIST}} PARENT_SCOPE) 133 | endfunction() 134 | 135 | macro(_LLVM_CMAKE_APPLY_MODIFIED_LIST _LLVM_CMAKE_APPLY_MODIFIED_LIST_FUNC_MODLIST) 136 | list(REMOVE_DUPLICATES ${_LLVM_CMAKE_APPLY_MODIFIED_LIST_FUNC_MODLIST}) 137 | foreach(_LLVM_CMAKE_APPLY_MODIFIED_LIST_INTERNAL_ENTITY_NAME ${${_LLVM_CMAKE_APPLY_MODIFIED_LIST_FUNC_MODLIST}}) 138 | set(${_LLVM_CMAKE_APPLY_MODIFIED_LIST_INTERNAL_ENTITY_NAME} 139 | ${${_LLVM_CMAKE_APPLY_MODIFIED_LIST_INTERNAL_ENTITY_NAME}} PARENT_SCOPE) 140 | endforeach() 141 | endmacro() 142 | 143 | function(_LLVM_CMAKE_MAKE_EXTERNAL_PTR _LLVM_CMAKE_MAKE_EXTERNAL_PTR_VALUE _LLVM_CMAKE_MAKE_EXTERNAL_PTR_RESULT_PTR) 144 | if(_LLVM_CMAKE_MAKE_EXTERNAL_PTR_VALUE MATCHES "_LLVM_CMAKE_PTR.*") 145 | if(NOT _LLVM_CMAKE_MAKE_EXTERNAL_PTR_VALUE MATCHES "_LLVM_CMAKE_PTR.*\\.EXT.*") 146 | string(REGEX REPLACE "_LLVM_CMAKE_PTR([^:]*):" "_LLVM_CMAKE_PTR\\1.EXT:" 147 | _LLVM_CMAKE_MAKE_EXTERNAL_PTR_VALUE ${_LLVM_CMAKE_MAKE_EXTERNAL_PTR_VALUE}) 148 | endif() 149 | else() 150 | set(_LLVM_CMAKE_MAKE_EXTERNAL_PTR_VALUE "_LLVM_CMAKE_PTR.EXT:${_LLVM_CMAKE_MAKE_EXTERNAL_PTR_VALUE}") 151 | endif() 152 | set(${_LLVM_CMAKE_MAKE_EXTERNAL_PTR_RESULT_PTR} ${_LLVM_CMAKE_MAKE_EXTERNAL_PTR_VALUE} PARENT_SCOPE) 153 | endfunction() 154 | 155 | function(_LLVM_CMAKE_BYTEARRAY_TO_STRING _LLVM_CMAKE_BYTEARRAY_TO_STRING_STR_NAME) 156 | if(ARGC GREATER 1) 157 | string(ASCII ${ARGN} ${_LLVM_CMAKE_BYTEARRAY_TO_STRING_STR_NAME}) 158 | set(${_LLVM_CMAKE_BYTEARRAY_TO_STRING_STR_NAME} ${${_LLVM_CMAKE_BYTEARRAY_TO_STRING_STR_NAME}} PARENT_SCOPE) 159 | else() 160 | set(${_LLVM_CMAKE_BYTEARRAY_TO_STRING_STR_NAME} "") 161 | endif() 162 | endfunction() 163 | 164 | function(_LLVM_CMAKE_STRING_TO_BYTEARRAY _LLVM_CMAKE_STRING_TO_BYTEARRAY_STR _LLVM_CMAKE_STRING_TO_BYTEARRAY_ARRAY_NAME) 165 | string(HEX ${_LLVM_CMAKE_STRING_TO_BYTEARRAY_STR} _LLVM_CMAKE_STRING_TO_BYTEARRAY_HEX_STR) 166 | string(REGEX REPLACE "([0-9a-fA-F][0-9a-fA-F])" "0x\\1;" _LLVM_CMAKE_STRING_TO_BYTEARRAY_RESULT_ARRAY "${_LLVM_CMAKE_STRING_TO_BYTEARRAY_HEX_STR}") 167 | list(POP_BACK _LLVM_CMAKE_STRING_TO_BYTEARRAY_RESULT_ARRAY) 168 | set(${_LLVM_CMAKE_STRING_TO_BYTEARRAY_ARRAY_NAME} ${_LLVM_CMAKE_STRING_TO_BYTEARRAY_RESULT_ARRAY} PARENT_SCOPE) 169 | endfunction() 170 | 171 | function(_LLVM_CMAKE_POINTER_APPLY_OFFSET _LLVM_CMAKE_POINTER_APPLY_OFFSET_PTR _LLVM_CMAKE_POINTER_APPLY_OFFSET_OFFSET _LLVM_CMAKE_POINTER_APPLY_OFFSET_RESULT) 172 | if(_LLVM_CMAKE_POINTER_APPLY_OFFSET_PTR MATCHES "^_LLVM_CMAKE_PTR(.*\\.GEP.*):([0-9]+):(.+)$") 173 | set(_LLVM_CMAKE_POINTER_APPLY_OFFSET_PTR_OFFSET ${CMAKE_MATCH_2}) 174 | set(_LLVM_CMAKE_POINTER_APPLY_OFFSET_REF_ENTITY ${CMAKE_MATCH_3}) 175 | math(EXPR _LLVM_CMAKE_POINTER_APPLY_OFFSET_PTR_OFFSET 176 | "${_LLVM_CMAKE_POINTER_APPLY_OFFSET_PTR_OFFSET} + ${_LLVM_CMAKE_POINTER_APPLY_OFFSET_OFFSET}") 177 | set(${_LLVM_CMAKE_POINTER_APPLY_OFFSET_RESULT} 178 | "_LLVM_CMAKE_PTR${CMAKE_MATCH_1}:${_LLVM_CMAKE_POINTER_APPLY_OFFSET_PTR_OFFSET}:${_LLVM_CMAKE_POINTER_APPLY_OFFSET_REF_ENTITY}" 179 | PARENT_SCOPE) 180 | else() 181 | message(FATAL_ERROR "Pointer should be a gep pointer") 182 | endif() 183 | endfunction() 184 | 185 | function(_LLVM_CMAKE_POINTER_OFFSET _LLVM_CMAKE_POINTER_OFFSET_TO _LLVM_CMAKE_POINTER_OFFSET_FROM _LLVM_CMAKE_POINTER_OFFSET_RESULT) 186 | if(_LLVM_CMAKE_POINTER_OFFSET_TO MATCHES "^_LLVM_CMAKE_PTR.*\\.GEP.*:([0-9]+):(.+)$") 187 | set(_LLVM_CMAKE_POINTER_OFFSET_TO_OFFSET ${CMAKE_MATCH_1}) 188 | set(_LLVM_CMAKE_POINTER_OFFSET_TO_REF_ENTITY ${CMAKE_MATCH_2}) 189 | if(_LLVM_CMAKE_POINTER_OFFSET_FROM MATCHES "^_LLVM_CMAKE_PTR.*\\.GEP.*:([0-9]+):(.+)$") 190 | set(_LLVM_CMAKE_POINTER_OFFSET_FROM_OFFSET ${CMAKE_MATCH_1}) 191 | set(_LLVM_CMAKE_POINTER_OFFSET_FROM_REF_ENTITY ${CMAKE_MATCH_2}) 192 | 193 | if(NOT _LLVM_CMAKE_POINTER_OFFSET_TO_REF_ENTITY STREQUAL _LLVM_CMAKE_POINTER_OFFSET_FROM_REF_ENTITY) 194 | message(FATAL_ERROR "From and to should reference to same object") 195 | endif() 196 | 197 | math(EXPR ${_LLVM_CMAKE_POINTER_OFFSET_RESULT} 198 | "${_LLVM_CMAKE_POINTER_OFFSET_TO_OFFSET} - ${_LLVM_CMAKE_POINTER_OFFSET_FROM_OFFSET}") 199 | set(${_LLVM_CMAKE_POINTER_OFFSET_RESULT} ${${_LLVM_CMAKE_POINTER_OFFSET_RESULT}} PARENT_SCOPE) 200 | return() 201 | endif() 202 | endif() 203 | message(FATAL_ERROR "From and to should be a gep pointer") 204 | endfunction() 205 | 206 | function(_LLVM_CMAKE_EXTRACT_POINTER_OFFSET _LLVM_CMAKE_EXTRACT_POINTER_OFFSET_PTR _LLVM_CMAKE_EXTRACT_POINTER_OFFSET_RESULT) 207 | if(_LLVM_CMAKE_EXTRACT_POINTER_OFFSET_PTR MATCHES "^_LLVM_CMAKE_PTR.*\\.GEP.*:([0-9]+):.+$") 208 | set(_LLVM_CMAKE_EXTRACT_POINTER_OFFSET_PTR_OFFSET ${CMAKE_MATCH_1}) 209 | set(${_LLVM_CMAKE_EXTRACT_POINTER_OFFSET_RESULT} ${_LLVM_CMAKE_EXTRACT_POINTER_OFFSET_PTR_OFFSET} PARENT_SCOPE) 210 | else() 211 | message(FATAL_ERROR "Pointer is not a gep pointer") 212 | endif() 213 | endfunction() 214 | 215 | function(_LLVM_CMAKE_EXTRACT_POINTER_REF_OBJ _LLVM_CMAKE_EXTRACT_POINTER_REF_OBJ_PTR _LLVM_CMAKE_EXTRACT_POINTER_REF_OBJ_RESULT) 216 | if(_LLVM_CMAKE_EXTRACT_POINTER_REF_OBJ_PTR MATCHES "^_LLVM_CMAKE_PTR.*\\.GEP.*:[0-9]+:(.+)$") 217 | set(_LLVM_CMAKE_EXTRACT_POINTER_REF_OBJ_PTR_REF_OBJ ${CMAKE_MATCH_1}) 218 | set(${_LLVM_CMAKE_EXTRACT_POINTER_REF_OBJ_RESULT} ${_LLVM_CMAKE_EXTRACT_POINTER_REF_OBJ_PTR_REF_OBJ} PARENT_SCOPE) 219 | elseif(_LLVM_CMAKE_EXTRACT_POINTER_REF_OBJ_PTR MATCHES "^_LLVM_CMAKE_PTR.*\\.EXT.*:(.+)$") 220 | set(_LLVM_CMAKE_EXTRACT_POINTER_REF_OBJ_PTR_REF_OBJ ${CMAKE_MATCH_1}) 221 | set(${_LLVM_CMAKE_EXTRACT_POINTER_REF_OBJ_RESULT} ${_LLVM_CMAKE_EXTRACT_POINTER_REF_OBJ_PTR_REF_OBJ} PARENT_SCOPE) 222 | elseif(_LLVM_CMAKE_EXTRACT_POINTER_REF_OBJ_PTR MATCHES "^_LLVM_CMAKE_PTR.*\\.NUL.*") 223 | message(FATAL_ERROR "Pointer is a null pointer") 224 | else() 225 | set(${_LLVM_CMAKE_EXTRACT_POINTER_REF_OBJ_RESULT} ${_LLVM_CMAKE_EXTRACT_POINTER_REF_OBJ_PTR} PARENT_SCOPE) 226 | endif() 227 | endfunction() 228 | 229 | function(_LLVM_CMAKE_POINTER_TO_INT _LLVM_CMAKE_POINTER_TO_INT_PTR _LLVM_CMAKE_POINTER_TO_INT_PTR_SIZE _LLVM_CMAKE_POINTER_TO_INT_RESULT) 230 | _LLVM_CMAKE_EXTRACT_POINTER_OFFSET(${_LLVM_CMAKE_POINTER_TO_INT_PTR} _LLVM_CMAKE_EXTRACT_POINTER_OFFSET_RESULT_VALUE) 231 | math(EXPR _LLVM_CMAKE_EXTRACT_POINTER_OFFSET_RESULT_VALUE "${_LLVM_CMAKE_EXTRACT_POINTER_OFFSET_RESULT_VALUE} * ${_LLVM_CMAKE_POINTER_TO_INT_PTR_SIZE}") 232 | set(${_LLVM_CMAKE_POINTER_TO_INT_RESULT} ${_LLVM_CMAKE_EXTRACT_POINTER_OFFSET_RESULT_VALUE} PARENT_SCOPE) 233 | endfunction() 234 | 235 | function(_LLVM_CMAKE_ASSUME_MEMORY_LAYOUT _LLVM_CMAKE_ASSUME_MEMORY_LAYOUT_FUNC_MODLIST _LLVM_CMAKE_ASSUME_MEMORY_LAYOUT_PTR _LLVM_CMAKE_ASSUME_MEMORY_LAYOUT_EXPECT_FIELD_SIZE) 236 | if(_LLVM_CMAKE_ASSUME_MEMORY_LAYOUT_PTR MATCHES "^_LLVM_CMAKE_PTR.*\\.NUL.*") 237 | message(FATAL_ERROR "Pointer is a null pointer") 238 | elseif(_LLVM_CMAKE_ASSUME_MEMORY_LAYOUT_PTR MATCHES "^_LLVM_CMAKE_PTR.*\\.GEP.*") 239 | message(FATAL_ERROR "Pointer is a gep pointer, which is not support now") 240 | else() 241 | _LLVM_CMAKE_EXTRACT_POINTER_REF_OBJ(${_LLVM_CMAKE_ASSUME_MEMORY_LAYOUT_PTR} _LLVM_CMAKE_ASSUME_MEMORY_LAYOUT_PTR_REF_OBJ) 242 | list(LENGTH _LLVM_CMAKE_ASSUME_MEMORY_LAYOUT_PTR_REF_OBJ _LLVM_CMAKE_ASSUME_MEMORY_LAYOUT_PTR_REF_OBJ_FIELD_SIZE) 243 | if(_LLVM_CMAKE_ASSUME_MEMORY_LAYOUT_PTR_REF_OBJ_FIELD_SIZE LESS _LLVM_CMAKE_ASSUME_MEMORY_LAYOUT_EXPECT_FIELD_SIZE) 244 | math(EXPR _LLVM_CMAKE_ASSUME_MEMORY_LAYOUT_ADD_FIELD_COUNT "${_LLVM_CMAKE_ASSUME_MEMORY_LAYOUT_EXPECT_FIELD_SIZE} - ${_LLVM_CMAKE_ASSUME_MEMORY_LAYOUT_PTR_REF_OBJ_FIELD_SIZE}") 245 | string(REPEAT ";0" ${_LLVM_CMAKE_ASSUME_MEMORY_LAYOUT_ADD_FIELD_COUNT} _LLVM_CMAKE_ASSUME_MEMORY_LAYOUT_ADD_FIELD) 246 | set(${_LLVM_CMAKE_ASSUME_MEMORY_LAYOUT_PTR_REF_OBJ} "${${_LLVM_CMAKE_ASSUME_MEMORY_LAYOUT_PTR_REF_OBJ}}${_LLVM_CMAKE_ASSUME_MEMORY_LAYOUT_ADD_FIELD}" PARENT_SCOPE) 247 | list(APPEND ${_LLVM_CMAKE_ASSUME_MEMORY_LAYOUT_FUNC_MODLIST} ${_LLVM_CMAKE_ASSUME_MEMORY_LAYOUT_PTR_REF_OBJ}) 248 | set(${_LLVM_CMAKE_ASSUME_MEMORY_LAYOUT_FUNC_MODLIST} ${${_LLVM_CMAKE_ASSUME_MEMORY_LAYOUT_FUNC_MODLIST}} PARENT_SCOPE) 249 | endif() 250 | endif() 251 | endfunction() 252 | 253 | function(_LLVM_CMAKE_EXTRACT_REF_LIST _LLVM_CMAKE_EXTRACT_REF_LIST_PTR _LLVM_CMAKE_EXTRACT_REF_LIST_RESULT) 254 | if(_LLVM_CMAKE_EXTRACT_REF_LIST_PTR MATCHES "^_LLVM_CMAKE_PTR.*\\.GEP.*:([0-9]+):(.+)$") 255 | set(_LLVM_CMAKE_EXTRACT_REF_LIST_PTR_OFFSET ${CMAKE_MATCH_1}) 256 | set(_LLVM_CMAKE_EXTRACT_REF_LIST_PTR_REF_OBJ ${CMAKE_MATCH_2}) 257 | list(SUBLIST ${_LLVM_CMAKE_EXTRACT_REF_LIST_PTR_REF_OBJ} ${_LLVM_CMAKE_EXTRACT_REF_LIST_PTR_OFFSET} -1 ${_LLVM_CMAKE_EXTRACT_REF_LIST_RESULT}) 258 | set(${_LLVM_CMAKE_EXTRACT_REF_LIST_RESULT} ${${_LLVM_CMAKE_EXTRACT_REF_LIST_RESULT}} PARENT_SCOPE) 259 | else() 260 | set(${_LLVM_CMAKE_EXTRACT_REF_LIST_RESULT} ${${_LLVM_CMAKE_EXTRACT_REF_LIST_PTR}} PARENT_SCOPE) 261 | endif() 262 | endfunction() 263 | 264 | # End of LLVM CMake intrinsics 265 | 266 | )CMakeIntrinsics"; 267 | 268 | void NormalizeIdentifier(std::string& identifier) 269 | { 270 | std::replace(identifier.begin(), identifier.end(), '$', '_'); 271 | std::replace(identifier.begin(), identifier.end(), '.', '_'); 272 | } 273 | 274 | std::string NormalizeIdentifier(StringRef identifier) 275 | { 276 | std::string id{ identifier }; 277 | NormalizeIdentifier(id); 278 | return id; 279 | } 280 | } // namespace 281 | 282 | char CMakeBackend::ID{}; 283 | 284 | bool CMakeBackend::doInitialization(Module& M) 285 | { 286 | m_DataLayout = std::make_unique(&M); 287 | m_IntrinsicLowering = std::make_unique(*m_DataLayout); 288 | 289 | emitModulePrologue(M); 290 | 291 | return false; 292 | } 293 | 294 | bool CMakeBackend::doFinalization(Module& M) 295 | { 296 | emitModuleEpilogue(M); 297 | 298 | m_IntrinsicLowering.reset(); 299 | m_DataLayout.reset(); 300 | 301 | return false; 302 | } 303 | 304 | bool CMakeBackend::runOnFunction(Function& F) 305 | { 306 | m_CurrentFunction = &F; 307 | const auto& checkNeedGotoPass = getAnalysis(); 308 | if (checkNeedGotoPass.isCurrentFunctionNeedGoto()) 309 | { 310 | m_CurrentStateInfo.emplace(checkNeedGotoPass.getCurrentStateInfo()); 311 | } 312 | else 313 | { 314 | m_CurrentStateInfo.reset(); 315 | } 316 | m_CurrentIntent = 0; 317 | m_CurrentTemporaryID = 0; 318 | m_TemporaryID.clear(); 319 | m_CurrentFunctionLocalEntityNames.clear(); 320 | 321 | auto modified = lowerIntrinsics(F); 322 | // 不能依赖 PHIElimination,只好自己实现 323 | modified |= lowerPHINode(F); 324 | #ifndef NDEBUG 325 | if (modified) 326 | { 327 | outs() << "Function modified: \n" << F << "\n"; 328 | } 329 | #endif 330 | 331 | emitFunction(F); 332 | 333 | return modified; 334 | } 335 | 336 | void CMakeBackend::getAnalysisUsage(llvm::AnalysisUsage& au) const 337 | { 338 | au.addRequired(); 339 | au.addPreservedID(LowerSwitchID); 340 | au.setPreservesCFG(); 341 | } 342 | 343 | void CMakeBackend::visitInstruction(Instruction& I) 344 | { 345 | errs() << "Unimplemented: " << I << "\n"; 346 | 347 | assert(!"Unimplemented"); 348 | std::terminate(); 349 | } 350 | 351 | void CMakeBackend::visitCastInst(CastInst& I) 352 | { 353 | const auto srcType = I.getSrcTy(); 354 | const auto dstType = I.getDestTy(); 355 | 356 | const auto src = I.getOperand(0); 357 | const auto resultName = getValueName(&I); 358 | 359 | // TODO: 当前未实现转换,仅允许值不实际发生变化的情况 360 | if (srcType == dstType || (srcType->isIntegerTy() && dstType->isIntegerTy())) 361 | { 362 | const auto operand = evalOperand(src); 363 | emitIntent(); 364 | m_Out << "set(" << resultName << " " << operand << ")\n"; 365 | } 366 | else if (const auto srcPtrType = dyn_cast(srcType)) 367 | { 368 | if (dstType->isIntegerTy()) 369 | { 370 | const auto operand = evalOperand(src); 371 | const auto srcElemType = srcType->getPointerElementType(); 372 | 373 | emitIntent(); 374 | m_Out << "_LLVM_CMAKE_POINTER_TO_INT(" << operand << " " 375 | << (getTypeSizeInBits(srcElemType) / 8) << " " << resultName << ")\n"; 376 | } 377 | else if (const auto dstPtrType = dyn_cast(dstType)) 378 | { 379 | const auto srcElemType = srcPtrType->getElementType(); 380 | const auto dstElemType = dstPtrType->getElementType(); 381 | 382 | if (const auto srcElemArrayType = dyn_cast(srcElemType); 383 | srcElemArrayType && 384 | getTypeSizeInBits(srcElemArrayType->getElementType()) == getTypeSizeInBits(dstElemType)) 385 | { 386 | // 可能是数组退化 387 | const auto operand = evalOperand(src); 388 | emitIntent(); 389 | m_Out << "_LLVM_CMAKE_CONSTRUCT_GEP(0 " << operand << " " << resultName << ")\n"; 390 | } 391 | else 392 | { 393 | if (getTypeSizeInBits(srcElemType) != getTypeSizeInBits(dstElemType)) 394 | { 395 | errs() << I << "\n"; 396 | errs() << "Warning: incompatible pointers casting may be unsafe.\n"; 397 | } 398 | 399 | // 指针 reinterpret_cast 400 | const auto operand = evalOperand(src); 401 | emitIntent(); 402 | m_Out << "set(" << resultName << " " << operand << ")\n"; 403 | 404 | // 暂时不处理不安全转换的情形 405 | } 406 | } 407 | } 408 | else 409 | { 410 | errs() << I << "\n"; 411 | assert(!"Unimplemented"); 412 | std::terminate(); 413 | } 414 | } 415 | 416 | void CMakeBackend::visitReturnInst(ReturnInst& i) 417 | { 418 | using namespace std::literals::string_literals; 419 | 420 | // CMake 不能返回值,太菜了 421 | if (i.getNumOperands()) 422 | { 423 | const auto opName = evalOperand(i.getOperand(0)); 424 | emitIntent(); 425 | m_Out << "set(" << getFunctionReturnValueName(m_CurrentFunction) << " " << opName 426 | << " PARENT_SCOPE)\n"; 427 | } 428 | 429 | const auto functionModifiedListName = 430 | getFunctionModifiedExternalVariableListName(m_CurrentFunction); 431 | 432 | if (!m_CurrentFunctionLocalEntityNames.empty()) 433 | { 434 | emitIntent(); 435 | m_Out << "list(REMOVE_ITEM " << functionModifiedListName; 436 | for (const auto& entityName : m_CurrentFunctionLocalEntityNames) 437 | { 438 | m_Out << " " << entityName; 439 | } 440 | m_Out << ")\n"; 441 | } 442 | 443 | emitIntent(); 444 | m_Out << "_LLVM_CMAKE_APPLY_MODIFIED_LIST(" << functionModifiedListName << ")\n"; 445 | emitIntent(); 446 | m_Out << "set(" << getFunctionModifiedExternalVariableListName(m_CurrentFunction, true) << " ${" 447 | << functionModifiedListName << "} PARENT_SCOPE)\n"; 448 | 449 | emitIntent(); 450 | m_Out << "return()\n"; 451 | } 452 | 453 | void CMakeBackend::visitCallInst(CallInst& I) 454 | { 455 | if (isa(I.getCalledOperand())) 456 | { 457 | visitInlineAsm(I); 458 | return; 459 | } 460 | else if (I.getCalledFunction() && I.getCalledFunction()->isIntrinsic()) 461 | { 462 | visitIntrinsics(I); 463 | return; 464 | } 465 | 466 | std::vector argumentList; 467 | 468 | for (std::size_t i = 0; i < I.getNumArgOperands(); ++i) 469 | { 470 | const auto op = I.getArgOperand(i); 471 | auto opExpr = evalOperand(op); 472 | 473 | // 若传入参数是指针,标记指针为指向外部的 474 | if (op->getType()->isPointerTy()) 475 | { 476 | auto resultName = allocateTemporaryName(); 477 | 478 | emitIntent(); 479 | m_Out << "_LLVM_CMAKE_MAKE_EXTERNAL_PTR(" << opExpr << " " << resultName << ")\n"; 480 | 481 | argumentList.emplace_back("${" + resultName + "}"); 482 | } 483 | else 484 | { 485 | argumentList.emplace_back(std::move(opExpr)); 486 | } 487 | } 488 | 489 | const auto callOp = I.getCalledOperand(); 490 | if (!callOp) 491 | { 492 | assert(!"Error"); 493 | std::terminate(); 494 | } 495 | 496 | Type* returnType; 497 | std::string returnValueName; 498 | std::string returnModifiedListName; 499 | 500 | emitIntent(); 501 | if (const auto func = dyn_cast(callOp)) 502 | { 503 | returnType = func->getReturnType(); 504 | returnValueName = getFunctionReturnValueName(func); 505 | returnModifiedListName = getFunctionModifiedExternalVariableListName(func, true); 506 | m_Out << NormalizeIdentifier(func->getName()) << "("; 507 | } 508 | else 509 | { 510 | const auto callOpType = callOp->getType(); 511 | if (const auto ptr = dyn_cast(callOpType)) 512 | { 513 | const auto ptrValue = evalOperand(callOp); 514 | 515 | const auto funcType = dyn_cast(ptr->getPointerElementType()); 516 | if (!funcType) 517 | { 518 | assert(!"Error"); 519 | std::terminate(); 520 | } 521 | 522 | returnType = funcType->getReturnType(); 523 | returnValueName = "_LLVM_CMAKE_RETURN_VALUE"; 524 | returnModifiedListName = "_LLVM_CMAKE_RETURN_MODIFIED_EXTERNAL_VARIABLE_LIST"; 525 | m_Out << "_LLVM_CMAKE_INVOKE_FUNCTION_PTR(" << ptrValue << " "; 526 | } 527 | else 528 | { 529 | assert(!"Error"); 530 | std::terminate(); 531 | } 532 | } 533 | 534 | if (!argumentList.empty()) 535 | { 536 | m_Out << "" << argumentList.front() << ""; 537 | for (auto iter = std::next(argumentList.begin()); iter != argumentList.end(); ++iter) 538 | { 539 | m_Out << " " << *iter; 540 | } 541 | } 542 | m_Out << ")\n"; 543 | 544 | if (!returnType->isVoidTy()) 545 | { 546 | emitIntent(); 547 | m_Out << "set(" << getValueName(&I) << " ${" << returnValueName << "})\n"; 548 | } 549 | 550 | emitIntent(); 551 | m_Out << "list(APPEND " << getFunctionModifiedExternalVariableListName(m_CurrentFunction) << " ${" 552 | << returnModifiedListName << "})\n"; 553 | } 554 | 555 | void CMakeBackend::visitInlineAsm(CallInst& I) 556 | { 557 | const auto as = cast(I.getCalledOperand()); 558 | const auto constraints = as->ParseConstraints(); 559 | const auto retValueName = getValueName(&I); 560 | std::unordered_map outputVars; 561 | std::unordered_map inputVars; 562 | std::size_t argCount = 0; 563 | for (std::size_t i = 0; i < constraints.size(); ++i) 564 | { 565 | const auto& info = constraints[i]; 566 | if (info.Type == InlineAsm::isOutput) 567 | { 568 | outputVars.emplace(i, allocateTemporaryName()); 569 | } 570 | else if (info.Type == InlineAsm::isInput) 571 | { 572 | const auto arg = I.getArgOperand(argCount++); 573 | inputVars.emplace(i, evalOperand(arg)); 574 | } 575 | } 576 | 577 | std::string_view templateString = as->getAsmString(); 578 | std::string result; 579 | auto isBrace = false; 580 | 581 | while (true) 582 | { 583 | const auto nextDollar = templateString.find('$'); 584 | if (nextDollar != std::string_view::npos) 585 | { 586 | result.append(templateString.begin(), nextDollar); 587 | if (nextDollar == templateString.size()) 588 | { 589 | assert(!"Invalid asm"); 590 | std::terminate(); 591 | } 592 | const auto nextChar = templateString[nextDollar + 1]; 593 | isBrace = false; 594 | if (nextChar == '$') 595 | { 596 | result.append(1, '$'); 597 | templateString = templateString.substr(nextDollar + 2); 598 | } 599 | else if (nextChar >= '0' && nextChar <= '9') 600 | { 601 | ParseNumber: 602 | unsigned index; 603 | if (const auto [ptr, ec] = std::from_chars(templateString.begin() + nextDollar + 1, 604 | templateString.end(), index); 605 | ec == std::errc{}) [[likely]] 606 | { 607 | if (index >= constraints.size()) 608 | { 609 | assert(!"Invalid index"); 610 | std::terminate(); 611 | } 612 | const auto refConstraint = constraints[index]; 613 | if (refConstraint.Type == InlineAsm::isInput) 614 | { 615 | result.append(inputVars[index]); 616 | } 617 | else if (refConstraint.Type == InlineAsm::isOutput) 618 | { 619 | result.append(outputVars[index]); 620 | } 621 | else 622 | { 623 | assert(!"Invalid asm"); 624 | std::terminate(); 625 | } 626 | templateString = templateString.substr(ptr - templateString.begin()); 627 | if (isBrace) 628 | { 629 | if (templateString.empty()) 630 | { 631 | assert(!"Invalid asm: brace is not closed"); 632 | std::terminate(); 633 | } 634 | 635 | const auto nextChar = templateString.front(); 636 | if (nextChar == ':') 637 | { 638 | // 跳过 modifier 639 | const auto rightBrace = templateString.find('}'); 640 | if (rightBrace == std::string_view::npos) 641 | { 642 | assert(!"Invalid asm: brace is not closed"); 643 | std::terminate(); 644 | } 645 | templateString = templateString.substr(rightBrace + 1); 646 | } 647 | else if (nextChar == '}') 648 | { 649 | templateString = templateString.substr(1); 650 | } 651 | else 652 | { 653 | assert(!"Invalid asm: brace is not closed"); 654 | std::terminate(); 655 | } 656 | } 657 | } 658 | else 659 | { 660 | assert(!"Impossible"); 661 | std::terminate(); 662 | } 663 | } 664 | else if (nextChar == '{') 665 | { 666 | isBrace = true; 667 | templateString = templateString.substr(1); 668 | goto ParseNumber; 669 | } 670 | else 671 | { 672 | assert(!"Invalid asm"); 673 | std::terminate(); 674 | } 675 | } 676 | else 677 | { 678 | result.append(templateString); 679 | break; 680 | } 681 | } 682 | 683 | m_Out << result << "\n"; 684 | 685 | if (!outputVars.empty()) 686 | { 687 | emitIntent(); 688 | m_Out << "set(" << retValueName << " \""; 689 | auto iter = outputVars.begin(); 690 | m_Out << "${" << iter->second << "}"; 691 | ++iter; 692 | for (; iter != outputVars.end(); ++iter) 693 | { 694 | m_Out << ";${" << iter->second << "}"; 695 | } 696 | m_Out << "\")\n"; 697 | } 698 | } 699 | 700 | void CMakeBackend::visitBinaryOperator(llvm::BinaryOperator& I) 701 | { 702 | emitBinaryExpr(I.getOpcode(), I.getOperand(0), I.getOperand(1), getValueName(&I)); 703 | } 704 | 705 | void CMakeBackend::visitLoadInst(llvm::LoadInst& I) 706 | { 707 | const auto destName = getValueName(&I); 708 | emitLoad(destName, I.getOperand(0)); 709 | } 710 | 711 | void CMakeBackend::visitStoreInst(llvm::StoreInst& I) 712 | { 713 | emitStore(I.getOperand(0), I.getOperand(1)); 714 | } 715 | 716 | void CMakeBackend::visitAllocaInst(llvm::AllocaInst& I) 717 | { 718 | const auto elemType = I.getAllocatedType(); 719 | assert(elemType == I.getType()->getPointerElementType()); 720 | 721 | const auto elemSize = I.getArraySize(); 722 | if (const auto constSize = dyn_cast(elemSize)) 723 | { 724 | const auto size = constSize->getValue().getZExtValue(); 725 | 726 | auto objectName = allocateTemporaryName(); 727 | 728 | emitIntent(); 729 | m_Out << "set(" << objectName << " \""; 730 | const auto typeLayout = getTypeLayout(elemType); 731 | for (std::size_t i = 0; i < size; ++i) 732 | { 733 | m_Out << typeLayout; 734 | } 735 | m_Out << "\")\n"; 736 | 737 | // 结果为指针 738 | emitIntent(); 739 | m_Out << "set(" << getValueName(&I) << " " << objectName << ")\n"; 740 | 741 | m_CurrentFunctionLocalEntityNames.emplace_back(std::move(objectName)); 742 | } 743 | else 744 | { 745 | assert(!"Not implemented"); 746 | std::terminate(); 747 | } 748 | } 749 | 750 | void CMakeBackend::visitGetElementPtrInst(llvm::GetElementPtrInst& I) 751 | { 752 | emitGetElementPtr(I.getPointerOperand(), gep_type_begin(I), gep_type_end(I), getValueName(&I)); 753 | } 754 | 755 | // TODO: 代码重复 756 | void CMakeBackend::visitExtractValueInst(llvm::ExtractValueInst& I) 757 | { 758 | // 不是指针 759 | const auto aggregate = I.getAggregateOperand(); 760 | const auto aggregateValue = evalOperand(aggregate); 761 | 762 | std::size_t realIdx{}; 763 | auto type = aggregate->getType(); 764 | for (const auto idx : I.indices()) 765 | { 766 | if (const auto arrType = dyn_cast(type)) 767 | { 768 | const auto elemType = arrType->getElementType(); 769 | realIdx += getTypeFieldCount(elemType) * idx; 770 | type = elemType; 771 | } 772 | else if (const auto structType = dyn_cast(type)) 773 | { 774 | for (std::size_t i = 0; i < idx; ++i) 775 | { 776 | realIdx += getTypeFieldCount(structType->getElementType(i)); 777 | } 778 | type = structType->getElementType(idx); 779 | } 780 | else 781 | { 782 | assert(!"Unimplemented"); 783 | std::terminate(); 784 | } 785 | } 786 | 787 | emitIntent(); 788 | const auto aggregateTmpValue = allocateTemporaryName(); 789 | m_Out << "set(" << aggregateTmpValue << " " << aggregateValue << ")\n"; 790 | emitIntent(); 791 | m_Out << "list(GET " << aggregateTmpValue << " " << realIdx << " " << getValueName(&I) << ")\n"; 792 | } 793 | 794 | void CMakeBackend::visitInsertValueInst(llvm::InsertValueInst& I) 795 | { 796 | // 不是指针 797 | const auto aggregate = I.getAggregateOperand(); 798 | const auto aggregateValue = evalOperand(aggregate); 799 | 800 | const auto value = I.getInsertedValueOperand(); 801 | const auto valueValue = evalOperand(value); 802 | 803 | std::size_t realIdx{}; 804 | auto type = aggregate->getType(); 805 | for (const auto idx : I.indices()) 806 | { 807 | if (const auto arrType = dyn_cast(type)) 808 | { 809 | const auto elemType = arrType->getElementType(); 810 | realIdx += getTypeFieldCount(elemType) * idx; 811 | type = elemType; 812 | } 813 | else if (const auto structType = dyn_cast(type)) 814 | { 815 | for (std::size_t i = 0; i < idx; ++i) 816 | { 817 | realIdx += getTypeFieldCount(structType->getElementType(i)); 818 | } 819 | type = structType->getElementType(idx); 820 | } 821 | else 822 | { 823 | assert(!"Unimplemented"); 824 | std::terminate(); 825 | } 826 | } 827 | 828 | const auto valueName = getValueName(&I); 829 | emitIntent(); 830 | m_Out << "set(" << valueName << " " << aggregateValue << ")\n"; 831 | emitIntent(); 832 | m_Out << "list(REMOVE_AT " << valueName << " " << realIdx << ")\n"; 833 | emitIntent(); 834 | m_Out << "list(INSERT " << valueName << " " << realIdx << " " << valueValue << ")\n"; 835 | } 836 | 837 | // 简单 if 形式 838 | // bb1 -> bb2 -> ... -> bb3 -> bb4 839 | // \-------------------/ 840 | // 841 | // if-else 形式 842 | // else 接 if 不影响判断 843 | // /---------------------\ 844 | // bb1 -> bb2 -> ... -> bb3 -/ -> bb4 -> ... -> bb5 -> bb6 845 | // \---------------------- / 846 | void CMakeBackend::visitBranchInst(BranchInst& I) 847 | { 848 | const auto bb = I.getParent(); 849 | const auto next = bb->getNextNode(); 850 | 851 | if (I.isConditional()) 852 | { 853 | const auto cond = I.getCondition(); 854 | const auto condValue = evalOperand(cond); 855 | 856 | auto trueBranch = I.getSuccessor(0); 857 | auto falseBranch = I.getSuccessor(1); 858 | 859 | // 有效时所有分支全关联状态,直接跳转 860 | if (m_CurrentStateInfo) 861 | { 862 | auto trueStateId = m_CurrentStateInfo->GetStateID(trueBranch); 863 | auto falseStateId = m_CurrentStateInfo->GetStateID(falseBranch); 864 | if (next == trueBranch || next == falseBranch) 865 | { 866 | // 下面代码视 false 分支为跳转 867 | // true 分支直接继续向下执行,因此生成 false 分支跳转 868 | if (next == trueBranch) 869 | { 870 | emitIntent(); 871 | m_Out << "if(NOT " << condValue << ")\n"; 872 | } 873 | else if (next == falseBranch) 874 | { 875 | emitIntent(); 876 | m_Out << "if(" << condValue << ")\n"; 877 | std::swap(trueBranch, falseBranch); 878 | std::swap(trueStateId, falseStateId); 879 | } 880 | else 881 | { 882 | // 是谁生成这种代码的? 883 | return; 884 | } 885 | 886 | ++m_CurrentIntent; 887 | emitIntent(); 888 | m_Out << "set(_LLVM_CMAKE_LOOP_STATE \"" << falseStateId << "\")\n"; 889 | emitIntent(); 890 | m_Out << "continue()\n"; 891 | --m_CurrentIntent; 892 | emitIntent(); 893 | m_Out << "endif()\n"; 894 | 895 | return; 896 | } 897 | assert(trueStateId != -1 && falseStateId != -1); 898 | emitIntent(); 899 | m_Out << "if(" << condValue << ")\n"; 900 | ++m_CurrentIntent; 901 | emitIntent(); 902 | m_Out << "set(_LLVM_CMAKE_LOOP_STATE \"" << trueStateId << "\")\n"; 903 | --m_CurrentIntent; 904 | emitIntent(); 905 | m_Out << "else()\n"; 906 | ++m_CurrentIntent; 907 | emitIntent(); 908 | m_Out << "set(_LLVM_CMAKE_LOOP_STATE \"" << falseStateId << "\")\n"; 909 | --m_CurrentIntent; 910 | emitIntent(); 911 | m_Out << "endif()\n"; 912 | emitIntent(); 913 | m_Out << "continue()\n"; 914 | return; 915 | } 916 | 917 | if (next != trueBranch) 918 | { 919 | if (next == falseBranch) 920 | { 921 | emitIntent(); 922 | m_Out << "if(NOT " << condValue << ")\n"; 923 | std::swap(trueBranch, falseBranch); 924 | } 925 | else 926 | { 927 | errs() << I << "\n"; 928 | assert(!"Goto not supported in CMake."); 929 | std::terminate(); 930 | } 931 | } 932 | else 933 | { 934 | emitIntent(); 935 | m_Out << "if(" << condValue << ")\n"; 936 | } 937 | 938 | // TODO: 处理 else if 939 | if (const auto endIfBr = dyn_cast(falseBranch->getPrevNode()->getTerminator()); 940 | endIfBr && endIfBr->getNumSuccessors() == 1) 941 | { 942 | ++m_CurrentIntent; 943 | const auto endIfBlock = endIfBr->getSuccessor(0); 944 | m_CondElseEndifStack.push_back({ falseBranch, endIfBlock }); 945 | } 946 | else 947 | { 948 | errs() << I << "\n"; 949 | assert(!"Goto not supported in CMake."); 950 | std::terminate(); 951 | } 952 | } 953 | else 954 | { 955 | const auto successor = I.getSuccessor(0); 956 | 957 | if (successor != next) 958 | { 959 | if (m_CondElseEndifStack.empty() || m_CondElseEndifStack.back().second != successor) 960 | { 961 | if (m_CurrentStateInfo) 962 | { 963 | const auto stateId = m_CurrentStateInfo->GetStateID(successor); 964 | if (stateId != -1) 965 | { 966 | emitIntent(); 967 | m_Out << "set(_LLVM_CMAKE_LOOP_STATE \"" << stateId << "\")\n"; 968 | emitIntent(); 969 | m_Out << "continue()\n"; 970 | return; 971 | } 972 | } 973 | errs() << I << "\n"; 974 | assert(!"Goto not supported in CMake."); 975 | std::terminate(); 976 | } 977 | } 978 | } 979 | } 980 | 981 | void CMakeBackend::visitICmpInst(llvm::ICmpInst& I) 982 | { 983 | const auto resultName = getValueName(&I); 984 | 985 | const auto lhs = I.getOperand(0); 986 | const auto rhs = I.getOperand(1); 987 | const auto pred = I.getPredicate(); 988 | 989 | const auto lhsOp = evalOperand(lhs); 990 | const auto rhsOp = evalOperand(rhs); 991 | 992 | emitIntent(); 993 | m_Out << "if("; 994 | switch (pred) 995 | { 996 | case CmpInst::ICMP_EQ: 997 | m_Out << lhsOp << " EQUAL "; 998 | break; 999 | case CmpInst::ICMP_NE: 1000 | m_Out << "NOT " << lhsOp << " EQUAL "; 1001 | break; 1002 | case CmpInst::ICMP_SGE: 1003 | case CmpInst::ICMP_UGE: 1004 | m_Out << lhsOp << " GREATER_EQUAL "; 1005 | break; 1006 | case CmpInst::ICMP_SGT: 1007 | case CmpInst::ICMP_UGT: 1008 | m_Out << lhsOp << " GREATER "; 1009 | break; 1010 | case CmpInst::ICMP_SLE: 1011 | case CmpInst::ICMP_ULE: 1012 | m_Out << lhsOp << " LESS_EQUAL "; 1013 | break; 1014 | case CmpInst::ICMP_SLT: 1015 | case CmpInst::ICMP_ULT: 1016 | m_Out << lhsOp << " LESS "; 1017 | break; 1018 | default: 1019 | assert(!"Impossible"); 1020 | std::terminate(); 1021 | } 1022 | m_Out << rhsOp << ")\n"; 1023 | 1024 | ++m_CurrentIntent; 1025 | emitIntent(); 1026 | m_Out << "set(" << resultName << " 1)\n"; 1027 | --m_CurrentIntent; 1028 | 1029 | emitIntent(); 1030 | m_Out << "else()\n"; 1031 | 1032 | ++m_CurrentIntent; 1033 | emitIntent(); 1034 | m_Out << "set(" << resultName << " 0)\n"; 1035 | --m_CurrentIntent; 1036 | 1037 | emitIntent(); 1038 | m_Out << "endif()\n"; 1039 | } 1040 | 1041 | void CMakeBackend::visitSelectInst(llvm::SelectInst& I) 1042 | { 1043 | const auto resultName = getValueName(&I); 1044 | 1045 | const auto cond = I.getCondition(); 1046 | const auto trueValue = I.getTrueValue(); 1047 | const auto falseValue = I.getFalseValue(); 1048 | 1049 | if (isa(cond->getType())) 1050 | { 1051 | assert(!"Unimplemented"); 1052 | std::terminate(); 1053 | } 1054 | const auto condExpr = evalOperand(cond); 1055 | emitIntent(); 1056 | m_Out << "if(" << condExpr << ")\n"; 1057 | ++m_CurrentIntent; 1058 | const auto trueValueExpr = evalOperand(trueValue); 1059 | emitIntent(); 1060 | m_Out << "set(" << resultName << " \"" << trueValueExpr << "\")\n"; 1061 | --m_CurrentIntent; 1062 | emitIntent(); 1063 | m_Out << "else()\n"; 1064 | ++m_CurrentIntent; 1065 | const auto falseValueExpr = evalOperand(falseValue); 1066 | emitIntent(); 1067 | m_Out << "set(" << resultName << " \"" << falseValueExpr << "\")\n"; 1068 | --m_CurrentIntent; 1069 | emitIntent(); 1070 | m_Out << "endif()\n"; 1071 | } 1072 | 1073 | void CMakeBackend::visitUnreachableInst(llvm::UnreachableInst& I) 1074 | { 1075 | emitIntent(); 1076 | m_Out << "message(FATAL_ERROR \"Unreachable\")\n"; 1077 | } 1078 | 1079 | void CMakeBackend::emitModuleInfo(llvm::Module& m) 1080 | { 1081 | m_Out << "# This file is generated by LLVMCMakeBackend, do not edit!\n"; 1082 | m_Out << "# From module " << m.getModuleIdentifier() << "\n\n"; 1083 | } 1084 | 1085 | void CMakeBackend::emitModulePrologue(llvm::Module& m) 1086 | { 1087 | emitModuleInfo(m); 1088 | emitIntrinsics(); 1089 | emitGlobals(m); 1090 | 1091 | m_Out << "\n"; 1092 | } 1093 | 1094 | void CMakeBackend::emitModuleEpilogue(llvm::Module& m) 1095 | { 1096 | // TODO: 输出类型布局元数据 1097 | } 1098 | 1099 | void CMakeBackend::emitGlobals(llvm::Module& m) 1100 | { 1101 | for (auto& global : m.globals()) 1102 | { 1103 | const auto name = NormalizeIdentifier(global.getName()); 1104 | if (!global.hasInitializer()) 1105 | { 1106 | emitIntent(); 1107 | m_Out << "set(" << name << " \"" << getTypeLayout(global.getValueType()) << "\")\n"; 1108 | } 1109 | else 1110 | { 1111 | const auto& [result, inlined] = evalConstant(global.getInitializer(), name); 1112 | if (inlined) 1113 | { 1114 | emitIntent(); 1115 | m_Out << "set(" << name << " \"" << result << "\")\n"; 1116 | } 1117 | } 1118 | } 1119 | } 1120 | 1121 | bool CMakeBackend::lowerIntrinsics(Function& f) 1122 | { 1123 | auto lowered = false; 1124 | 1125 | for (auto& bb : f) 1126 | { 1127 | for (auto iter = bb.begin(), end = bb.end(); iter != end;) 1128 | { 1129 | // TODO: 若 lower 后的 intrinsic 包含 intrinsic 的调用,需重新处理 1130 | if (const auto call = dyn_cast(iter++); 1131 | call && call->getCalledFunction() && call->getCalledFunction()->isIntrinsic() && 1132 | std::find(std::begin(ImplementedIntrinsics), std::end(ImplementedIntrinsics), 1133 | call->getCalledFunction()->getIntrinsicID()) == std::end(ImplementedIntrinsics)) 1134 | { 1135 | lowered = true; 1136 | m_IntrinsicLowering->LowerIntrinsicCall(call); 1137 | } 1138 | } 1139 | } 1140 | 1141 | return lowered; 1142 | } 1143 | 1144 | void CMakeBackend::emitIntrinsics() 1145 | { 1146 | m_Out << CMakeIntrinsics; 1147 | } 1148 | 1149 | void CMakeBackend::visitIntrinsics(CallInst& call) 1150 | { 1151 | const auto func = call.getCalledFunction(); 1152 | assert(func && func->isIntrinsic()); 1153 | const auto id = func->getIntrinsicID(); 1154 | assert(id != Intrinsic::not_intrinsic); 1155 | 1156 | switch (id) 1157 | { 1158 | case Intrinsic::memcpy: { 1159 | // 假设与 load + store 等价 1160 | 1161 | const auto dstPtr = call.getArgOperand(0); 1162 | const auto srcPtr = call.getArgOperand(1); 1163 | // 忽略 size 1164 | 1165 | const auto content = allocateTemporaryName(); 1166 | emitLoad(content, srcPtr); 1167 | emitStore("${" + content + "}", dstPtr); 1168 | 1169 | break; 1170 | } 1171 | case Intrinsic::memset: { 1172 | const auto dstPtr = call.getArgOperand(0); 1173 | [[maybe_unused]] const auto value = call.getArgOperand(1); 1174 | // 忽略 size 1175 | 1176 | // 假设总为 0 1177 | assert(isa(value) && cast(value)->getValue().isNullValue()); 1178 | 1179 | const auto dst = evalOperand(dstPtr); 1180 | emitIntent(); 1181 | m_Out << "list(TRANSFORM " << dst << " REPLACE \".*\" \"0\")\n"; 1182 | 1183 | break; 1184 | } 1185 | default: 1186 | assert(!"Not implemented."); 1187 | std::terminate(); 1188 | } 1189 | } 1190 | 1191 | bool CMakeBackend::lowerPHINode(llvm::Function& f) 1192 | { 1193 | if (f.empty()) 1194 | { 1195 | return false; 1196 | } 1197 | 1198 | auto modified = false; 1199 | IRBuilder<> builder{ &f.getEntryBlock(), f.getEntryBlock().begin() }; 1200 | for (auto& bb : f) 1201 | { 1202 | for (auto iter = bb.begin(); iter != bb.end();) 1203 | { 1204 | if (const auto phiNode = dyn_cast(iter++)) 1205 | { 1206 | const auto phiCopy = builder.CreateAlloca(phiNode->getType()); 1207 | for (std::size_t i = 0, size = phiNode->getNumIncomingValues(); i < size; ++i) 1208 | { 1209 | const auto incomingValue = phiNode->getIncomingValue(i); 1210 | const auto incomingBlock = phiNode->getIncomingBlock(i); 1211 | 1212 | IRBuilder<> incomingBuilder{ incomingBlock, 1213 | incomingBlock->getTerminator()->getIterator() }; 1214 | incomingBuilder.CreateStore(incomingValue, phiCopy); 1215 | } 1216 | 1217 | IRBuilder<> replaceBuilder{ phiNode->getParent(), phiNode->getIterator() }; 1218 | const auto load = 1219 | replaceBuilder.CreateLoad(phiCopy, NormalizeIdentifier(phiNode->getName())); 1220 | phiNode->replaceAllUsesWith(load); 1221 | phiNode->eraseFromParent(); 1222 | 1223 | modified = true; 1224 | } 1225 | } 1226 | } 1227 | 1228 | return modified; 1229 | } 1230 | 1231 | void CMakeBackend::emitIntent() 1232 | { 1233 | for (std::size_t i = 0; i < m_CurrentIntent; ++i) 1234 | { 1235 | m_Out << "\t"; 1236 | } 1237 | } 1238 | 1239 | std::size_t CMakeBackend::getTemporaryID(llvm::Value* v) 1240 | { 1241 | auto iter = m_TemporaryID.find(v); 1242 | if (iter == m_TemporaryID.end()) 1243 | { 1244 | std::tie(iter, std::ignore) = m_TemporaryID.emplace(v, m_CurrentTemporaryID++); 1245 | } 1246 | 1247 | return iter->second; 1248 | } 1249 | 1250 | std::size_t CMakeBackend::allocateTemporaryID() 1251 | { 1252 | return m_CurrentTemporaryID++; 1253 | } 1254 | 1255 | std::string CMakeBackend::getTemporaryName(std::size_t id) 1256 | { 1257 | return (m_CurrentFunction ? "_LLVM_CMAKE_TEMP_${_LLVM_CMAKE_CURRENT_DEPTH}_" 1258 | : "_LLVM_CMAKE_TEMP_GLOBAL_") + 1259 | std::to_string(id); 1260 | } 1261 | 1262 | std::string CMakeBackend::allocateTemporaryName() 1263 | { 1264 | return getTemporaryName(allocateTemporaryID()); 1265 | } 1266 | 1267 | std::string CMakeBackend::getValueName(llvm::Value* v) 1268 | { 1269 | if (v->hasName()) 1270 | { 1271 | if (dyn_cast(v)) 1272 | { 1273 | return NormalizeIdentifier(v->getName()); 1274 | } 1275 | 1276 | return "_LLVM_CMAKE_${_LLVM_CMAKE_CURRENT_DEPTH}_" + NormalizeIdentifier(v->getName()); 1277 | } 1278 | 1279 | return getTemporaryName(getTemporaryID(v)); 1280 | } 1281 | 1282 | std::string CMakeBackend::getFunctionReturnValueName(llvm::Function* f) 1283 | { 1284 | return "_LLVM_CMAKE_RETURN_VALUE"; 1285 | } 1286 | 1287 | std::string CMakeBackend::getFunctionModifiedExternalVariableListName(llvm::Function* f, 1288 | bool isReturning) 1289 | { 1290 | using namespace std::literals::string_literals; 1291 | return "_LLVM_CMAKE"s + (isReturning ? "_RETURN_MODIFIED_EXTERNAL_VARIABLE_LIST" 1292 | : "_MODIFIED_EXTERNAL_VARIABLE_LIST"); 1293 | } 1294 | 1295 | llvm::StringRef CMakeBackend::getTypeName(llvm::Type* type) 1296 | { 1297 | using namespace std::literals::string_literals; 1298 | 1299 | auto iter = m_TypeNameCache.find(type); 1300 | if (iter == m_TypeNameCache.end()) 1301 | { 1302 | std::string typeName; 1303 | if (type->isStructTy()) 1304 | { 1305 | typeName = type->getStructName(); 1306 | } 1307 | else if (type->isVoidTy()) 1308 | { 1309 | typeName = "Void"; 1310 | } 1311 | else if (const auto intType = dyn_cast(type)) 1312 | { 1313 | switch (intType->getBitWidth()) 1314 | { 1315 | case 1: 1316 | typeName = "Bool"; 1317 | break; 1318 | case 8: 1319 | case 16: 1320 | case 32: 1321 | case 64: 1322 | case 128: 1323 | default: 1324 | typeName = "Integer"; 1325 | break; 1326 | } 1327 | } 1328 | else if (const auto ptrType = dyn_cast(type)) 1329 | { 1330 | const auto elemType = ptrType->getElementType(); 1331 | typeName = "*"; 1332 | typeName += getTypeName(elemType); 1333 | } 1334 | else if (type->isArrayTy()) 1335 | { 1336 | typeName = "["; 1337 | typeName += getTypeName(type->getArrayElementType()); 1338 | typeName += "; "; 1339 | typeName += std::to_string(type->getArrayNumElements()); 1340 | typeName += "]"; 1341 | } 1342 | else if (const auto funcType = dyn_cast(type)) 1343 | { 1344 | typeName = "("; 1345 | if (funcType->getNumParams()) 1346 | { 1347 | typeName += getTypeName(funcType->getParamType(0)); 1348 | 1349 | for (std::size_t i = 1; i < funcType->getNumParams(); ++i) 1350 | { 1351 | typeName += ", "; 1352 | typeName += getTypeName(funcType->getParamType(i)); 1353 | } 1354 | } 1355 | 1356 | typeName += ") -> "; 1357 | typeName += getTypeName(funcType->getReturnType()); 1358 | } 1359 | else 1360 | { 1361 | assert(!"Unsupported type."); 1362 | std::terminate(); 1363 | } 1364 | 1365 | std::tie(iter, std::ignore) = m_TypeNameCache.emplace(type, std::move(typeName)); 1366 | } 1367 | 1368 | return iter->second; 1369 | } 1370 | 1371 | std::size_t CMakeBackend::getTypeFieldCount(llvm::Type* type) 1372 | { 1373 | auto iter = m_TypeFieldCountCache.find(type); 1374 | if (iter == m_TypeFieldCountCache.end()) 1375 | { 1376 | std::size_t result; 1377 | if (const auto structType = dyn_cast(type)) 1378 | { 1379 | result = 0; 1380 | for (std::size_t i = 0; i < structType->getNumElements(); ++i) 1381 | { 1382 | result += getTypeFieldCount(structType->getElementType(i)); 1383 | } 1384 | } 1385 | else if (type->isArrayTy()) 1386 | { 1387 | result = type->getArrayNumElements() * getTypeFieldCount(type->getArrayElementType()); 1388 | } 1389 | else 1390 | { 1391 | result = 1; 1392 | } 1393 | 1394 | std::tie(iter, std::ignore) = m_TypeFieldCountCache.emplace(type, result); 1395 | } 1396 | 1397 | return iter->second; 1398 | } 1399 | 1400 | unsigned CMakeBackend::getTypeSizeInBits(Type* type) 1401 | { 1402 | if (!type->isSized()) 1403 | { 1404 | return 0; 1405 | } 1406 | 1407 | if (const auto size = type->getScalarSizeInBits()) 1408 | { 1409 | return size; 1410 | } 1411 | 1412 | if (type->isPointerTy()) 1413 | { 1414 | return m_DataLayout->getPointerSizeInBits(); 1415 | } 1416 | 1417 | return 0; 1418 | } 1419 | 1420 | llvm::StringRef CMakeBackend::getTypeZeroInitializer(llvm::Type* type) 1421 | { 1422 | auto iter = m_TypeZeroInitializerCache.find(type); 1423 | if (iter == m_TypeZeroInitializerCache.end()) 1424 | { 1425 | std::string zeroInitializer; 1426 | if (const auto structType = dyn_cast(type)) 1427 | { 1428 | const auto numFields = structType->getNumElements(); 1429 | for (std::size_t i = 0; i < numFields; ++i) 1430 | { 1431 | zeroInitializer += getTypeZeroInitializer(structType->getElementType(i)); 1432 | if (i != numFields - 1) 1433 | { 1434 | zeroInitializer += ';'; 1435 | } 1436 | } 1437 | } 1438 | else if (const auto arrayType = dyn_cast(type)) 1439 | { 1440 | const auto size = arrayType->getNumElements(); 1441 | auto singleElem = getTypeZeroInitializer(arrayType->getElementType()); 1442 | for (std::size_t i = 0; i < size; ++i) 1443 | { 1444 | zeroInitializer += singleElem; 1445 | if (i != size - 1) 1446 | { 1447 | zeroInitializer += ';'; 1448 | } 1449 | } 1450 | } 1451 | else if (const auto intType = dyn_cast(type)) 1452 | { 1453 | switch (intType->getBitWidth()) 1454 | { 1455 | case 1: 1456 | zeroInitializer = "0"; 1457 | break; 1458 | case 8: 1459 | // TODO: 空字符串?目前作为整数类型处理 1460 | default: 1461 | zeroInitializer = "0"; 1462 | break; 1463 | } 1464 | } 1465 | else if (isa(type)) 1466 | { 1467 | zeroInitializer = "_LLVM_CMAKE_PTR.NUL"; 1468 | } 1469 | else 1470 | { 1471 | assert(!"Unimplemented"); 1472 | std::terminate(); 1473 | } 1474 | 1475 | std::tie(iter, std::ignore) = 1476 | m_TypeZeroInitializerCache.emplace(type, std::move(zeroInitializer)); 1477 | } 1478 | 1479 | return iter->second; 1480 | } 1481 | 1482 | std::unordered_set const& 1483 | CMakeBackend::getReferencedGlobalValues(llvm::Function& f) 1484 | { 1485 | auto iter = m_FunctionReferencedGlobalValues.find(&f); 1486 | if (iter == m_FunctionReferencedGlobalValues.end()) 1487 | { 1488 | std::unordered_set referencedGlobalValues; 1489 | 1490 | for (auto& bb : f) 1491 | { 1492 | for (auto& ins : bb) 1493 | { 1494 | const auto opNum = ins.getNumOperands(); 1495 | for (std::size_t i = 0; i < opNum; ++i) 1496 | { 1497 | const auto op = ins.getOperand(i); 1498 | if (const auto globalValue = dyn_cast(op)) 1499 | { 1500 | referencedGlobalValues.emplace(globalValue); 1501 | } 1502 | } 1503 | } 1504 | } 1505 | 1506 | std::tie(iter, std::ignore) = 1507 | m_FunctionReferencedGlobalValues.emplace(&f, std::move(referencedGlobalValues)); 1508 | } 1509 | 1510 | return iter->second; 1511 | } 1512 | 1513 | void CMakeBackend::emitFunction(Function& f) 1514 | { 1515 | m_Out << "function(" << NormalizeIdentifier(f.getName()) << ")\n"; 1516 | 1517 | ++m_CurrentIntent; 1518 | emitFunctionPrologue(f); 1519 | --m_CurrentIntent; 1520 | 1521 | if (m_CurrentStateInfo) 1522 | { 1523 | emitStateMachineFunction(f); 1524 | } 1525 | else 1526 | { 1527 | for (auto& bb : f) 1528 | { 1529 | emitBasicBlock(&bb); 1530 | } 1531 | } 1532 | 1533 | ++m_CurrentIntent; 1534 | emitFunctionEpilogue(f); 1535 | --m_CurrentIntent; 1536 | 1537 | m_Out << "endfunction()\n\n"; 1538 | } 1539 | 1540 | void CMakeBackend::emitStateMachineFunction(llvm::Function& f) 1541 | { 1542 | assert(m_CurrentStateInfo); 1543 | 1544 | m_CurrentStateInfo->Init(); 1545 | 1546 | ++m_CurrentIntent; 1547 | emitIntent(); 1548 | m_Out << "set(_LLVM_CMAKE_LOOP_STATE 0)\n"; 1549 | emitIntent(); 1550 | m_Out << "while(1)\n"; 1551 | 1552 | std::size_t curStateId = 0; 1553 | auto shouldEmitSectionProlog = true; 1554 | 1555 | for (auto& bb : f) 1556 | { 1557 | if (shouldEmitSectionProlog) 1558 | { 1559 | ++m_CurrentIntent; 1560 | emitIntent(); 1561 | m_Out << "if(_LLVM_CMAKE_LOOP_STATE STREQUAL \"" << curStateId << "\")\n"; 1562 | shouldEmitSectionProlog = false; 1563 | } 1564 | 1565 | emitBasicBlock(&bb, false); 1566 | 1567 | const auto terminator = bb.getTerminator(); 1568 | const auto br = llvm::dyn_cast(terminator); 1569 | const auto isFallThrough = 1570 | br && br->isUnconditional() && br->getSuccessor(0) == bb.getNextNode(); 1571 | ++m_CurrentIntent; 1572 | visit(terminator); 1573 | --m_CurrentIntent; 1574 | 1575 | const auto stateId = m_CurrentStateInfo->GetNextStateID(); 1576 | if (curStateId != stateId) 1577 | { 1578 | if (isFallThrough) 1579 | { 1580 | ++m_CurrentIntent; 1581 | emitIntent(); 1582 | m_Out << "set(_LLVM_CMAKE_LOOP_STATE \"" << stateId << "\")\n"; 1583 | --m_CurrentIntent; 1584 | } 1585 | 1586 | emitIntent(); 1587 | m_Out << "endif()\n"; 1588 | --m_CurrentIntent; 1589 | curStateId = stateId; 1590 | shouldEmitSectionProlog = true; 1591 | } 1592 | } 1593 | 1594 | emitIntent(); 1595 | m_Out << "endwhile()\n"; 1596 | --m_CurrentIntent; 1597 | } 1598 | 1599 | void CMakeBackend::emitFunctionPrologue(llvm::Function& f) 1600 | { 1601 | // 增加深度 1602 | // 不需要恢复,函数返回时会丢弃修改的值 1603 | emitIntent(); 1604 | m_Out << "math(EXPR _LLVM_CMAKE_CURRENT_DEPTH \"${_LLVM_CMAKE_CURRENT_DEPTH} + 1\")\n"; 1605 | 1606 | // 输出参数 1607 | std::size_t index{}; 1608 | for (auto& arg : f.args()) 1609 | { 1610 | const auto argType = arg.getType(); 1611 | const auto fieldCount = getTypeFieldCount(argType); 1612 | 1613 | emitIntent(); 1614 | m_Out << "list(SUBLIST ARGN " << index << " " << fieldCount << " " << getValueName(&arg) 1615 | << ")\n"; 1616 | index += fieldCount; 1617 | } 1618 | } 1619 | 1620 | void CMakeBackend::emitFunctionEpilogue(llvm::Function& f) 1621 | { 1622 | } 1623 | 1624 | void CMakeBackend::emitBasicBlock(BasicBlock* bb, bool emitTerminator) 1625 | { 1626 | #ifndef NDEBUG 1627 | outs() << *bb << "\n"; 1628 | #endif 1629 | 1630 | if (!m_CondElseEndifStack.empty()) 1631 | { 1632 | if (const auto [elseBlock, endIfBlock] = m_CondElseEndifStack.back(); 1633 | bb == elseBlock && bb != endIfBlock) 1634 | { 1635 | emitIntent(); 1636 | m_Out << "else()\n"; 1637 | } 1638 | else if (bb == endIfBlock) 1639 | { 1640 | emitIntent(); 1641 | m_Out << "endif()\n"; 1642 | m_CondElseEndifStack.pop_back(); 1643 | --m_CurrentIntent; 1644 | } 1645 | } 1646 | 1647 | ++m_CurrentIntent; 1648 | for (auto iter = bb->begin(), end = std::prev(bb->end()); iter != end; ++iter) 1649 | { 1650 | auto& ins = *iter; 1651 | visit(ins); 1652 | } 1653 | 1654 | if (emitTerminator) 1655 | { 1656 | visit(bb->getTerminator()); 1657 | } 1658 | --m_CurrentIntent; 1659 | } 1660 | 1661 | std::string CMakeBackend::evalOperand(llvm::Value* v, llvm::StringRef nameHint) 1662 | { 1663 | if (const auto con = dyn_cast(v); con) 1664 | { 1665 | return evalConstant(con, nameHint).first; 1666 | } 1667 | else 1668 | { 1669 | // 是变量引用 1670 | return "${" + (nameHint.empty() ? getValueName(v) : static_cast(nameHint)) + "}"; 1671 | } 1672 | } 1673 | 1674 | std::pair CMakeBackend::evalConstant(llvm::Constant* con, 1675 | llvm::StringRef nameHint) 1676 | { 1677 | // 全局变量在 llvm 中始终以指针引用,故不需解引用 1678 | if (isa(con)) 1679 | { 1680 | if (isa(con)) 1681 | { 1682 | return { getValueName(con), true }; 1683 | } 1684 | return { "_LLVM_CMAKE_PTR.EXT:" + getValueName(con), true }; 1685 | } 1686 | 1687 | auto conName = nameHint.empty() ? getValueName(con) : static_cast(nameHint); 1688 | 1689 | if (const auto ce = dyn_cast(con)) 1690 | { 1691 | const auto opCode = ce->getOpcode(); 1692 | if (Instruction::isBinaryOp(opCode)) 1693 | { 1694 | emitBinaryExpr(static_cast(opCode), ce->getOperand(0), 1695 | ce->getOperand(1), conName); 1696 | return { "${" + conName + "}", false }; 1697 | } 1698 | 1699 | if (opCode == Instruction::GetElementPtr) 1700 | { 1701 | emitGetElementPtr(ce->getOperand(0), gep_type_begin(ce), gep_type_end(ce), conName); 1702 | return { "${" + conName + "}", false }; 1703 | } 1704 | 1705 | if (Instruction::isCast(opCode)) 1706 | { 1707 | // TODO: 当前 cast 不进行任何操作 1708 | return evalConstant(ce->getOperand(0), conName); 1709 | } 1710 | 1711 | errs() << "Unimplemented: " << *con << "\n"; 1712 | std::terminate(); 1713 | } 1714 | 1715 | if (const auto i = dyn_cast(con)) 1716 | { 1717 | if (i->getBitWidth() == 1) 1718 | { 1719 | return { i->getValue().isNullValue() ? "0" : "1", true }; 1720 | } 1721 | else 1722 | { 1723 | return { i->getValue().toString(10, true), true }; 1724 | } 1725 | } 1726 | else if (isa(con)) 1727 | { 1728 | return { "_LLVM_CMAKE_PTR.NUL", true }; 1729 | } 1730 | else if (const auto aggregate = dyn_cast(con)) 1731 | { 1732 | std::vector operands; 1733 | for (std::size_t i = 0; i < aggregate->getNumOperands(); ++i) 1734 | { 1735 | const auto op = aggregate->getOperand(i); 1736 | operands.emplace_back( 1737 | evalConstant(aggregate->getOperand(i), getConstantFieldTempName(conName, i)).first); 1738 | } 1739 | 1740 | std::string result; 1741 | if (!operands.empty()) 1742 | { 1743 | result += operands.front(); 1744 | for (auto iter = std::next(operands.begin()); iter != operands.end(); ++iter) 1745 | { 1746 | result += ";"; 1747 | result += *iter; 1748 | } 1749 | } 1750 | 1751 | return { std::move(result), true }; 1752 | } 1753 | else if (const auto seq = dyn_cast(con)) 1754 | { 1755 | std::string result; 1756 | const auto elemType = cast(seq->getType())->getElementType(); 1757 | if (elemType->isIntegerTy()) 1758 | { 1759 | if (seq->getNumElements()) 1760 | { 1761 | result += cast(seq->getElementAsConstant(0))->getValue().toString(10, true); 1762 | for (std::size_t i = 1; i < seq->getNumElements(); ++i) 1763 | { 1764 | result += ";"; 1765 | result += cast(seq->getElementAsConstant(i))->getValue().toString(10, true); 1766 | } 1767 | } 1768 | } 1769 | else 1770 | { 1771 | assert(!"Unimplemented"); 1772 | std::terminate(); 1773 | } 1774 | 1775 | return { std::move(result), true }; 1776 | } 1777 | else if (isa(con) || isa(con)) 1778 | { 1779 | return std::pair{ getTypeZeroInitializer(con->getType()), true }; 1780 | } 1781 | else 1782 | { 1783 | errs() << "Unimplemented: " << *con << "\n"; 1784 | std::terminate(); 1785 | } 1786 | 1787 | return { "${" + conName + "}", false }; 1788 | } 1789 | 1790 | std::string CMakeBackend::getConstantFieldTempName(llvm::StringRef constantName, std::size_t index) 1791 | { 1792 | return ("_LLVM_CMAKE_" + constantName + "_FIELD_TEMP_" + std::to_string(index)).str(); 1793 | } 1794 | 1795 | llvm::StringRef CMakeBackend::getTypeLayout(llvm::Type* type) 1796 | { 1797 | auto iter = m_TypeLayoutCache.find(type); 1798 | if (iter == m_TypeLayoutCache.end()) 1799 | { 1800 | std::string result; 1801 | if (const auto structType = dyn_cast(type)) 1802 | { 1803 | if (structType->getNumElements()) 1804 | { 1805 | result += getTypeLayout(structType->getElementType(0)); 1806 | for (std::size_t i = 1; i < structType->getNumElements(); ++i) 1807 | { 1808 | result += ";"; 1809 | result += getTypeLayout(structType->getElementType(i)); 1810 | } 1811 | } 1812 | } 1813 | else if (const auto arrayType = dyn_cast(type)) 1814 | { 1815 | if (arrayType->getNumElements()) 1816 | { 1817 | const auto elemType = arrayType->getElementType(); 1818 | const auto elemTypeLayout = getTypeLayout(elemType); 1819 | result += elemTypeLayout; 1820 | for (std::size_t i = 1; i < arrayType->getNumElements(); ++i) 1821 | { 1822 | result += ";"; 1823 | result += elemTypeLayout; 1824 | } 1825 | } 1826 | } 1827 | else 1828 | { 1829 | result = getTypeName(type); 1830 | } 1831 | 1832 | std::tie(iter, std::ignore) = m_TypeLayoutCache.emplace(type, std::move(result)); 1833 | } 1834 | 1835 | return iter->second; 1836 | } 1837 | 1838 | void CMakeBackend::emitLoad(llvm::StringRef resultName, llvm::Value* srcPtr) 1839 | { 1840 | const auto operand = evalOperand(srcPtr); 1841 | emitIntent(); 1842 | m_Out << "_LLVM_CMAKE_LOAD(" << operand << " " << resultName << ")\n"; 1843 | } 1844 | 1845 | void CMakeBackend::emitStore(llvm::Value* value, llvm::Value* destPtr) 1846 | { 1847 | const auto operandValue = evalOperand(value); 1848 | emitStore(operandValue, destPtr); 1849 | } 1850 | 1851 | void CMakeBackend::emitStore(llvm::StringRef valueExpr, llvm::Value* destPtr) 1852 | { 1853 | const auto operandDest = evalOperand(destPtr); 1854 | 1855 | emitIntent(); 1856 | m_Out << "_LLVM_CMAKE_STORE(" << getFunctionModifiedExternalVariableListName(m_CurrentFunction) 1857 | << " " << operandDest << " " << valueExpr << ")\n"; 1858 | } 1859 | 1860 | void CMakeBackend::emitBinaryExpr(llvm::Instruction::BinaryOps opCode, llvm::Value* lhs, 1861 | llvm::Value* rhs, llvm::StringRef name) 1862 | { 1863 | if (lhs->getType()->isPointerTy() || rhs->getType()->isPointerTy()) 1864 | { 1865 | emitPointerArithmetic(opCode, lhs, rhs, name); 1866 | return; 1867 | } 1868 | 1869 | const auto operandLhs = evalOperand(lhs); 1870 | const auto operandRhs = evalOperand(rhs); 1871 | 1872 | emitIntent(); 1873 | m_Out << "math(EXPR " << name << " \"" << operandLhs << " "; 1874 | 1875 | // TODO: 溢出处理 1876 | // NOTE: CMake 的 math 是将所有数作为 int64 处理的 1877 | switch (opCode) 1878 | { 1879 | default: 1880 | assert("Impossible"); 1881 | std::terminate(); 1882 | case Instruction::Add: 1883 | m_Out << "+"; 1884 | break; 1885 | case Instruction::Sub: 1886 | m_Out << "-"; 1887 | break; 1888 | case Instruction::Mul: 1889 | m_Out << "*"; 1890 | break; 1891 | case Instruction::SDiv: 1892 | case Instruction::UDiv: 1893 | m_Out << "/"; 1894 | break; 1895 | case Instruction::SRem: 1896 | case Instruction::URem: 1897 | m_Out << "%"; 1898 | break; 1899 | case Instruction::Or: 1900 | m_Out << "|"; 1901 | break; 1902 | case Instruction::And: 1903 | m_Out << "&"; 1904 | break; 1905 | case Instruction::Xor: 1906 | m_Out << "^"; 1907 | break; 1908 | case Instruction::Shl: 1909 | m_Out << "<<"; 1910 | break; 1911 | case Instruction::LShr: 1912 | case Instruction::AShr: 1913 | m_Out << ">>"; 1914 | break; 1915 | } 1916 | 1917 | m_Out << " " << operandRhs << "\")\n"; 1918 | } 1919 | 1920 | void CMakeBackend::emitPointerArithmetic(llvm::Instruction::BinaryOps opCode, llvm::Value* lhs, 1921 | llvm::Value* rhs, llvm::StringRef name) 1922 | { 1923 | if (opCode != Instruction::Add && opCode != Instruction::Sub) 1924 | { 1925 | errs() << "Error"; 1926 | std::terminate(); 1927 | } 1928 | 1929 | const auto operandLhs = evalOperand(lhs); 1930 | const auto operandRhs = evalOperand(rhs); 1931 | 1932 | if (lhs->getType()->isPointerTy() && rhs->getType()->isPointerTy()) 1933 | { 1934 | if (opCode != Instruction::Sub) 1935 | { 1936 | errs() << "Error"; 1937 | std::terminate(); 1938 | } 1939 | 1940 | emitIntent(); 1941 | m_Out << "_LLVM_CMAKE_POINTER_OFFSET(" << operandLhs << " " << operandRhs << " " << name 1942 | << ")\n"; 1943 | } 1944 | else 1945 | { 1946 | const auto& [ptrOp, ptrValue, offsetOp, offsetValue] = 1947 | lhs->getType()->isPointerTy() ? std::tuple{ lhs, operandLhs, rhs, operandRhs } 1948 | : std::tuple{ rhs, operandRhs, lhs, operandLhs }; 1949 | if (!offsetOp->getType()->isIntegerTy()) 1950 | { 1951 | errs() << "Error type for offset: " << *offsetOp->getType() << "\n"; 1952 | std::terminate(); 1953 | } 1954 | 1955 | emitIntent(); 1956 | m_Out << "_LLVM_CMAKE_POINTER_APPLY_OFFSET(" << ptrValue 1957 | << (opCode == Instruction::Add ? " " : " -") << offsetValue << " " << name << ")\n"; 1958 | } 1959 | } 1960 | 1961 | void CMakeBackend::emitGetElementPtr(llvm::Value* ptrOperand, llvm::gep_type_iterator gepBegin, 1962 | llvm::gep_type_iterator gepEnd, llvm::StringRef name) 1963 | { 1964 | if (gepBegin == gepEnd) 1965 | { 1966 | assert(!"Not implemented"); 1967 | std::terminate(); 1968 | } 1969 | 1970 | std::size_t realIdx{}; 1971 | // 系数及对应的表达式 1972 | std::vector> offsets; 1973 | 1974 | const auto ptrValue = [&] { 1975 | const auto firstIdx = (gepBegin++).getOperand(); 1976 | if (const auto constIdx = dyn_cast(firstIdx); 1977 | !constIdx || !constIdx->getValue().isNullValue()) 1978 | { 1979 | const auto resultPtrName = allocateTemporaryName(); 1980 | emitPointerArithmetic(Instruction::Add, ptrOperand, firstIdx, resultPtrName); 1981 | return "${" + resultPtrName + "}"; 1982 | } 1983 | else 1984 | { 1985 | return evalOperand(ptrOperand); 1986 | } 1987 | }(); 1988 | 1989 | const auto ptrType = cast(ptrOperand->getType()); 1990 | auto pointeeType = ptrType->getElementType(); 1991 | for (auto iter = gepBegin; iter != gepEnd; ++iter) 1992 | { 1993 | const auto idxOperand = iter.getOperand(); 1994 | if (const auto constIdx = dyn_cast(idxOperand)) 1995 | { 1996 | const auto idxValue = constIdx->getValue().getZExtValue(); 1997 | if (const auto arrayType = dyn_cast(pointeeType)) 1998 | { 1999 | const auto fieldSize = getTypeFieldCount(arrayType->getElementType()); 2000 | realIdx += idxValue * fieldSize; 2001 | pointeeType = arrayType->getElementType(); 2002 | } 2003 | else if (const auto structType = dyn_cast(pointeeType)) 2004 | { 2005 | for (std::size_t i = 0; i < idxValue; ++i) 2006 | { 2007 | const auto fieldType = structType->getElementType(i); 2008 | realIdx += getTypeFieldCount(fieldType); 2009 | } 2010 | pointeeType = structType->getElementType(idxValue); 2011 | } 2012 | else if (const auto ptrType = dyn_cast(pointeeType)) 2013 | { 2014 | // TODO: 确认正确性 2015 | const auto fieldSize = getTypeFieldCount(ptrType->getElementType()); 2016 | realIdx += idxValue * fieldSize; 2017 | pointeeType = ptrType->getElementType(); 2018 | } 2019 | else 2020 | { 2021 | assert(!"Unsupported type"); 2022 | std::terminate(); 2023 | } 2024 | } 2025 | else 2026 | { 2027 | // TODO: 当前仅支持数组及指针的动态索引,是否需要支持结构体的动态索引? 2028 | if (const auto arrayType = dyn_cast(pointeeType)) 2029 | { 2030 | offsets.emplace_back(getTypeFieldCount(arrayType->getElementType()), 2031 | evalOperand(idxOperand)); 2032 | } 2033 | else if (const auto ptrType = dyn_cast(pointeeType)) 2034 | { 2035 | offsets.emplace_back(getTypeFieldCount(ptrType->getElementType()), evalOperand(idxOperand)); 2036 | } 2037 | else 2038 | { 2039 | assert(!"Not implemented"); 2040 | std::terminate(); 2041 | } 2042 | } 2043 | } 2044 | 2045 | // 结果是指针 2046 | 2047 | const auto offsetName = allocateTemporaryName(); 2048 | 2049 | if (offsets.empty()) 2050 | { 2051 | emitIntent(); 2052 | m_Out << "set(" << offsetName << " \"" << realIdx << "\")\n"; 2053 | } 2054 | else 2055 | { 2056 | emitIntent(); 2057 | m_Out << "math(EXPR " << offsetName << " \"" << realIdx; 2058 | for (const auto& [size, expr] : offsets) 2059 | { 2060 | m_Out << " + " << size << " * " << expr; 2061 | } 2062 | m_Out << "\")\n"; 2063 | } 2064 | 2065 | emitIntent(); 2066 | m_Out << "_LLVM_CMAKE_CONSTRUCT_GEP(${" << offsetName << "} " << ptrValue << " " << name << ")\n"; 2067 | } 2068 | -------------------------------------------------------------------------------- /lib/CMakeBackend.h: -------------------------------------------------------------------------------- 1 | #ifndef CMAKE_BACKEND_H 2 | #define CMAKE_BACKEND_H 3 | 4 | #include "CMakeTargetMachine.h" 5 | #include "CheckNeedGotoPass.h" 6 | 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | namespace LLVMCMakeBackend 36 | { 37 | class CMakeBackend : public llvm::FunctionPass, public llvm::InstVisitor 38 | { 39 | static char ID; 40 | 41 | static constexpr unsigned ImplementedIntrinsics[]{ llvm::Intrinsic::memcpy, 42 | llvm::Intrinsic::memset }; 43 | 44 | public: 45 | explicit CMakeBackend(llvm::raw_ostream& outStream) 46 | : FunctionPass{ ID }, m_Out{ outStream }, m_CurrentFunction{}, m_CurrentIntent{}, 47 | m_CurrentTemporaryID{} 48 | { 49 | } 50 | 51 | llvm::StringRef getPassName() const override 52 | { 53 | return "CMake backend"; 54 | } 55 | 56 | bool doInitialization(llvm::Module& M) override; 57 | bool doFinalization(llvm::Module& M) override; 58 | bool runOnFunction(llvm::Function& F) override; 59 | 60 | void getAnalysisUsage(llvm::AnalysisUsage& au) const override; 61 | 62 | void visitInstruction(llvm::Instruction& I); 63 | 64 | void visitCastInst(llvm::CastInst& I); 65 | 66 | void visitReturnInst(llvm::ReturnInst& i); 67 | 68 | void visitCallInst(llvm::CallInst& I); 69 | void visitInlineAsm(llvm::CallInst& I); 70 | 71 | void visitBinaryOperator(llvm::BinaryOperator& I); 72 | 73 | void visitLoadInst(llvm::LoadInst& I); 74 | void visitStoreInst(llvm::StoreInst& I); 75 | void visitAllocaInst(llvm::AllocaInst& I); 76 | void visitGetElementPtrInst(llvm::GetElementPtrInst& I); 77 | 78 | void visitExtractValueInst(llvm::ExtractValueInst& I); 79 | void visitInsertValueInst(llvm::InsertValueInst& I); 80 | 81 | void visitBranchInst(llvm::BranchInst& I); 82 | 83 | void visitICmpInst(llvm::ICmpInst& I); 84 | 85 | void visitSelectInst(llvm::SelectInst& I); 86 | 87 | void visitUnreachableInst(llvm::UnreachableInst& I); 88 | 89 | private: 90 | llvm::raw_ostream& m_Out; 91 | llvm::Function* m_CurrentFunction; 92 | std::optional m_CurrentStateInfo; 93 | std::vector m_CurrentFunctionLocalEntityNames; 94 | std::unique_ptr m_DataLayout; 95 | std::unique_ptr m_IntrinsicLowering; 96 | 97 | void emitModuleInfo(llvm::Module& m); 98 | 99 | void emitModulePrologue(llvm::Module& m); 100 | void emitModuleEpilogue(llvm::Module& m); 101 | 102 | void emitGlobals(llvm::Module& m); 103 | 104 | bool lowerIntrinsics(llvm::Function& f); 105 | void emitIntrinsics(); 106 | void visitIntrinsics(llvm::CallInst& call); 107 | 108 | bool lowerPHINode(llvm::Function& f); 109 | 110 | std::size_t m_CurrentIntent; 111 | void emitIntent(); 112 | 113 | std::vector> m_CondElseEndifStack; 114 | 115 | std::unordered_map m_TemporaryID; 116 | std::size_t m_CurrentTemporaryID; 117 | std::size_t getTemporaryID(llvm::Value* v); 118 | std::size_t allocateTemporaryID(); 119 | std::string getTemporaryName(std::size_t id); 120 | std::string allocateTemporaryName(); 121 | std::string getValueName(llvm::Value* v); 122 | std::string getFunctionReturnValueName(llvm::Function* f); 123 | std::string getFunctionModifiedExternalVariableListName(llvm::Function* f, 124 | bool isReturning = false); 125 | 126 | std::unordered_map m_TypeNameCache; 127 | llvm::StringRef getTypeName(llvm::Type* type); 128 | 129 | std::unordered_map m_TypeFieldCountCache; 130 | std::size_t getTypeFieldCount(llvm::Type* type); 131 | 132 | unsigned getTypeSizeInBits(llvm::Type* type); 133 | 134 | std::unordered_map m_TypeZeroInitializerCache; 135 | llvm::StringRef getTypeZeroInitializer(llvm::Type* type); 136 | 137 | std::unordered_map> 138 | m_FunctionReferencedGlobalValues; 139 | 140 | std::unordered_set const& getReferencedGlobalValues(llvm::Function& f); 141 | 142 | void emitFunction(llvm::Function& f); 143 | void emitFunctionPrologue(llvm::Function& f); 144 | void emitFunctionEpilogue(llvm::Function& f); 145 | void emitStateMachineFunction(llvm::Function& f); 146 | 147 | void emitBasicBlock(llvm::BasicBlock* bb, bool emitTerminator = true); 148 | 149 | std::string evalOperand(llvm::Value* v, llvm::StringRef nameHint = ""); 150 | // 返回值:结果,是否内联 151 | std::pair evalConstant(llvm::Constant* con, llvm::StringRef nameHint = ""); 152 | 153 | std::string getConstantFieldTempName(llvm::StringRef constantName, std::size_t index); 154 | 155 | std::unordered_map m_TypeLayoutCache; 156 | llvm::StringRef getTypeLayout(llvm::Type* type); 157 | 158 | void emitLoad(llvm::StringRef resultName, llvm::Value* src); 159 | void emitStore(llvm::Value* value, llvm::Value* dest); 160 | void emitStore(llvm::StringRef valueExpr, llvm::Value* dest); 161 | 162 | void emitBinaryExpr(llvm::Instruction::BinaryOps opCode, llvm::Value* lhs, llvm::Value* rhs, 163 | llvm::StringRef name); 164 | void emitPointerArithmetic(llvm::Instruction::BinaryOps opCode, llvm::Value* lhs, 165 | llvm::Value* rhs, llvm::StringRef name); 166 | void emitGetElementPtr(llvm::Value* ptrOperand, llvm::gep_type_iterator gepBegin, 167 | llvm::gep_type_iterator gepEnd, llvm::StringRef name); 168 | }; 169 | } // namespace LLVMCMakeBackend 170 | 171 | #endif 172 | -------------------------------------------------------------------------------- /lib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(LLVM_LINK_COMPONENTS 2 | Analysis 3 | CodeGen 4 | Core 5 | MC 6 | ScalarOpts 7 | Support 8 | Target 9 | SelectionDAG 10 | TransformUtils 11 | ) 12 | 13 | file(GLOB_RECURSE SRCS 14 | *.cpp 15 | *.h 16 | ) 17 | 18 | add_llvm_target(CMakeBackendCodeGen 19 | ${SRCS} 20 | ) 21 | 22 | target_compile_features(LLVMCMakeBackendCodeGen PUBLIC cxx_std_20) 23 | -------------------------------------------------------------------------------- /lib/CMakeTargetMachine.cpp: -------------------------------------------------------------------------------- 1 | #include "CMakeTargetMachine.h" 2 | #include "CMakeBackend.h" 3 | #include "CheckNeedGotoPass.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | using namespace llvm; 10 | 11 | Target CMakeBackendTarget{}; 12 | 13 | extern "C" void LLVMInitializeCMakeBackendTarget() 14 | { 15 | RegisterTargetMachine X(CMakeBackendTarget); 16 | } 17 | 18 | extern "C" void LLVMInitializeCMakeBackendTargetInfo() 19 | { 20 | RegisterTarget<> X(CMakeBackendTarget, "cmake", "CMake backend", "CMake"); 21 | } 22 | 23 | extern "C" void LLVMInitializeCMakeBackendTargetMC() 24 | { 25 | } 26 | 27 | bool LLVMCMakeBackend::CMakeTargetSubtargetInfo::enableAtomicExpand() const 28 | { 29 | return true; 30 | } 31 | 32 | const llvm::TargetLowering* LLVMCMakeBackend::CMakeTargetSubtargetInfo::getTargetLowering() const 33 | { 34 | return &Lowering; 35 | } 36 | 37 | bool LLVMCMakeBackend::CMakeTargetMachine::addPassesToEmitFile( 38 | llvm::PassManagerBase& PM, llvm::raw_pwrite_stream& Out, llvm::raw_pwrite_stream* DwoOut, 39 | llvm::CodeGenFileType FileType, bool DisableVerify, llvm::MachineModuleInfoWrapperPass* MMIWP) 40 | { 41 | if (FileType != CodeGenFileType::CGFT_AssemblyFile) 42 | { 43 | return true; 44 | } 45 | 46 | const auto reg = PassRegistry::getPassRegistry(); 47 | 48 | const auto passConfig = createPassConfig(PM); 49 | PM.add(passConfig); 50 | PM.add(createLowerSwitchPass()); 51 | PM.add(new CheckNeedGotoPass()); 52 | PM.add(new CMakeBackend(Out)); 53 | 54 | return false; 55 | } 56 | 57 | const llvm::TargetSubtargetInfo* 58 | LLVMCMakeBackend::CMakeTargetMachine::getSubtargetImpl(const Function&) const 59 | { 60 | return &SubtargetInfo; 61 | } 62 | -------------------------------------------------------------------------------- /lib/CMakeTargetMachine.h: -------------------------------------------------------------------------------- 1 | #ifndef CMAKE_TARGET_MACHINE_H 2 | #define CMAKE_TARGET_MACHINE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | extern llvm::Target CMakeBackendTarget; 10 | extern "C" void LLVMInitializeCMakeBackendTarget(); 11 | extern "C" void LLVMInitializeCMakeBackendTargetInfo(); 12 | extern "C" void LLVMInitializeCMakeBackendTargetMC(); 13 | 14 | namespace LLVMCMakeBackend 15 | { 16 | class CMakeTargetLowering : public llvm::TargetLowering 17 | { 18 | public: 19 | explicit CMakeTargetLowering(const llvm::TargetMachine& TM) : TargetLowering(TM) 20 | { 21 | setMaxAtomicSizeInBitsSupported(0); 22 | } 23 | }; 24 | 25 | class CMakeTargetSubtargetInfo : public llvm::TargetSubtargetInfo 26 | { 27 | public: 28 | CMakeTargetSubtargetInfo(const llvm::TargetMachine& TM, const llvm::Triple& TT, 29 | llvm::StringRef CPU, llvm::StringRef FS) 30 | : TargetSubtargetInfo(TT, CPU, 31 | #if LLVM_VERSION_MAJOR >= 12 32 | {}, 33 | #endif 34 | FS, {}, {}, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr), 35 | Lowering(TM) 36 | { 37 | } 38 | bool enableAtomicExpand() const override; 39 | const llvm::TargetLowering* getTargetLowering() const override; 40 | 41 | private: 42 | CMakeTargetLowering Lowering; 43 | }; 44 | 45 | class CMakeTargetMachine : public llvm::LLVMTargetMachine 46 | { 47 | public: 48 | CMakeTargetMachine(const llvm::Target& T, const llvm::Triple& TT, llvm::StringRef CPU, 49 | llvm::StringRef FS, const llvm::TargetOptions& Options, 50 | llvm::Optional RM, 51 | llvm::Optional CM, llvm::CodeGenOpt::Level OL, 52 | bool /*JIT*/) 53 | : LLVMTargetMachine(T, "", TT, CPU, FS, Options, RM.getValueOr(llvm::Reloc::Static), 54 | CM.getValueOr(llvm::CodeModel::Small), OL), 55 | SubtargetInfo(*this, TT, CPU, FS) 56 | { 57 | } 58 | 59 | bool addPassesToEmitFile(llvm::PassManagerBase& PM, llvm::raw_pwrite_stream& Out, 60 | llvm::raw_pwrite_stream* DwoOut, llvm::CodeGenFileType FileType, 61 | bool DisableVerify = true, 62 | llvm::MachineModuleInfoWrapperPass* MMIWP = nullptr) override; 63 | 64 | const llvm::TargetSubtargetInfo* getSubtargetImpl(const llvm::Function&) const override; 65 | 66 | private: 67 | CMakeTargetSubtargetInfo SubtargetInfo; 68 | }; 69 | } // namespace LLVMCMakeBackend 70 | 71 | #endif 72 | -------------------------------------------------------------------------------- /lib/CheckNeedGotoPass.cpp: -------------------------------------------------------------------------------- 1 | #include "CheckNeedGotoPass.h" 2 | 3 | #include 4 | 5 | #include 6 | #if __has_include() 7 | # include 8 | #endif 9 | 10 | using namespace LLVMCMakeBackend; 11 | 12 | StateInfo::StateInfo(std::vector bbList) : m_BBList{ std::move(bbList) } 13 | { 14 | } 15 | 16 | void StateInfo::Init() 17 | { 18 | m_CurBB = m_BBList.front(); 19 | m_CurBBStateID = 0; 20 | } 21 | 22 | std::size_t StateInfo::GetNextStateID() 23 | { 24 | if (!m_CurBB || !(m_CurBB = m_CurBB->getNextNode())) 25 | { 26 | return -1; 27 | } 28 | 29 | if (m_CurBBStateID < m_BBList.size() - 1 && m_CurBB == m_BBList[m_CurBBStateID + 1]) 30 | { 31 | ++m_CurBBStateID; 32 | } 33 | return m_CurBBStateID; 34 | } 35 | 36 | std::size_t StateInfo::GetStateID(const llvm::BasicBlock* bb) 37 | { 38 | const auto iter = std::find(m_BBList.begin(), m_BBList.end(), bb); 39 | if (iter == m_BBList.end()) 40 | { 41 | return -1; 42 | } 43 | 44 | return iter - m_BBList.begin(); 45 | } 46 | 47 | char CheckNeedGotoPass::ID{}; 48 | 49 | CheckNeedGotoPass::CheckNeedGotoPass() : FunctionPass(ID) 50 | { 51 | } 52 | 53 | bool CheckNeedGotoPass::runOnFunction(llvm::Function& f) 54 | { 55 | if (checkNeedGoto(f)) 56 | { 57 | buildState(f); 58 | } 59 | 60 | return false; 61 | } 62 | 63 | void CheckNeedGotoPass::releaseMemory() 64 | { 65 | m_BBList.clear(); 66 | } 67 | 68 | bool CheckNeedGotoPass::isCurrentFunctionNeedGoto() const noexcept 69 | { 70 | return !m_BBList.empty(); 71 | } 72 | 73 | StateInfo CheckNeedGotoPass::getCurrentStateInfo() const noexcept 74 | { 75 | return { m_BBList }; 76 | } 77 | 78 | bool CheckNeedGotoPass::checkNeedGoto(llvm::Function& f) const 79 | { 80 | std::vector elseStack; 81 | std::vector endIfStack; 82 | for (const auto& bb : f) 83 | { 84 | const auto terminator = bb.getTerminator(); 85 | if (const auto br = llvm::dyn_cast(terminator)) 86 | { 87 | // 若 br 出现在函数结尾的 bb 里,则其一定是前向的,因此一定需要 goto 88 | if (&bb == &f.back()) 89 | { 90 | return true; 91 | } 92 | 93 | const auto next = bb.getNextNode(); 94 | if (br->isUnconditional()) 95 | { 96 | const auto successor = br->getSuccessor(0); 97 | // 非 fall through,检查是否是 if-else 的非 else 分支,若是,则 endif 应是跳转到的分支而不是 98 | // else 分支 99 | if (successor != next) 100 | { 101 | if (!elseStack.empty() && next == elseStack.back() && !endIfStack.empty() && 102 | next == endIfStack.back()) 103 | { 104 | endIfStack.back() = successor; 105 | } 106 | else 107 | { 108 | return true; 109 | } 110 | } 111 | else 112 | { 113 | while (!endIfStack.empty() && endIfStack.back() == next) 114 | { 115 | endIfStack.pop_back(); 116 | } 117 | } 118 | 119 | continue; 120 | } 121 | 122 | // 是条件跳转 123 | assert(br->isConditional()); 124 | 125 | const auto branch1 = br->getSuccessor(0), branch2 = br->getSuccessor(1); 126 | if (branch1 != next && branch2 != next) 127 | { 128 | return true; 129 | } 130 | 131 | const auto elseBranch = branch1 == next ? branch2 : branch1; 132 | elseStack.push_back(elseBranch); 133 | // 先假设是 endif(即简单 if 形式) 134 | endIfStack.push_back(elseBranch); 135 | } 136 | } 137 | 138 | return !elseStack.empty() || !endIfStack.empty(); 139 | } 140 | 141 | void CheckNeedGotoPass::buildState(llvm::Function& f) 142 | { 143 | std::unordered_set jumpedToBB{ &f.front() }; 144 | for (const auto& bb : f) 145 | { 146 | m_BBList.push_back(&bb); 147 | const auto terminator = bb.getTerminator(); 148 | if (const auto br = llvm::dyn_cast(terminator)) 149 | { 150 | const auto next = bb.getNextNode(); 151 | const auto successorCount = br->getNumSuccessors(); 152 | for (std::size_t i = 0; i < successorCount; ++i) 153 | { 154 | if (const auto successor = br->getSuccessor(i); successor != next) 155 | { 156 | jumpedToBB.emplace(successor); 157 | } 158 | } 159 | } 160 | } 161 | #if __cpp_lib_erase_if >= 202002L 162 | std::erase_if(m_BBList, [&](const llvm::BasicBlock* bb) { return !jumpedToBB.contains(bb); }); 163 | #else 164 | for (auto iter = m_BBList.begin(); iter != m_BBList.end();) 165 | { 166 | const auto bb = *iter; 167 | if (jumpedToBB.find(bb) == jumpedToBB.end()) 168 | { 169 | iter = m_BBList.erase(iter); 170 | } 171 | else 172 | { 173 | ++iter; 174 | } 175 | } 176 | #endif 177 | } 178 | -------------------------------------------------------------------------------- /lib/CheckNeedGotoPass.h: -------------------------------------------------------------------------------- 1 | #ifndef CHECK_NEED_GOTO_PASS_H 2 | #define CHECK_NEED_GOTO_PASS_H 3 | 4 | #include 5 | #include 6 | 7 | namespace LLVMCMakeBackend 8 | { 9 | // 包含必须 goto 的指令的函数体将是如下: 10 | // # function prolog 11 | // set(_LLVM_CMAKE_LOOP_STATE 0) 12 | // while(1) 13 | // if(_LLVM_CMAKE_LOOP_STATE STREQUAL "0") 14 | // # do sth 15 | // set(_LLVM_CMAKE_LOOP_STATE "1") 16 | // endif() 17 | // if(_LLVM_CMAKE_LOOP_STATE STREQUAL "1") 18 | // # do sth2 19 | // set(_LLVM_CMAKE_LOOP_STATE "3") 20 | // continue() 21 | // endif() 22 | // if(_LLVM_CMAKE_LOOP_STATE STREQUAL "2") 23 | // ... 24 | // endif() 25 | // endwhile() 26 | // 暂无 function epilog,因此不需要循环最终退出,但如果之后添加了,需要进行修改 27 | class StateInfo 28 | { 29 | public: 30 | StateInfo(std::vector bbList); 31 | 32 | void Init(); 33 | std::size_t GetNextStateID(); 34 | // 仅对 section 头部返回 id,若非头部返回 -1 35 | std::size_t GetStateID(const llvm::BasicBlock* bb); 36 | 37 | private: 38 | mutable const llvm::BasicBlock* m_CurBB; 39 | mutable std::size_t m_CurBBStateID; 40 | // 各 section 的头部(被跳转到的块) 41 | std::vector m_BBList; 42 | }; 43 | 44 | class CheckNeedGotoPass : public llvm::FunctionPass 45 | { 46 | public: 47 | CheckNeedGotoPass(); 48 | 49 | bool runOnFunction(llvm::Function& f) override; 50 | void releaseMemory() override; 51 | 52 | bool isCurrentFunctionNeedGoto() const noexcept; 53 | StateInfo getCurrentStateInfo() const noexcept; 54 | 55 | static char ID; 56 | 57 | private: 58 | std::vector m_BBList; 59 | 60 | bool checkNeedGoto(llvm::Function& f) const; 61 | void buildState(llvm::Function& f); 62 | }; 63 | } // namespace LLVMCMakeBackend 64 | 65 | #endif 66 | -------------------------------------------------------------------------------- /lib/LLVMBuild.txt: -------------------------------------------------------------------------------- 1 | [component_0] 2 | type = TargetGroup 3 | name = CMakeBackend 4 | parent = Target 5 | 6 | [component_1] 7 | type = Library 8 | name = CMakeBackendCodeGen 9 | parent = CMakeBackend 10 | required_libraries = Analysis CodeGen Core MC ScalarOpts Support Target TransformUtils 11 | add_to_library_groups = CMakeBackend 12 | -------------------------------------------------------------------------------- /testcase/Fib.cpp: -------------------------------------------------------------------------------- 1 | extern "C" unsigned long long fib(int count) 2 | { 3 | if (count < 2) 4 | { 5 | return 1; 6 | } 7 | 8 | unsigned long long result[2]{ 1, 1 }; 9 | for (auto i = 0; i < count - 2; ++i) 10 | { 11 | const auto a = i % 2; 12 | result[a] += result[1 - a]; 13 | } 14 | return result[0] + result[1]; 15 | } 16 | -------------------------------------------------------------------------------- /testcase/Test.cmake: -------------------------------------------------------------------------------- 1 | include(${CMAKE_CURRENT_LIST_DIR}/Test.ll.cmake) 2 | 3 | Main() 4 | message(STATUS "Main() returned ${_LLVM_CMAKE_RETURN_VALUE}") 5 | 6 | # compile with `clang++ TestCpp.cpp -S -emit-llvm -o TestCpp.ll` 7 | include(${CMAKE_CURRENT_LIST_DIR}/TestCpp.ll.cmake) 8 | 9 | GetElem(1) 10 | set(Ptr1 ${_LLVM_CMAKE_RETURN_VALUE}) 11 | message(STATUS "GetElem: ${Ptr1}") 12 | LoadOffset(${Ptr1} 1) 13 | message(STATUS "LoadOffset: ${_LLVM_CMAKE_RETURN_VALUE}") 14 | GetElem(3) 15 | set(Ptr2 ${_LLVM_CMAKE_RETURN_VALUE}) 16 | message(STATUS "GetElem: ${Ptr2}") 17 | GetOffset(${Ptr1} ${Ptr2}) 18 | message(STATUS "GetOffset: ${_LLVM_CMAKE_RETURN_VALUE}") 19 | 20 | CreatePair(1 2) 21 | message(STATUS "returns: ${_LLVM_CMAKE_RETURN_VALUE}") 22 | 23 | set(Dest ";") 24 | CopyPair("_LLVM_CMAKE_PTR.EXT:Dest" "_LLVM_CMAKE_PTR.EXT:_LLVM_CMAKE_RETURN_VALUE") 25 | message(STATUS "${Dest}") 26 | 27 | TestInner0() 28 | message(STATUS "${_LLVM_CMAKE_RETURN_VALUE}") 29 | 30 | GetCallback(0) 31 | InvokeFunc(${_LLVM_CMAKE_RETURN_VALUE} 1) 32 | message(STATUS "InvokeFunc returns ${_LLVM_CMAKE_RETURN_VALUE}") 33 | 34 | GetCallback(1) 35 | InvokeFunc(${_LLVM_CMAKE_RETURN_VALUE} 1) 36 | message(STATUS "InvokeFunc returns ${_LLVM_CMAKE_RETURN_VALUE}") 37 | 38 | AsmTest(1 2) 39 | message(STATUS "AsmTest returns ${_LLVM_CMAKE_RETURN_VALUE}") 40 | 41 | LoopTest(4) 42 | message(STATUS "LoopTest returns ${_LLVM_CMAKE_RETURN_VALUE}") 43 | 44 | LoopTest2(4) 45 | message(STATUS "LoopTest2 returns ${_LLVM_CMAKE_RETURN_VALUE}") 46 | 47 | LoopTest3(4) 48 | message(STATUS "LoopTest3 returns ${_LLVM_CMAKE_RETURN_VALUE}") 49 | 50 | LoopTest4(4) 51 | message(STATUS "LoopTest4 returns ${_LLVM_CMAKE_RETURN_VALUE}") 52 | 53 | TestNew() 54 | message(STATUS "TestNew returns ${_LLVM_CMAKE_RETURN_VALUE}, dereferenced: ${${_LLVM_CMAKE_RETURN_VALUE}}") 55 | 56 | TestNew2() 57 | message(STATUS "TestNew2 returns ${_LLVM_CMAKE_RETURN_VALUE}, dereferenced: ${${_LLVM_CMAKE_RETURN_VALUE}}") 58 | 59 | TestCMakeString() 60 | 61 | # compile with `clang++ VirtualTest.cpp -S -emit-llvm -fno-rtti -fno-exceptions -o VirtualTest.ll` 62 | include(${CMAKE_CURRENT_LIST_DIR}/VirtualTest.ll.cmake) 63 | 64 | set(Obj "") 65 | CreateVirtualBase(_LLVM_CMAKE_PTR.EXT:Obj) 66 | message(STATUS "CreateVirtualBase: ${Obj}") 67 | InvokeFunc(_LLVM_CMAKE_PTR.EXT:Obj 1) 68 | message(STATUS "InvokeFunc: ${_LLVM_CMAKE_RETURN_VALUE}") 69 | 70 | set(Obj "") 71 | CreateDerived(_LLVM_CMAKE_PTR.EXT:Obj) 72 | message(STATUS "CreateDerived: ${Obj}") 73 | InvokeFunc(_LLVM_CMAKE_PTR.EXT:Obj 1) 74 | message(STATUS "InvokeFunc: ${_LLVM_CMAKE_RETURN_VALUE}") 75 | 76 | # compile with `rustc --emit=llvm-ir --crate-type=lib TestRust.rs -o TestRust.ll` 77 | include(${CMAKE_CURRENT_LIST_DIR}/TestRust.ll.cmake) 78 | rust_mian() 79 | message(STATUS "rust_mian: ${_LLVM_CMAKE_RETURN_VALUE}") 80 | 81 | # compile with `clang++ Fib.cpp -S -emit-llvm -o Fib.ll` 82 | include(${CMAKE_CURRENT_LIST_DIR}/Fib.ll.cmake) 83 | fib(100) 84 | message(STATUS "fib: ${_LLVM_CMAKE_RETURN_VALUE}") 85 | -------------------------------------------------------------------------------- /testcase/Test.ll: -------------------------------------------------------------------------------- 1 | @.str = private unnamed_addr constant [4 x i8] c"abc\00", align 1 2 | @.list = private unnamed_addr constant [4 x i32] [i32 1, i32 2, i32 3, i32 4], align 16 3 | 4 | define dso_local i32 @Add(i32, i32) 5 | { 6 | %3 = add i32 %0, %1 7 | ret i32 %3 8 | } 9 | 10 | define dso_local void @Store(i32, i32*) 11 | { 12 | store i32 %0, i32* %1 13 | ret void 14 | } 15 | 16 | define dso_local i32 @Load(i32*) 17 | { 18 | %2 = load i32, i32* %0 19 | ret i32 %2 20 | } 21 | 22 | define dso_local i32 @Main() 23 | { 24 | call void asm "message(STATUS abc)", ""() 25 | %elemptr = getelementptr inbounds [4 x i32], [4 x i32]* @.list, i64 0, i64 1 26 | %elem = load i32, i32* %elemptr 27 | %1 = call i32 @Add(i32 1, i32 2) 28 | ret i32 %1 29 | } 30 | 31 | define dso_local i32 @Compare(i1, i32, i32) 32 | { 33 | %retValue = alloca i32, i32 1 34 | br i1 %0, label %trueBranch, label %falseBranch 35 | trueBranch: 36 | store i32 %1, i32* %retValue 37 | br label %retBranch 38 | falseBranch: 39 | store i32 %2, i32* %retValue 40 | br label %retBranch 41 | retBranch: 42 | %4 = load i32, i32* %retValue 43 | ret i32 %4 44 | } 45 | -------------------------------------------------------------------------------- /testcase/TestCpp.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | std::size_t HeapCount{}; 4 | 5 | void* operator new(std::size_t size) 6 | { 7 | const auto id = HeapCount++; 8 | void* result; 9 | asm( 10 | "list(APPEND _LLVM_CMAKE_MODIFIED_EXTERNAL_VARIABLE_LIST _AllocatedObject_%1)\n" 11 | "string(REPEAT \";0\" %2 _AllocatedObject_%1)\n" 12 | "set(_AllocatedObject_%1 \"0$%{_AllocatedObject_%1%}\")\n" 13 | "set(%0 _AllocatedObject_%1)\n" 14 | : "=r"(result) 15 | : "r"(id), "r"(size - 1) 16 | : 17 | ); 18 | return result; 19 | } 20 | 21 | void* operator new[](std::size_t size) 22 | { 23 | return ::operator new(size); 24 | } 25 | 26 | void operator delete(void* ptr) noexcept 27 | { 28 | } 29 | 30 | void operator delete[](void* ptr) noexcept 31 | { 32 | } 33 | 34 | extern "C" 35 | { 36 | class CMakeString 37 | { 38 | public: 39 | CMakeString() 40 | { 41 | asm( 42 | "set(%0 \"%1\")" 43 | : "=r"(m_Dummy) 44 | : "r"("") 45 | : 46 | ); 47 | } 48 | 49 | CMakeString(const char* str) 50 | { 51 | asm( 52 | "_LLVM_CMAKE_EXTRACT_REF_LIST(%1 _CMakeString_TmpList)\n" 53 | "list(POP_BACK _CMakeString_TmpList)\n" 54 | "_LLVM_CMAKE_BYTEARRAY_TO_STRING(%0 $%{_CMakeString_TmpList%})\n" 55 | : "=r"(m_Dummy) 56 | : "r"(str) 57 | : 58 | ); 59 | } 60 | 61 | CMakeString operator+(CMakeString const& other) const 62 | { 63 | CMakeString result; 64 | asm( 65 | "set(%0 \"%1%2\")" 66 | : "=r"(result.m_Dummy) 67 | : "r"(m_Dummy), "r"(other.m_Dummy) 68 | : 69 | ); 70 | return result; 71 | } 72 | 73 | private: 74 | char m_Dummy; 75 | }; 76 | 77 | void Print(CMakeString const& str) 78 | { 79 | asm( 80 | "message(\"%0\")\n" 81 | : 82 | : "r"(str) 83 | : 84 | ); 85 | } 86 | 87 | std::size_t strlen(const char* ptr) 88 | { 89 | auto end = ptr; 90 | while (*end++); 91 | return end - ptr - 1; 92 | } 93 | 94 | int* Ptr = nullptr; 95 | int Array[] = { 1, 2, 3, 4 }; 96 | 97 | int Load(int* ptr) 98 | { 99 | return *ptr; 100 | } 101 | 102 | int* GetElem(int idx) 103 | { 104 | return Array + idx; 105 | } 106 | 107 | int LoadOffset(int* ptr, int offset) 108 | { 109 | return ptr[offset]; 110 | } 111 | 112 | int GetOffset(int* from, int* to) 113 | { 114 | return to - from; 115 | } 116 | 117 | struct Pair 118 | { 119 | int A; 120 | int B; 121 | }; 122 | 123 | Pair PairArray[5]{}; 124 | 125 | Pair CreatePair(int a, int b) 126 | { 127 | return { a, b }; 128 | } 129 | 130 | void CopyPair(Pair& dst, Pair const& src) 131 | { 132 | dst = src; 133 | } 134 | 135 | Pair const& GetPair(int i) 136 | { 137 | return PairArray[i]; 138 | } 139 | 140 | int GetA(Pair p) 141 | { 142 | return p.A; 143 | } 144 | 145 | void TestInner2(Pair& r) 146 | { 147 | r.A = 2; 148 | } 149 | 150 | void TestInner1(Pair& r) 151 | { 152 | TestInner2(r); 153 | } 154 | 155 | int TestInner0() 156 | { 157 | auto pair = CreatePair(0, 1); 158 | TestInner1(pair); 159 | return pair.A; 160 | } 161 | 162 | struct BigObj 163 | { 164 | int Payload[100]; 165 | }; 166 | 167 | int GetFirst(BigObj b) 168 | { 169 | return b.Payload[0]; 170 | } 171 | 172 | BigObj CreateBigObj() 173 | { 174 | return {}; 175 | } 176 | 177 | int Func1(int arg) 178 | { 179 | return arg + 1; 180 | } 181 | 182 | int Func2(int arg) 183 | { 184 | return arg + 2; 185 | } 186 | 187 | int (*GetCallback(bool func1))(int) 188 | { 189 | return func1 ? Func1 : Func2; 190 | } 191 | 192 | int InvokeFunc(int (*callback)(int), int arg) 193 | { 194 | return callback(arg); 195 | } 196 | 197 | int AsmTest(int arg1, int arg2) 198 | { 199 | int result1, result2; 200 | asm( 201 | R"( 202 | message(STATUS "arg1 is %2, arg2 is %3") 203 | math(EXPR %0 "%2 + %3") 204 | math(EXPR %1 "%2 - %3") 205 | )" 206 | : "=r"(result1), "=r"(result2) 207 | : "r"(arg1), "r"(arg2) 208 | : 209 | ); 210 | return result1 * result2; 211 | } 212 | 213 | int BranchTest(int cond) 214 | { 215 | if (cond == 1) 216 | { 217 | return 1; 218 | } 219 | else if (cond == 2) 220 | { 221 | return 2; 222 | } 223 | else 224 | { 225 | return 0; 226 | } 227 | } 228 | 229 | int LoopTest(int count) 230 | { 231 | int result{}; 232 | for (auto i = 0; i < count; ++i) 233 | { 234 | if (i > 2) 235 | { 236 | result += count; 237 | } 238 | } 239 | 240 | return result; 241 | } 242 | 243 | int LoopTest2(int count) 244 | { 245 | int result{}; 246 | do 247 | { 248 | result += count; 249 | } while (--count); 250 | return result; 251 | } 252 | 253 | int LoopTest3(int count) 254 | { 255 | int result{}; 256 | while (true) 257 | { 258 | result += count; 259 | if (result > 10) 260 | { 261 | break; 262 | } 263 | result += 1; 264 | } 265 | return result; 266 | } 267 | 268 | int LoopTest4(int count) 269 | { 270 | int result{}; 271 | while (true) 272 | { 273 | result += count; 274 | if (result < 10) 275 | { 276 | continue; 277 | } 278 | else 279 | { 280 | break; 281 | } 282 | result += 1; 283 | } 284 | return result; 285 | } 286 | 287 | int* TestNew() 288 | { 289 | return new int(123); 290 | } 291 | 292 | int* TestNewArray() 293 | { 294 | return new int[2]{ 123, 456 }; 295 | } 296 | 297 | Pair* TestNew2() 298 | { 299 | return new Pair{ 123, 456 }; 300 | } 301 | 302 | void TestCMakeString() 303 | { 304 | CMakeString str = "This is a "; 305 | CMakeString str2 = "CMakeString"; 306 | Print(str + str2); 307 | Print(""); 308 | } 309 | } 310 | -------------------------------------------------------------------------------- /testcase/TestRust.rs: -------------------------------------------------------------------------------- 1 | use std::num::Wrapping; 2 | 3 | pub fn test(a1: i32, a2: i32) -> i32 4 | { 5 | (Wrapping(a1) + Wrapping(a2)).0 6 | } 7 | 8 | pub struct Foo 9 | { 10 | Field: i32 11 | } 12 | 13 | impl Foo 14 | { 15 | pub fn new(field: i32) -> Foo 16 | { 17 | Foo{ Field: field } 18 | } 19 | } 20 | 21 | pub struct Bar 22 | { 23 | } 24 | 25 | impl Bar 26 | { 27 | pub fn new() -> Bar 28 | { 29 | Bar{} 30 | } 31 | } 32 | 33 | pub trait GetValueTrait 34 | { 35 | fn GetValue(&self) -> i32; 36 | 37 | fn ToDyn(&self) -> &dyn GetValueTrait 38 | where Self: std::marker::Sized 39 | { 40 | self 41 | } 42 | } 43 | 44 | impl GetValueTrait for Foo 45 | { 46 | fn GetValue(&self) -> i32 47 | { 48 | self.Field 49 | } 50 | } 51 | 52 | impl GetValueTrait for Bar 53 | { 54 | fn GetValue(&self) -> i32 55 | { 56 | 233 57 | } 58 | } 59 | 60 | pub fn GetValue(o : &dyn GetValueTrait) -> i32 61 | { 62 | o.GetValue() 63 | } 64 | 65 | #[no_mangle] 66 | pub fn rust_mian() -> i32 67 | { 68 | let foo = Foo::new(123); 69 | let bar = Bar::new(); 70 | test(GetValue((&foo).ToDyn()), GetValue((&bar).ToDyn())) 71 | } 72 | -------------------------------------------------------------------------------- /testcase/VirtualTest.cpp: -------------------------------------------------------------------------------- 1 | extern "C" 2 | { 3 | struct VirtualBase 4 | { 5 | virtual ~VirtualBase() = default; 6 | 7 | virtual int Func(int arg) 8 | { 9 | return arg + 1; 10 | } 11 | }; 12 | 13 | struct Derived : VirtualBase 14 | { 15 | int Func(int arg) override 16 | { 17 | return arg + 2; 18 | } 19 | }; 20 | 21 | VirtualBase CreateVirtualBase() 22 | { 23 | return {}; 24 | } 25 | 26 | Derived CreateDerived() 27 | { 28 | return {}; 29 | } 30 | 31 | int InvokeFunc(VirtualBase& obj, int arg) 32 | { 33 | return obj.Func(arg); 34 | } 35 | } 36 | --------------------------------------------------------------------------------