├── C++ ├── .gitignore ├── const_example.cpp ├── deleter_example.cpp ├── expected_example.cpp ├── smart_ptr_example.cpp ├── README.md └── container_example.cpp ├── KaleidoscopeJP ├── src │ ├── chap04 │ │ ├── launch.sh │ │ ├── build.sh │ │ ├── CMakeLists.txt │ │ └── README.md │ ├── chap05 │ │ ├── visualize.sh │ │ ├── callgraph.png │ │ ├── build.sh │ │ ├── CMakeLists.txt │ │ ├── t.ll │ │ └── cfg.baz.dot │ ├── chap02 │ │ ├── build.sh │ │ ├── CMakeLists.txt │ │ └── main.cpp │ ├── chap03 │ │ ├── build.sh │ │ └── CMakeLists.txt │ ├── CMakeLists.txt │ ├── chap06 │ │ ├── build.sh │ │ └── CMakeLists.txt │ ├── chap07 │ │ ├── build.sh │ │ ├── CMakeLists.txt │ │ └── README.md │ ├── shared.cmake │ └── include │ │ └── KaleidoscopeJIT.h ├── README.md ├── llvm-license ├── chap08.md ├── chap01.md ├── chap10.md ├── chap09.md ├── chap03.md ├── chap02.md └── chap04.md ├── va_list ├── .gitignore ├── variable_arguments.cpp └── README.md ├── jit ├── .gitignore ├── README.md ├── jit_mc.cpp └── jit_orc_lljit.cpp ├── README.md ├── .gitignore ├── LICENSE ├── phabricator └── README.md ├── M1 └── README.md └── BuildingAJITJP ├── chap01.md ├── chap04.md ├── chap02.md └── chap03.md /C++/.gitignore: -------------------------------------------------------------------------------- 1 | *_example 2 | *.dSYM -------------------------------------------------------------------------------- /KaleidoscopeJP/src/chap04/launch.sh: -------------------------------------------------------------------------------- 1 | echo "1.0 + 4.0;" | ./a.out -------------------------------------------------------------------------------- /va_list/.gitignore: -------------------------------------------------------------------------------- 1 | variable_arguments 2 | variable_arguments.dSYM -------------------------------------------------------------------------------- /jit/.gitignore: -------------------------------------------------------------------------------- 1 | jit_mc 2 | jit_orc_lljit 3 | jit_mc.dSYM 4 | jit_orc_lljit.dSYM -------------------------------------------------------------------------------- /KaleidoscopeJP/src/chap05/visualize.sh: -------------------------------------------------------------------------------- 1 | opt -dot-cfg ./t.ll 2 | dot -Tpng ./cfg.baz.dot -o ./callgraph.png -------------------------------------------------------------------------------- /KaleidoscopeJP/src/chap02/build.sh: -------------------------------------------------------------------------------- 1 | clang++ -c ./main.cpp -o ./main.o `llvm-config --cxxflags` 2 | clang++ -o ./a.out ./main.o -------------------------------------------------------------------------------- /KaleidoscopeJP/src/chap05/callgraph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sonsongithub/llvm-tutorial/HEAD/KaleidoscopeJP/src/chap05/callgraph.png -------------------------------------------------------------------------------- /KaleidoscopeJP/src/chap03/build.sh: -------------------------------------------------------------------------------- 1 | clang++ -c ./main.cpp -o ./main.o `llvm-config --cxxflags` 2 | clang++ -o ./a.out ./main.o `llvm-config --ldflags --libs --libfiles --system-libs` -------------------------------------------------------------------------------- /KaleidoscopeJP/src/chap05/build.sh: -------------------------------------------------------------------------------- 1 | clang++ -c ./main.cpp -o ./main.o `llvm-config --cxxflags` 2 | clang++ -o ./a.out ./main.o `llvm-config --ldflags --libs --libfiles --system-libs` -------------------------------------------------------------------------------- /KaleidoscopeJP/src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | 3 | project(hoge) 4 | 5 | add_subdirectory(chap02) 6 | add_subdirectory(chap03) 7 | add_subdirectory(chap04) -------------------------------------------------------------------------------- /KaleidoscopeJP/src/chap06/build.sh: -------------------------------------------------------------------------------- 1 | LLVM_CONFIG="" 2 | clang++ -c ./main.cpp -o ./main.o `${LLVM_CONFIG} --cxxflags` 3 | clang++ -o ./a.out ./main.o `${LLVM_CONFIG} --ldflags --libs --libfiles --system-libs` -------------------------------------------------------------------------------- /KaleidoscopeJP/src/chap07/build.sh: -------------------------------------------------------------------------------- 1 | LLVM_CONFIG="" 2 | clang++ -c ./main.cpp -o ./main.o `${LLVM_CONFIG} --cxxflags` 3 | clang++ -o ./a.out ./main.o `${LLVM_CONFIG} --ldflags --libs --libfiles --system-libs` -------------------------------------------------------------------------------- /KaleidoscopeJP/src/chap04/build.sh: -------------------------------------------------------------------------------- 1 | LLVM_CONFIG="/usr/local/Cellar/llvm/10.0.1_1/bin/llvm-config" 2 | clang++ -c ./main.cpp -o ./main.o `${LLVM_CONFIG} --cxxflags` 3 | clang++ -o ./a.out ./main.o `${LLVM_CONFIG} --ldflags --libs --libfiles --system-libs core` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LLVM documents 2 | 3 | 1. [Kaleidoscopeチュートリアル 日本語訳](./KaleidoscopeJP) 4 | 2. [Buildling A JIT 日本語訳](./BuildingAJITJP) 5 | 3. [How to build LLVM on M1](./M1) 6 | 4. [JIT samples](./jit) 7 | 5. [Variable lentgh argments in LLVM API](./va_list) 8 | 6. [How to use Phabricator(日本語)](./phabricator/README.md) -------------------------------------------------------------------------------- /KaleidoscopeJP/src/chap04/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | 3 | include(${CMAKE_CURRENT_SOURCE_DIR}/../shared.cmake) 4 | 5 | SET_LLVM_COMPILE_CONFIG() 6 | SET_LLVM_LINK_CONFIG() 7 | SET_CMAKE_PARAMETER() 8 | 9 | set(CMAKE_CXX_FLAGS ${LLVM_COMPILE_CONFIG}) 10 | set(CMAKE_EXE_LINKER_FLAGS ${LLVM_LINK_CONFIG}) 11 | 12 | add_executable(main main.cpp) 13 | -------------------------------------------------------------------------------- /KaleidoscopeJP/src/chap06/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | 3 | include(${CMAKE_CURRENT_SOURCE_DIR}/../shared.cmake) 4 | 5 | SET_LLVM_COMPILE_CONFIG() 6 | SET_LLVM_LINK_CONFIG() 7 | SET_CMAKE_PARAMETER() 8 | 9 | set(CMAKE_CXX_FLAGS ${LLVM_COMPILE_CONFIG}) 10 | set(CMAKE_EXE_LINKER_FLAGS ${LLVM_LINK_CONFIG}) 11 | 12 | add_executable(chap06 main.cpp) 13 | -------------------------------------------------------------------------------- /KaleidoscopeJP/src/chap07/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | 3 | include(${CMAKE_CURRENT_SOURCE_DIR}/../shared.cmake) 4 | 5 | SET_LLVM_COMPILE_CONFIG() 6 | SET_LLVM_LINK_CONFIG() 7 | SET_CMAKE_PARAMETER() 8 | 9 | set(CMAKE_CXX_FLAGS ${LLVM_COMPILE_CONFIG}) 10 | set(CMAKE_EXE_LINKER_FLAGS ${LLVM_LINK_CONFIG}) 11 | 12 | add_executable(chap07 main.cpp) 13 | -------------------------------------------------------------------------------- /KaleidoscopeJP/src/chap07/README.md: -------------------------------------------------------------------------------- 1 | うーん,クラッシュする. 2 | 3 | ## 再現手順 4 | 5 | ``` 6 | > ./build.sh 7 | warning: unknown warning option '-Wno-class-memaccess'; did you mean 8 | '-Wno-class-varargs'? [-Wunknown-warning-option] 9 | 1 warning generated. 10 | > ./a.out 11 | ready> extern printd(x); 12 | ready> Read extern: 13 | declare double @printd(double) 14 | 15 | ready> printd(1.0); 16 | ready> Illegal instruction: 4 17 | ``` 18 | -------------------------------------------------------------------------------- /KaleidoscopeJP/src/chap02/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | 3 | include(${CMAKE_CURRENT_SOURCE_DIR}/../shared.cmake) 4 | 5 | SET_LLVM_COMPILE_CONFIG() 6 | 7 | set(CMAKE_C_LINK_EXECUTABLE "/usr/local/opt/llvm/bin/clang++") 8 | set(CMAKE_CXX_COMPILER "/usr/local/opt/llvm/bin/clang++") 9 | set(CMAKE_CXX_FLAGS ${LLVM_COMPILE_CONFIG}) 10 | set(CMAKE_CXX_FLAGS "-std=c++14") 11 | 12 | add_executable(chap02 main.cpp) 13 | -------------------------------------------------------------------------------- /KaleidoscopeJP/src/chap03/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | 3 | include(${CMAKE_CURRENT_SOURCE_DIR}/../shared.cmake) 4 | 5 | SET_LLVM_COMPILE_CONFIG() 6 | SET_LLVM_LINK_CONFIG() 7 | 8 | set(CMAKE_C_LINK_EXECUTABLE "/usr/local/opt/llvm/bin/clang++") 9 | set(CMAKE_CXX_COMPILER "/usr/local/opt/llvm/bin/clang++") 10 | set(CMAKE_CXX_FLAGS ${LLVM_COMPILE_CONFIG}) 11 | set(CMAKE_EXE_LINKER_FLAGS ${LLVM_LINK_CONFIG}) 12 | 13 | add_executable(chap03 main.cpp) 14 | -------------------------------------------------------------------------------- /KaleidoscopeJP/src/chap05/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | 3 | include(${CMAKE_CURRENT_SOURCE_DIR}/../shared.cmake) 4 | 5 | SET_LLVM_COMPILE_CONFIG() 6 | SET_LLVM_LINK_CONFIG() 7 | 8 | set(CMAKE_C_LINK_EXECUTABLE "/usr/local/opt/llvm/bin/clang++") 9 | set(CMAKE_CXX_COMPILER "/usr/local/opt/llvm/bin/clang++") 10 | set(CMAKE_CXX_FLAGS ${LLVM_COMPILE_CONFIG}) 11 | set(CMAKE_EXE_LINKER_FLAGS ${LLVM_LINK_CONFIG}) 12 | 13 | add_executable(chap05 main.cpp) 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | *~ 35 | .DS_Store 36 | 37 | # vscode 38 | .vscode 39 | 40 | # macos 41 | *.icloud 42 | *.dSYM -------------------------------------------------------------------------------- /KaleidoscopeJP/src/chap05/t.ll: -------------------------------------------------------------------------------- 1 | declare double @foo() 2 | 3 | declare double @bar() 4 | 5 | define double @baz(double %x) { 6 | entry: 7 | %ifcond = fcmp one double %x, 0.000000e+00 8 | br i1 %ifcond, label %then, label %else 9 | 10 | then: ; preds = %entry 11 | %calltmp = call double @foo() 12 | br label %ifcont 13 | 14 | else: ; preds = %entry 15 | %calltmp1 = call double @bar() 16 | br label %ifcont 17 | 18 | ifcont: ; preds = %else, %then 19 | %iftmp = phi double [ %calltmp, %then ], [ %calltmp1, %else ] 20 | ret double %iftmp 21 | } 22 | -------------------------------------------------------------------------------- /C++/const_example.cpp: -------------------------------------------------------------------------------- 1 | 2 | int main() { 3 | { 4 | int i = 0; 5 | int j = 0; 6 | 7 | // Can change a reference but can not change value. 8 | int const *a = &i; 9 | const int* b = &i; 10 | 11 | // This is OK. 12 | a = &j; 13 | b = &j; 14 | // This is NG. 15 | // *a = 10; 16 | 17 | } 18 | { 19 | int i = 0; 20 | int j = 0; 21 | // Can not change a reference 22 | int* const b = &i; 23 | // This is NG. 24 | // b = &j; 25 | // This is OK. 26 | *b = 10; 27 | } 28 | { 29 | int i = 0; 30 | int j = 0; 31 | // All prohibited. 32 | const int* const b = &i; 33 | // NG 34 | // b = &j; 35 | // *b = 10; 36 | } 37 | 38 | return 0; 39 | } -------------------------------------------------------------------------------- /C++/deleter_example.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | class Hoge { 5 | public: 6 | Hoge(); 7 | ~Hoge(); 8 | Hoge(const Hoge &o); 9 | void print(); 10 | }; 11 | 12 | Hoge::Hoge() { 13 | printf("Init Hoge - %p\n", this); 14 | } 15 | 16 | Hoge::Hoge(const Hoge &o) { 17 | printf("Copy Hoge - %p -> %p\n", &o, this); 18 | } 19 | 20 | Hoge::~Hoge() { 21 | printf("Destruct Hoge - %p\n", this); 22 | } 23 | 24 | void Hoge::print() { 25 | printf("print Hoge - %p\n", this); 26 | } 27 | 28 | void deleter_hoge(Hoge* p) { 29 | std::cout << "Implment, here, the code which must be executed before deleting(or call destructor?) Hoge instance." << std::endl; 30 | std::cout << "deleter_hoge - " << static_cast(p) << std::endl; 31 | delete p; 32 | } 33 | 34 | int main() { 35 | std::shared_ptr p1(new Hoge(), deleter_hoge); 36 | } -------------------------------------------------------------------------------- /KaleidoscopeJP/src/chap05/cfg.baz.dot: -------------------------------------------------------------------------------- 1 | digraph "CFG for 'baz' function" { 2 | label="CFG for 'baz' function"; 3 | 4 | Node0x7f9c31502cd0 [shape=record,label="{entry:\l %ifcond = fcmp one double %x, 0.000000e+00\l br i1 %ifcond, label %then, label %else\l|{T|F}}"]; 5 | Node0x7f9c31502cd0:s0 -> Node0x7f9c31502e50; 6 | Node0x7f9c31502cd0:s1 -> Node0x7f9c31502eb0; 7 | Node0x7f9c31502e50 [shape=record,label="{then: \l %calltmp = call double @foo()\l br label %ifcont\l}"]; 8 | Node0x7f9c31502e50 -> Node0x7f9c315030a0; 9 | Node0x7f9c31502eb0 [shape=record,label="{else: \l %calltmp1 = call double @bar()\l br label %ifcont\l}"]; 10 | Node0x7f9c31502eb0 -> Node0x7f9c315030a0; 11 | Node0x7f9c315030a0 [shape=record,label="{ifcont: \l %iftmp = phi double [ %calltmp, %then ], [ %calltmp1, %else ]\l ret double %iftmp\l}"]; 12 | } 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 sonson 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 | -------------------------------------------------------------------------------- /jit/README.md: -------------------------------------------------------------------------------- 1 | # JIT Sample codes 2 | 3 | ## MCJIT 4 | 5 | MCJIT is the second generation of the JIT compiler of LLVM(MC means "Machine Code?"). 6 | MCJIT is supported on Since LLVM 2.9 to the present version. 7 | It is already recommended to migrate the MCJIT code to ORC. 8 | Old Kaleidoscope source codes were based on MCJIT. 9 | If you want to extract the code to JIT compile from such as sample code(old kaleidoscope), refer this codes. 10 | 11 | [MCJIT sample code](./jit_mc.cpp) 12 | 13 | ## ORC 14 | 15 | ORC means "On Request Compilation". 16 | ORC development has focused on supporting for the concurrent JIT compilation, lazy compiliation and isolation some layers. 17 | 18 | From [4], 19 | > The majority of the ORCv1 layers and utilities were renamed with a ‘Legacy’ prefix in LLVM 8.0, and have deprecation warnings attached in LLVM 9.0. In LLVM 12.0 ORCv1 will be removed entirely. 20 | 21 | If you want to extract the code to JIT compile, refer this codes. 22 | 23 | [ORC sample code](./jit_orc_lljit.cpp) 24 | 25 | ## Preferences 26 | 27 | 1. https://llvm.org/devmtg/2011-11/Grosbach_Anderson_LLVMMC.pdf 28 | 2. https://llvm.org/devmtg/2016-11/Slides/Hames-ORC.pdf 29 | 3. https://llvm.org/docs/MCJITDesignAndImplementation.html 30 | 4. https://llvm.org/docs/ORCv2.html -------------------------------------------------------------------------------- /phabricator/README.md: -------------------------------------------------------------------------------- 1 | 2 | # How to request a review of your contiribution 3 | 4 | ### Install tools 5 | 6 | Install `Arcanist` that is a command line tool for Phabricator. 7 | PHP should be installed for `Arcanist`. 8 | 9 | 1. Clone PHP utility. 10 | 1. `> git clone https://github.com/phacility/libphutil.git` 11 | 2. Clone `Arcanist`. 12 | 1. `> git clone https://github.com/phacility/arcanist.git` 13 | 3. Added a path to `Arcanist` to `$PATH`. 14 | 1. `export PATH="$HOME/WWW/tools/arcanist/bin:$PATH"` 15 | 16 | ### Set up Phabricator 17 | 18 | 1. LLVM uses Phabricator. [https://reviews.llvm.org](https://reviews.llvm.org) 19 | 2. Create an account at https://reviews.llvm.org. 20 | 3. Set up `Arcanist` for LLVM Phabricator. 21 | 1. `> arc install-certificate https://reviews.llvm.org` 22 | 2. Follow the instructions on the `arc`. (Set up your client token.) 23 | 4. Make it LLVM Phabricator default target. 24 | 1. `> arc set-config default https://reviews.llvm.org` 25 | 5. And, use `arc` command. 26 | 27 | ### New request 28 | 29 | ### Update patch during reviewing 30 | 31 | ### References 32 | 33 | 1. [Source](https://llvm.org/docs/Contributing.html#id8) 34 | 2. [Source](https://wiki.freebsd.org/action/show/Phabricator?action=show&redirect=CodeReview) 35 | -------------------------------------------------------------------------------- /KaleidoscopeJP/src/chap04/README.md: -------------------------------------------------------------------------------- 1 | このサンプルもmacOS, Ubuntuでllvm7.0では動作しない. 2 | `printd`を呼び出すタイミングでクラッシュする・・・・・. 3 | 4 | ``` 5 | > sudo apt-get install llvm-6.0 llvm-6.0-examples llvm-6.0-tools clang-6.0 6 | > sudo apt-get install llvm-7 llvm-7-tools llvm-7-examples clang-7 7 | > cp -r /usr/share/doc/llvm-6.0-examples ~/llvm-6.0-examples 8 | > cp -r /usr/share/doc/llvm-7-examples ~/llvm-7-examples 9 | > cd ~/llvm-6.0-examples/examples/Kaleidoscope/include 10 | > gzip -d ./KaleidoscopeJIT.h.gz 11 | > cd ../Chapter4/ 12 | > gzip -d ./toy.cpp.gz 13 | > clang++-6.0 -g -rdynamic toy.cpp `llvm-config-6.0 --cxxflags --ldflags --system-libs --libs core mcjit native` -O3 -o toy 14 | > ./toy 15 | ready> extern printd(X); 16 | ready> Read extern: 17 | declare double @printd(double) 18 | 19 | ready> printd(1.0); 20 | ready> 1.000000 21 | Evaluated to 0.000000 22 | ready> 23 | > cd ~/llvm-7-examples/examples/Kaleidoscope 24 | > cd include/ 25 | > gzip -d ./KaleidoscopeJIT.h.gz 26 | > cd ../Chapter4 27 | > gzip -d ./toy.cpp.gz 28 | > clang++-7 -g -rdynamic toy.cpp `llvm-config-7 --cxxflags --ldflags --system-libs --libs core mcjit native` -O3 -o toy 29 | > ./toy 30 | > ready> extern printd(X); 31 | ready> Read extern: 32 | declare double @printd(double) 33 | 34 | ready> printd(1.0); 35 | ready> Segmentation fault (core dumped) 36 | > 37 | ``` 38 | -------------------------------------------------------------------------------- /C++/expected_example.cpp: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2020 sonson 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 W ARRANTY 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 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "llvm/IR/Verifier.h" 31 | 32 | class Hoge { 33 | public: 34 | Hoge(); 35 | ~Hoge(); 36 | void print(); 37 | }; 38 | 39 | Hoge::Hoge() { 40 | printf("Init Hoge - %p\n", this); 41 | } 42 | 43 | Hoge::~Hoge() { 44 | printf("Destruct Hoge - %p\n", this); 45 | } 46 | 47 | void Hoge::print() { 48 | printf("print Hoge - %p\n", this); 49 | } 50 | 51 | std::error_code sampleErrorCode = std::make_error_code(std::errc::already_connected); 52 | 53 | llvm::Expected create(int input) { 54 | if (input == 0) 55 | return llvm::createStringError(sampleErrorCode, "Something happened."); 56 | return std::move(Hoge()); 57 | } 58 | 59 | int main(int argc, char *argv[]) { 60 | auto p = create(1); 61 | if (auto error = p.takeError()) { 62 | std::cout << "error" << std::endl; 63 | } else { 64 | p->print(); 65 | } 66 | return 0; 67 | } 68 | -------------------------------------------------------------------------------- /C++/smart_ptr_example.cpp: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2021 sonson 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 W ARRANTY 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 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | class Hoge { 31 | public: 32 | Hoge(); 33 | ~Hoge(); 34 | Hoge(const Hoge &o); 35 | void print(); 36 | }; 37 | 38 | Hoge::Hoge() { 39 | printf("Init Hoge - %p\n", this); 40 | } 41 | 42 | Hoge::Hoge(const Hoge &o) { 43 | printf("Copy Hoge - %p -> %p\n", &o, this); 44 | } 45 | 46 | Hoge::~Hoge() { 47 | printf("Destruct Hoge - %p\n", this); 48 | } 49 | 50 | void Hoge::print() { 51 | printf("print Hoge - %p\n", this); 52 | } 53 | 54 | std::vector> local_shared_ptr_vector() { 55 | std::vector> vec = {}; 56 | 57 | vec.push_back(std::make_shared()); 58 | 59 | std::shared_ptr p(new Hoge()); 60 | 61 | vec.push_back(p); 62 | 63 | return vec; 64 | } 65 | 66 | std::shared_ptr hoge(int i) { 67 | auto ptr_vec = local_shared_ptr_vector(); 68 | return ptr_vec[i]; 69 | } 70 | 71 | int main() { 72 | auto p = hoge(0); 73 | p->print(); 74 | return 0; 75 | } 76 | -------------------------------------------------------------------------------- /KaleidoscopeJP/src/shared.cmake: -------------------------------------------------------------------------------- 1 | 2 | find_program(LLVM_CONFIG "llvm-config") 3 | set(LLVM_CONFIG "/Users/sonson/Downloads/llvm-11.0.0.src/build/bin/llvm-config") 4 | 5 | macro(SET_LLVM_COMPILE_CONFIG) 6 | set(LLVM_COMPILE_CONFIG) 7 | if(LLVM_CONFIG) 8 | message(STATUS "Found LLVM_CONFIG as ${LLVM_CONFIG}") 9 | set(CONFIG_COMMAND ${LLVM_CONFIG} 10 | "--cxxflags") 11 | execute_process( 12 | COMMAND ${CONFIG_COMMAND} 13 | RESULT_VARIABLE HAD_ERROR 14 | OUTPUT_VARIABLE LLVM_COMPILE_CONFIG 15 | ) 16 | if(NOT HAD_ERROR) 17 | message(STATUS "bbbbb") 18 | string(REGEX REPLACE "[ \t]*[\r\n]+[ \t]*" " " LLVM_COMPILE_CONFIG ${LLVM_COMPILE_CONFIG}) 19 | else() 20 | string(REPLACE ";" " " CONFIG_COMMAND_STR "${CONFIG_COMMAND}") 21 | message(STATUS "${CONFIG_COMMAND_STR}") 22 | message(FATAL_ERROR "llvm-config failed with status ${HAD_ERROR}") 23 | endif() 24 | else() 25 | message(FATAL_ERROR "llvm-config not found -- ${LLVM_CONFIG}") 26 | endif() 27 | endmacro(SET_LLVM_COMPILE_CONFIG) 28 | 29 | macro(SET_LLVM_LINK_CONFIG) 30 | set(LLVM_LINK_CONFIG) 31 | if(LLVM_CONFIG) 32 | message(STATUS "Found LLVM_CONFIG as ${LLVM_CONFIG}") 33 | set(CONFIG_COMMAND ${LLVM_CONFIG} 34 | "--cxxflags" 35 | "--ldflags" 36 | "--libs" 37 | "--libfiles" 38 | "--system-libs" 39 | "core") 40 | execute_process( 41 | COMMAND ${CONFIG_COMMAND} 42 | RESULT_VARIABLE HAD_ERROR 43 | OUTPUT_VARIABLE LLVM_LINK_CONFIG 44 | ) 45 | if(NOT HAD_ERROR) 46 | string(REGEX REPLACE "\n" " " LLVM_LINK_CONFIG ${LLVM_LINK_CONFIG}) 47 | else() 48 | string(REPLACE ";" " " CONFIG_COMMAND_STR "${CONFIG_COMMAND}") 49 | message(STATUS "${CONFIG_COMMAND_STR}") 50 | message(FATAL_ERROR "llvm-config failed with status ${HAD_ERROR}") 51 | endif() 52 | else() 53 | message(FATAL_ERROR "llvm-config not found -- ${LLVM_CONFIG}") 54 | endif() 55 | endmacro(SET_LLVM_LINK_CONFIG) 56 | 57 | macro(SET_CMAKE_PARAMETER) 58 | set(CMAKE_C_LINK_EXECUTABLE "/usr/bin/clang++") 59 | set(CMAKE_CXX_COMPILER "/usr/bin/clang++") 60 | endmacro(SET_CMAKE_PARAMETER) -------------------------------------------------------------------------------- /KaleidoscopeJP/README.md: -------------------------------------------------------------------------------- 1 | ## llvm-tutorial 2 | LLVM Tutorialを勉強するリポジトリ 3 | 4 | ## How to compile samples 5 | 6 | KaleidoscopeのChapter04以降で必要になるdynamic linkのコードが一部ビルドで正しく動きません. 7 | 今の所,llvmのgithubミラーの`release_60`ブランチだと正しく動作するようです. 8 | brewなどのパッケージでインストールされたllvmの場合,うまく動作しないので,githubから`release_60`ブランチのソースをチェックアウトし,コンパイルして使ってください.コンパイラは,macOSの場合,デフォルトのclang++を使えばよいようです. 9 | 10 | ### LLVMのビルド 11 | 12 | ``` 13 | git clone https://github.com/llvm-mirror/llvm.git 14 | cd llvm 15 | git checkout -b release_60 origin/release_60 16 | mkdir build 17 | cd build 18 | CC=gcc CXX=g++ \ 19 | cmake -DCMAKE_INSTALL_PREFIX=/usr \ 20 | -DLLVM_ENABLE_FFI=ON \ 21 | -DCMAKE_BUILD_TYPE=Release \ 22 | -DLLVM_BUILD_LLVM_DYLIB=ON \ 23 | -DLLVM_LINK_LLVM_DYLIB=ON \ 24 | -DLLVM_TARGETS_TO_BUILD="host;" \ 25 | -DLLVM_BUILD_TESTS=ON \ 26 | -Wno-dev -G Ninja .. && 27 | ninja 28 | ``` 29 | 30 | ### LLVMのソース中のサンプルのビルドと動作確認 31 | 32 | ``` 33 | cd ./examples/Kaleidoscope/Chapter4/ 34 | g++ ./toy.cpp `../../../build/bin/llvm-config --cxxflags --ldflags --libs --libfiles --system-libs` 35 | echo "extern printd(x);printd(1.0);" | ./a.out 36 | ``` 37 | 38 | 正しくビルドできていれば,上記コードは,以下のような結果を出す. 39 | 40 | ``` 41 | ready> ready> Read extern: 42 | declare double @printd(double) 43 | 44 | ready> ready> 1.000000 45 | Evaluated to 0.000000 46 | ready> ready> > Chapter4 sonson$ 47 | ``` 48 | 49 | ## Table of contents 50 | 1. [Chapter 01](https://github.com/sonsongithub/llvm-tutorial/blob/master/chap01.md) 51 | 2. [Chapter 02](https://github.com/sonsongithub/llvm-tutorial/blob/master/chap02.md) 52 | 3. [Chapter 03](https://github.com/sonsongithub/llvm-tutorial/blob/master/chap03.md) 53 | 3. [Chapter 04](https://github.com/sonsongithub/llvm-tutorial/blob/master/chap04.md) 54 | 3. [Chapter 05](https://github.com/sonsongithub/llvm-tutorial/blob/master/chap05.md) 55 | 3. [Chapter 06](https://github.com/sonsongithub/llvm-tutorial/blob/master/chap06.md) 56 | 3. [Chapter 07](https://github.com/sonsongithub/llvm-tutorial/blob/master/chap07.md) 57 | 3. [Chapter 08](https://github.com/sonsongithub/llvm-tutorial/blob/master/chap08.md) 58 | 3. [Chapter 09](https://github.com/sonsongithub/llvm-tutorial/blob/master/chap09.md) 59 | 3. [Chapter 10](https://github.com/sonsongithub/llvm-tutorial/blob/master/chap10.md) 60 | 61 | ## License 62 | このリポジトリは,LLVMのソースコード,ドキュメントをベースに作成し,[LLVM Release License](http://releases.llvm.org/7.0.0/LICENSE.TXT)に従い,コンテンツを作成しています. 63 | -------------------------------------------------------------------------------- /C++/README.md: -------------------------------------------------------------------------------- 1 | # C++復習?勉強? 2 | 3 | * This document is my memo, so it is in Japanese, and English. 4 | * This folder is especially tivial... because the author is studying C++..... 5 | * (このフォルダは,著者がC++の勉強のためにあるので,特にレベルが低いです.あまりに参考になさらぬよう.) 6 | 7 | # llvm::Expected 8 | 9 | `llvm::Expected`は,オブジェクトとエラーをラップするテンプレート.生成に失敗した時はエラーを返し,そうでないときは,オブジェクトを返す・・みたいなことが実装できる. 10 | 11 | ``` 12 | class Hoge { 13 | public: 14 | Hoge(); 15 | ~Hoge(); 16 | void print(); 17 | }; 18 | 19 | Hoge::Hoge() { 20 | printf("Init Hoge - %p\n", this); 21 | } 22 | 23 | Hoge::~Hoge() { 24 | printf("Destruct Hoge - %p\n", this); 25 | } 26 | 27 | void Hoge::print() { 28 | printf("print Hoge - %p\n", this); 29 | } 30 | 31 | std::error_code sampleErrorCode = std::make_error_code(std::errc::already_connected); 32 | 33 | llvm::Expected create(int input) { 34 | if (input == 0) 35 | return llvm::createStringError(sampleErrorCode, "Something happened."); 36 | return std::move(Hoge()); 37 | } 38 | 39 | int main(int argc, char *argv[]) { 40 | auto p = create(1); 41 | if (auto error = p.takeError()) { 42 | std::cout << "error" << std::endl; 43 | } else { 44 | p->print(); 45 | } 46 | return 0; 47 | } 48 | ``` 49 | 50 | [source](./expected_example.cpp) 51 | 52 | # const 53 | 54 | ``` 55 | int main() { 56 | { 57 | int i = 0; 58 | int j = 0; 59 | 60 | // Can change a reference but can not change value. 61 | int const *a = &i; 62 | const int* b = &i; 63 | 64 | // This is OK. 65 | a = &j; 66 | b = &j; 67 | // This is NG. 68 | // *a = 10; 69 | 70 | } 71 | { 72 | int i = 0; 73 | int j = 0; 74 | // Can not change a reference, but can update value at the reference. 75 | int* const b = &i; 76 | // This is NG. 77 | // b = &j; 78 | // This is OK. 79 | *b = 10; 80 | } 81 | { 82 | int i = 0; 83 | int j = 0; 84 | // All prohibited. 85 | const int* const b = &i; 86 | // NG 87 | // b = &j; 88 | // *b = 10; 89 | } 90 | 91 | return 0; 92 | } 93 | ``` 94 | 95 | [source](./const_example.cpp) 96 | 97 | # shared_ptr 98 | 99 | 100 | 101 | # Deleter 102 | 103 | `shared_ptr`で自動削除される前に実行したい処理がある場合,`deleter`を`shared_ptr`のコンストラクタのときに引き渡せる.`deleter`の引数で渡されるポインタは最後に`delete`する必要がある. 104 | 105 | ``` 106 | void deleter_hoge(Hoge* p) { 107 | std::cout << "Implment, here, the code which must be executed before deleting(or call destructor?) Hoge instance." << std::endl; 108 | std::cout << "deleter_hoge - " << static_cast(p) << std::endl; 109 | delete p; 110 | } 111 | 112 | int main() { 113 | std::shared_ptr p1(new Hoge(), deleter_hoge); 114 | } 115 | ``` 116 | 117 | [source](./deleter_example.cpp) 118 | 119 | # 参考文献 120 | 121 | [1] https://proc-cpuinfo.fixstars.com/2016/03/c-html/ 122 | [2] https://yohhoy.hatenablog.jp/entry/2012/12/15/120839 123 | [3] https://rinatz.github.io/cpp-book/ch07-08-assignment-operator/ -------------------------------------------------------------------------------- /C++/container_example.cpp: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2021 sonson 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 W ARRANTY 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 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | class Storage { 31 | // std::shared_ptr buffer; 32 | public: 33 | Storage(); 34 | ~Storage(); 35 | Storage(const Storage &o); 36 | }; 37 | 38 | Storage::Storage() { 39 | // std::shared_ptr p(new char[1024]); 40 | // buffer = p; 41 | std::cout << "Init Storage - " << std::endl; 42 | } 43 | 44 | Storage::~Storage() { 45 | std::cout << "Destruct Storage - " << std::endl; 46 | } 47 | 48 | class Hoge { 49 | std::shared_ptr storage; 50 | public: 51 | Hoge(); 52 | ~Hoge(); 53 | Hoge(const Hoge &o); 54 | Hoge(const Hoge &&o); 55 | void print(); 56 | }; 57 | 58 | Hoge::Hoge() { 59 | std::shared_ptr p(new Storage); 60 | storage = p; 61 | printf("Init Hoge - %p\n", this); 62 | } 63 | 64 | Hoge::Hoge(const Hoge &o) { 65 | std::shared_ptr p(new Storage); 66 | storage = p; 67 | printf("Copy Hoge - %p -> %p\n", &o, this); 68 | } 69 | 70 | Hoge::Hoge(const Hoge &&o) { 71 | storage = o.storage; 72 | printf("Move Hoge - %p -> %p\n", &o, this); 73 | } 74 | 75 | Hoge::~Hoge() { 76 | printf("Destruct Hoge - %p\n", this); 77 | } 78 | 79 | void Hoge::print() { 80 | printf("print Hoge - %p\n", this); 81 | } 82 | 83 | std::vector create_vector() { 84 | std::vector vec = {}; 85 | std::cout << "push_back" << std::endl; 86 | vec.push_back(Hoge()); 87 | std::cout << "push_back" << std::endl; 88 | vec.push_back(Hoge()); 89 | std::cout << "push_back" << std::endl; 90 | vec.push_back(Hoge()); 91 | 92 | std::cout << "Before return" << std::endl; 93 | return vec; 94 | } 95 | 96 | void local_create_vector() { 97 | std::vector vec = {}; 98 | vec.push_back(Hoge()); 99 | } 100 | 101 | int main() { 102 | // std::cout << "local_create_vector" << std::endl; 103 | // local_create_vector(); 104 | std::cout << "local_create_vector" << std::endl; 105 | auto vec = create_vector(); 106 | return 0; 107 | } 108 | -------------------------------------------------------------------------------- /KaleidoscopeJP/llvm-license: -------------------------------------------------------------------------------- 1 | ============================================================================== 2 | LLVM Release License 3 | ============================================================================== 4 | University of Illinois/NCSA 5 | Open Source License 6 | 7 | Copyright (c) 2003-2018 University of Illinois at Urbana-Champaign. 8 | All rights reserved. 9 | 10 | Developed by: 11 | 12 | LLVM Team 13 | 14 | University of Illinois at Urbana-Champaign 15 | 16 | http://llvm.org 17 | 18 | Permission is hereby granted, free of charge, to any person obtaining a copy of 19 | this software and associated documentation files (the "Software"), to deal with 20 | the Software without restriction, including without limitation the rights to 21 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 22 | of the Software, and to permit persons to whom the Software is furnished to do 23 | so, subject to the following conditions: 24 | 25 | * Redistributions of source code must retain the above copyright notice, 26 | this list of conditions and the following disclaimers. 27 | 28 | * Redistributions in binary form must reproduce the above copyright notice, 29 | this list of conditions and the following disclaimers in the 30 | documentation and/or other materials provided with the distribution. 31 | 32 | * Neither the names of the LLVM Team, University of Illinois at 33 | Urbana-Champaign, nor the names of its contributors may be used to 34 | endorse or promote products derived from this Software without specific 35 | prior written permission. 36 | 37 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 38 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 39 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 40 | CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 41 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 42 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE 43 | SOFTWARE. 44 | 45 | ============================================================================== 46 | Copyrights and Licenses for Third Party Software Distributed with LLVM: 47 | ============================================================================== 48 | The LLVM software contains code written by third parties. Such software will 49 | have its own individual LICENSE.TXT file in the directory in which it appears. 50 | This file will describe the copyrights, license, and restrictions which apply 51 | to that code. 52 | 53 | The disclaimer of warranty in the University of Illinois Open Source License 54 | applies to all code in the LLVM Distribution, and nothing in any of the 55 | other licenses gives permission to use the names of the LLVM Team or the 56 | University of Illinois to endorse or promote products derived from this 57 | Software. 58 | 59 | The following pieces of software have additional or alternate copyrights, 60 | licenses, and/or restrictions: 61 | 62 | Program Directory 63 | ------- --------- 64 | Google Test llvm/utils/unittest/googletest 65 | OpenBSD regex llvm/lib/Support/{reg*, COPYRIGHT.regex} 66 | pyyaml tests llvm/test/YAMLParser/{*.data, LICENSE.TXT} 67 | ARM contributions llvm/lib/Target/ARM/LICENSE.TXT 68 | md5 contributions llvm/lib/Support/MD5.cpp llvm/include/llvm/Support/MD5.h -------------------------------------------------------------------------------- /M1/README.md: -------------------------------------------------------------------------------- 1 | # Build LLVM on Apple Silicon(M1) 2 | 3 | ## How bo build 4 | 5 | ### Build cmake 6 | 7 | I recommend that you install `cmake` via [cmake.org](https://cmake.org). 8 | You can use `cmake` with adding the path to `PATH`. 9 | 10 | スクリーンショット 2021-11-07 13 06 29 11 | 12 | ### Build LLVM 13 | 14 | At first, check your Terminal.app without using Rosetta. 15 | 16 | image 17 | 18 | To avoid using libxml2, edit `CMakeLists.txt` as follows, 19 | 20 | ``` 21 | set(LLVM_ENABLE_LIBXML2 "OFF" CACHE STRING "Use libxml2 if available. Can be ON, OFF, or FORCE_ON") 22 | ``` 23 | 24 | Without this modification, linker can not build llvm because it can not find and link libxml. 25 | I do not use libxml in my codes. If you want to use libxml in LLVM, I can not help you. 26 | 27 | Next, prepare and build any files using cmake. My MacBookPro M1 crashed when I ran the `make -j` command because the build process used up all the memory. It is recommended that you specify the number of processes with the `j` option. 28 | 29 | ``` 30 | mkdir build 31 | cd build 32 | cmake .. $LLVM_SRC_DIR -DCMAKE_BUILD_TYPE=Release -DLLVM_TARGETS_TO_BUILD="ARM;X86;AArch64" 33 | make -j 4 34 | ``` 35 | 36 | ## Build sources 37 | 38 | I do not copy any llvm files into `/usr/`, because I want to use multiple versions of LLVM switching between them. 39 | 40 | For example, we can build source codes using LLVM as follows, 41 | 42 | ``` 43 | /usr/bin/clang++ add.cpp -o add `/build/bin/llvm-config --cxxflags --ldflags --libs --libfiles core orcjit mcjit native --system-libs` 44 | ``` 45 | 46 | ## Compile speed 47 | 48 | I compared the compile speed between Apple Silicon M1 and Intel Core i9, using `add.cpp`. 49 | 50 | * Apple Silicon(M1) 1.93[sec] 51 | * Intel(Core i9) 4.6[sec] 52 | 53 | As a result, M1 was 2.4 times faster than Core i9. 54 | 55 | ## Visual Studio Code 56 | 57 | Visual studio terminal runs on i386 at default. Therefore, I enforcely compile the files for ARM using the following settings. 58 | 59 | ``` 60 | { 61 | "tasks": [ 62 | { 63 | "type": "shell", 64 | "label": "clang++ build active file", 65 | "command": "arch", 66 | "args": [ 67 | "-arm64", 68 | "/usr/bin/clang++", 69 | "-g", 70 | "-O0", 71 | "${fileDirname}/${fileBasename}", 72 | "-o", 73 | "${fileDirname}/${fileBasenameNoExtension}", 74 | "`/Users/sonson/Downloads/llvm-11.0.0.src/build/bin/llvm-config", "--cppflags`", 75 | "-std=c++17", 76 | "`/Users/sonson/Downloads/llvm-11.0.0.src/build/bin/llvm-config", "--ldflags", "--libs", "--libfiles", "core","orcjit","mcjit", "native", "--system-libs`" 77 | ] 78 | } 79 | ], 80 | "version": "2.0.0" 81 | } 82 | ``` 83 | 84 | ## Debugging in Visual studio code. 85 | 86 | * Use CodeLLDB and following `launch.json`. 87 | 88 | ``` 89 | { 90 | // Use IntelliSense to learn about possible attributes. 91 | // Hover to view descriptions of existing attributes. 92 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 93 | "version": "0.2.0", 94 | "configurations": [ 95 | { 96 | "name": "clang++", 97 | "type": "lldb", 98 | "request": "launch", 99 | // "targetArchitecture": "arm64", 100 | "program": "${fileDirname}/${fileBasenameNoExtension}", 101 | "args": [], 102 | // "stopAtEntry": false, 103 | "cwd": "${workspaceFolder}", 104 | // "environment": [], 105 | // "externalConsole": false, 106 | // "MIMode": "lldb", 107 | "preLaunchTask": "clang++ build active file" 108 | } 109 | ] 110 | } 111 | ``` 112 | -------------------------------------------------------------------------------- /KaleidoscopeJP/chap08.md: -------------------------------------------------------------------------------- 1 | # オブジェクトコードにコンパイルする 2 | ## はじめに 3 | 8章にようこそ. 4 | 本章では,自分で作った言語をオブジェクトファイルにコンパイルする方法について説明する. 5 | ## ターゲットの選択 6 | LLVMは,クロスコンパイルをサポートする. 7 | 今使っているマシンのアーキテクチャ用にコンパイルできるし,他のアーキテクチャのために簡単にコンパイルすることもできる. 8 | このチュートリアルでは,現在使っているマシンをターゲットにする. 9 | ターゲットにしたいアーキテクチャを指定するためには,“target triple”と呼ばれる文字列を使います. 10 | これは,`---`のような形式をとります(詳しくはクロスコンパイルのドキュメントを読んでください). 11 | 例えば,以下のコマンドで,clangが保持する,現在のターゲットの“target triple”をチェックできます. 12 | 13 | ``` 14 | $ clang --version | grep Target 15 | Target: x86_64-unknown-linux-gnu 16 | ``` 17 | 18 | このコマンドを実行して表示される内容は,使っているマシンや,アーキテクチャ,OSなどによって異なります. 19 | 幸運にも,今使っているマシンの"target triple"をハードコードする必要はない. 20 | LLVMは,今使っているマシンの"target triple"を返す,`sys::getDefaultTargetTriple`を提供する. 21 | 22 | ``` 23 | auto TargetTriple = sys::getDefaultTargetTriple(); 24 | ``` 25 | 26 | LLVMは,ターゲットの機能性のすべてをリンクすることを要求しない. 27 | 例えば,もし,JITを使っているのであれば,アセンブリを表示する必要はない. 28 | 同じように,もし,あるアーキテクチャのみをターゲットとしているなら,それらのアーキテクチャのための機能だけをリンクできる. 29 | 30 | 例えば,以下のようなコードでオブジェクトコードを発行するために,すべてのターゲットを初期化する. 31 | 32 | ``` 33 | InitializeAllTargetInfos(); 34 | InitializeAllTargets(); 35 | InitializeAllTargetMCs(); 36 | InitializeAllAsmParsers(); 37 | InitializeAllAsmPrinters(); 38 | ``` 39 | 40 | `Target`オブジェクトを取得するために,"target triple"を使う. 41 | 42 | ``` 43 | std::string Error; 44 | auto Target = TargetRegistry::lookupTarget(TargetTriple, Error); 45 | 46 | // Print an error and exit if we couldn't find the requested target. 47 | // This generally occurs if we've forgotten to initialise the 48 | // TargetRegistry or we have a bogus target triple. 49 | if (!Target) { 50 | errs() << Error; 51 | return 1; 52 | } 53 | ``` 54 | 55 | ## `TargetMachine` 56 | 次に`TargetMachine`クラスが必要である. 57 | このクラスは,現在ターゲットとしているマシンの完全な説明を提供してくれる. 58 | もし,SSEのような特定の機能や,IntelのSandylakeのような特定のCPUをターゲットとしたい場合,このクラスを使う. 59 | 60 | LLVMが知っている機能やCPUを知るために,`llc`コマンドを使える. 61 | 62 | ``` 63 | $ llvm-as < /dev/null | llc -march=x86 -mattr=help 64 | Available CPUs for this target: 65 | 66 | amdfam10 - Select the amdfam10 processor. 67 | athlon - Select the athlon processor. 68 | athlon-4 - Select the athlon-4 processor. 69 | ... 70 | 71 | Available features for this target: 72 | 73 | 74 | 16bit-mode - 16-bit mode (i8086). 75 | 32bit-mode - 32-bit mode (80386). 76 | 3dnow - Enable 3DNow! instructions. 77 | 3dnowa - Enable 3DNow! Athlon instructions. 78 | ... 79 | ``` 80 | 81 | 今のサンプルでは,追加機能やオプション,"relocation model(なにこれ)"はなしで,一般的なCPUを使うことにする. 82 | 83 | ``` 84 | auto CPU = "generic"; 85 | auto Features = ""; 86 | 87 | TargetOptions opt; 88 | auto RM = Optional(); 89 | auto TargetMachine = Target->createTargetMachine(TargetTriple, CPU, Features, opt, RM); 90 | ``` 91 | 92 | ## `Module`の初期化 93 | これで,ターゲットやデータレイアウトを指定するためにモジュールを設定する準備が整った. 94 | これは,厳密には必要はないが,フロントエンドのパフォーマンスガイドとしては,これをすることを勧める. 95 | 最適化は,ターゲットやデータレイアウトに関する知識から利益を得る. 96 | 97 | ``` 98 | TheModule->setDataLayout(TargetMachine->createDataLayout()); 99 | TheModule->setTargetTriple(TargetTriple); 100 | ``` 101 | 102 | ## オブジェクトコードの発行 103 | さぁ,オブジェクトコードを発行しよう. 104 | まず,書き出したいファイルの場所を定義する. 105 | 106 | ``` 107 | auto Filename = "output.o"; 108 | std::error_code EC; 109 | raw_fd_ostream dest(Filename, EC, sys::fs::F_None); 110 | 111 | if (EC) { 112 | errs() << "Could not open file: " << EC.message(); 113 | return 1; 114 | } 115 | ``` 116 | 117 | 最終的に,オブジェクトコードを発行するパスを定義する. 118 | そして,そのパスを実行する. 119 | 120 | ``` 121 | legacy::PassManager pass; 122 | auto FileType = TargetMachine::CGFT_ObjectFile; 123 | 124 | if (TargetMachine->addPassesToEmitFile(pass, dest, FileType)) { 125 | errs() << "TargetMachine can't emit a file of this type"; 126 | return 1; 127 | } 128 | 129 | pass.run(*TheModule); 130 | dest.flush(); 131 | ``` 132 | 133 | ## 全部一緒に 134 | 動作しただろうか. 135 | 試してみよう. 136 | 自分のコードをコンパイルする必要があるが,ここまでの章のコンパイルと`llvm-config`の引数が多少違うことに注意してほしい. 137 | 138 | ``` 139 | $ clang++ -g -O3 toy.cpp `llvm-config --cxxflags --ldflags --system-libs --libs all` -o toy 140 | ``` 141 | 142 | さぁ,動作させてみて,単純な平均を計算する関数を実装してみよう. 143 | コーディングが終わったときにCtrl-Dでプログラムを終了させると,オブジェクトファイルが出力される. 144 | 145 | ``` 146 | $ ./toy 147 | ready> def average(x y) (x + y) * 0.5; 148 | ^D 149 | Wrote output.o 150 | ``` 151 | 152 | これで,オブジェクトファイルができた. 153 | テストしてみよう. 154 | C++でこのオブジェクトファイルをリンクし,それをコールするコードを書いてみよう. 155 | 例えば,以下のようなコードになる. 156 | 157 | ``` 158 | #include 159 | 160 | extern "C" { 161 | double average(double, double); 162 | } 163 | 164 | int main() { 165 | std::cout << "average of 3.0 and 4.0: " << average(3.0, 4.0) << std::endl; 166 | } 167 | ``` 168 | 169 | 以下のようにして,コンパイル,リンクし,実行してみよう. 170 | 171 | ``` 172 | $ clang++ main.cpp output.o -o main 173 | $ ./main 174 | average of 3.0 and 4.0: 3.5 175 | ``` 176 | -------------------------------------------------------------------------------- /jit/jit_mc.cpp: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2020 sonson 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 W ARRANTY 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 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "llvm/IR/IRBuilder.h" 30 | #include "llvm/IR/Verifier.h" 31 | #include "llvm/Support/TargetSelect.h" 32 | #include "llvm/ExecutionEngine/MCJIT.h" 33 | 34 | // Context for LLVM 35 | static llvm::LLVMContext context; 36 | 37 | // ; ModuleID = 'originalModule' 38 | // source_filename = "originalModule" 39 | // 40 | // define double @originalFunction(double %a, double %b) { 41 | // entry: 42 | // %addtmp = fadd double %a, %b 43 | // ret double %addtmp 44 | // } 45 | 46 | int main() { 47 | using llvm::Module; 48 | using llvm::IRBuilder; 49 | using llvm::Function; 50 | using llvm::FunctionType; 51 | using llvm::Type; 52 | using llvm::Value; 53 | using llvm::BasicBlock; 54 | using llvm::ExecutionEngine; 55 | using llvm::EngineBuilder; 56 | 57 | llvm::InitializeNativeTarget(); 58 | llvm::InitializeNativeTargetAsmPrinter(); 59 | 60 | // Create a new module 61 | std::unique_ptr module(new Module("originalModule", context)); 62 | 63 | // LLVM IR builder 64 | static IRBuilder<> builder(context); 65 | 66 | // define function 67 | // argument name list 68 | auto functionName = "originalFunction"; 69 | std::vector argNames{"a", "b"}; 70 | // argument type list 71 | std::vector Doubles(2, Type::getDoubleTy(context)); 72 | // create function type 73 | FunctionType *functionType = FunctionType::get(Type::getDoubleTy(context), Doubles, false); 74 | // create function in the module. 75 | Function *function = Function::Create(functionType, Function::ExternalLinkage, functionName, module.get()); 76 | 77 | // Set names for all arguments. 78 | // I'd like to use "zip" function, here..... 79 | unsigned idx = 0; 80 | for (auto &arg : function->args()) { 81 | arg.setName(argNames[idx++]); 82 | } 83 | 84 | // Create argument table for LLVM::Value type. 85 | static std::map name2VariableMap; 86 | for (auto &arg : function->args()) { 87 | name2VariableMap[arg.getName().str()] = &arg; 88 | } 89 | 90 | // Create a new basic block to start insertion into. 91 | BasicBlock *basicBlock = BasicBlock::Create(context, "entry", function); 92 | builder.SetInsertPoint(basicBlock); 93 | 94 | // calculate "add" 95 | auto result = builder.CreateFAdd(name2VariableMap["a"], name2VariableMap["b"], "addtmp"); 96 | 97 | // set return 98 | builder.CreateRet(result); 99 | 100 | // varify LLVM IR 101 | if (verifyFunction(*function)) { 102 | std::cout << ": Error constructing function!\n" << std::endl; 103 | return 1; 104 | } 105 | 106 | // confirm LLVM IR 107 | module->print(llvm::outs(), nullptr); 108 | 109 | // confirm the current module status 110 | if (verifyModule(*module)) { 111 | std::cout << ": Error module!\n" << std::endl; 112 | return 1; 113 | } 114 | 115 | // Builder JIT 116 | std::string errStr; 117 | ExecutionEngine *engineBuilder = EngineBuilder(std::move(module)) 118 | .setEngineKind(llvm::EngineKind::JIT) 119 | .setErrorStr(&errStr) 120 | .create(); 121 | if (!engineBuilder) { 122 | std::cout << "error: " << errStr << std::endl; 123 | return 1; 124 | } 125 | 126 | // Get pointer to a function which is built by EngineBuilder. 127 | auto f = reinterpret_cast( 128 | engineBuilder->getFunctionAddress(function->getName().str())); 129 | if (f == NULL) { 130 | std::cout << "error" << std::endl; 131 | return 1; 132 | } 133 | 134 | // Execution 135 | // a + b 136 | std::cout << f(1.0, 2.0) << std::endl; 137 | std::cout << f(2.0, 2.0) << std::endl; 138 | 139 | return 0; 140 | } 141 | -------------------------------------------------------------------------------- /jit/jit_orc_lljit.cpp: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2020 sonson 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 W ARRANTY 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 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "llvm/IR/BasicBlock.h" 31 | #include "llvm/IR/Function.h" 32 | #include "llvm/IR/LLVMContext.h" 33 | #include "llvm/IR/Module.h" 34 | #include "llvm/IR/IRBuilder.h" 35 | #include "llvm/IR/Verifier.h" 36 | #include "llvm/Support/TargetSelect.h" 37 | #include "llvm/ExecutionEngine/Orc/LLJIT.h" 38 | #include "llvm/Support/InitLLVM.h" 39 | 40 | int main(int argc, char *argv[]) { 41 | using llvm::Type; 42 | using llvm::Function; 43 | using llvm::BasicBlock; 44 | using llvm::FunctionType; 45 | using llvm::Value; 46 | 47 | llvm::InitLLVM X(argc, argv); 48 | llvm::InitializeNativeTarget(); 49 | llvm::InitializeNativeTargetAsmPrinter(); 50 | llvm::InitializeNativeTargetAsmParser(); 51 | 52 | // create context 53 | auto context = std::make_unique(); 54 | 55 | // Create a new module 56 | std::unique_ptr module(new llvm::Module("originalModule", *context)); 57 | 58 | // LLVM IR builder 59 | static llvm::IRBuilder<> builder(*context); 60 | 61 | // define function 62 | // argument name list 63 | auto functionName = "originalFunction"; 64 | std::vector argNames{"a", "b"}; 65 | // argument type list 66 | std::vector Doubles(2, Type::getDoubleTy(*context)); 67 | // create function type 68 | FunctionType *functionType = FunctionType::get(Type::getDoubleTy(*context), Doubles, false); 69 | // create function in the module. 70 | Function *function = Function::Create(functionType, Function::ExternalLinkage, functionName, module.get()); 71 | 72 | // Set names for all arguments. 73 | // I'd like to use "zip" function, here..... 74 | unsigned idx = 0; 75 | for (auto &arg : function->args()) { 76 | arg.setName(argNames[idx++]); 77 | } 78 | 79 | // Create argument table for LLVM::Value type. 80 | static std::map name2VariableMap; 81 | for (auto &arg : function->args()) { 82 | name2VariableMap[arg.getName().str()] = &arg; 83 | } 84 | 85 | // Create a new basic block to start insertion into. 86 | BasicBlock *basicBlock = BasicBlock::Create(*context, "entry", function); 87 | builder.SetInsertPoint(basicBlock); 88 | 89 | // calculate "add" 90 | auto result = builder.CreateFAdd(name2VariableMap["a"], name2VariableMap["b"], "addtmp"); 91 | 92 | // set return 93 | builder.CreateRet(result); 94 | 95 | llvm::ExitOnError("Error constructing function!", verifyFunction(*function)); 96 | 97 | // confirm LLVM IR, text mode. 98 | module->print(llvm::outs(), nullptr); 99 | 100 | llvm::ExitOnError("Error module!", verifyModule(*module)); 101 | 102 | // Try to detect the host arch and construct an LLJIT instance. 103 | auto jit = llvm::orc::LLJITBuilder().create(); 104 | 105 | if (jit) { 106 | auto thread_safe_module = llvm::orc::ThreadSafeModule(std::move(module), std::move(context)); 107 | auto error = jit->get()->addIRModule(std::move(thread_safe_module)); 108 | assert(!error && "LLJIT can not add handle module."); 109 | llvm::orc::JITDylib &dylib = jit->get()->getMainJITDylib(); 110 | const llvm::DataLayout &dataLayout = jit->get()->getDataLayout(); 111 | auto prefix = dataLayout.getGlobalPrefix(); 112 | auto generator = llvm::cantFail(llvm::orc::DynamicLibrarySearchGenerator::GetForCurrentProcess(prefix)); 113 | dylib.addGenerator(std::move(generator)); 114 | auto symbol = jit->get()->lookup("originalFunction"); 115 | auto f = reinterpret_cast(symbol->getAddress()); 116 | std::cout << "Evaluated to " << f(10, 11) << std::endl; 117 | } else { 118 | std::cout << "Error - LLJIT can not be initialized." << std::endl; 119 | } 120 | return 0; 121 | } -------------------------------------------------------------------------------- /KaleidoscopeJP/chap01.md: -------------------------------------------------------------------------------- 1 | 2 | # Tutorial 3 | "LLVMで言語を実装する"のチュートリアルへようこそ. 4 | このチュートリアルは,シンプルな言語の実装を通じて,それがいかに簡単で楽しいものかを伝えることを目的にする. 5 | このチュートリアルは,LLVMを始めるだけでなく,他の言語へ拡張できるフレームワークを構築する手助けにもなる. 6 | このチュートリアルで出てくるコードは,その他のLLVMの特定の何かをハックするための砂場としても使えるはずだ. 7 | 8 | このチュートリアルの最終ゴールは,これから作る言語を,常にどうやって構築していくかを説明しながら,だんだんと明らかにしていくことである. 9 | これは,幅広い言語設計について知識やLLVMの特定の使い方をカバーし,それらを実現する手法を見せて,説明していく. 10 | ただし,困惑するくらいたくさんある詳細については,無視する. 11 | 12 | このチュートリアルは,本当にコンパイラ技術やLLVMを具体的に教えるもので,最近の,健全なソフトウェアエンジニアリングの原理原則について教えるものではない. 13 | 実践的に,これは,説明を簡単にするために,たくさんのショートカットを使うことを意味する. 14 | 例えば,コードがグローバル変数を使う,ビジターパターンのような良いデザインパターンを使わない・・・などなど・・しかし,コードはシンプルでる. 15 | もし,将来のプロジェクトのために基本として,コードを深くいじったり,使うなら,これらの欠陥を治すのは,そんなに難しいことではないだろう. 16 | 17 | このチュートリアルでは,すでに知っていることや,あまり興味がない章は,簡単にスキップできるようにしたつもりだ. 18 | 19 | チュートリアルの終わりまでに書き上げるコードは,コメントや空行を含めないで1000行に満たないものになる. 20 | この小規模のコードで,字句解析,パーサ,抽象構文器,コード生成,JITコンパイラを含めた合理的なコンパイラを作り上げる. 21 | ほかのシステムは,おもしろい"hello world"チュートリアルをやる一方で,このチュートリアルの幅広さは,LLVMの長所のよい証拠であり,もし,あなたが言語やコンパイラ設計で興味にあるなら,そうであることをあなたはじっくり考えるべきであると,私は考える. 22 | 23 | このチュートアルについて,特に言いたいことは,我々は,あなたが,この言語を拡張し,自身の手でそれで遊ぶようになることだ. 24 | 25 | コードを手に取り,それをクラックしまくってほしいーコンパイラを怖がる必要はないー言語で遊ぶことはすごく楽しい遊びになるはずだ. 26 | 27 | ## 基本言語 28 | 29 | このチュートリアルは,我々が"Kaleidoscope"(美しい形や見かけという意味から)と呼んでいるおもちゃの言語で構成される. 30 | Kaleidoscopeは,関数定義,条件分岐,数学関数が使える手続き型言語である. 31 | チュートリアルの終わりまでに,Kaleidoscopeは,`if/then/else`構造,`for`ループ,ユーザ定義演算子,JITコンパイラ,JITコンパイルのためのコマンドラインツールをサポートするように拡張されていく. 32 | 33 | 物事をシンプルにするために,Kaleidoscopeでは,データ型として,64bitの浮動小数点型のみをサポートするようにする(C言語でいうところの`double`型). 34 | 35 | すべての値は暗黙に倍精度であり,言語は型宣言を必要としない. 36 | これは,言語の文法を非常に簡単にする. 37 | 例えば,フィボナッチ数列を計算するコードは,以下のようになる. 38 | 39 | ``` 40 | # x番目のフィボナッチ数列を計算するコード 41 | def fib(x) 42 | if x < 3 then 43 | 1 44 | else 45 | fib(x-1)+fib(x-2) 46 | 47 | # この表現で,40番目のフィボナッチ数列を得る 48 | fib(40) 49 | ``` 50 | 51 | さらに,Kaleidoscopeでstandard libraryの関数を呼べるようにする(LLVM JITがこれを簡単にする). 52 | これは,`extern`キーワードを使って,今までに使ったことのある関数を定義できることを意味する(これはまた,相互に再帰関数を実装するのにも役立つ). 53 | 例えば,以下のようなコードを書ける. 54 | 55 | ``` 56 | extern sin(arg); 57 | extern cos(arg); 58 | extern atan2(arg1 arg2); 59 | 60 | atan2(sin(.4), cos(42)) 61 | ``` 62 | 63 | さらにおもしろい例は,任意のレベルのマンデルブロー集合を表示するKaleidoscopeアプリケーションを書く6章にある. 64 | 65 | さぁ,この言語の実装をはじめよう. 66 | 67 | ## 字句解析器 68 | 言語の実装を始める.はじめに,テキストファイルを処理し,テキストが何を言ってるのかを認識する能力を持たせる必要がある. 69 | 伝統的な方法は,入力をトークンに分割する"字句解析器"を使うことだ. 70 | 字句解析器が返すそれぞれのトークンは,トークンコードと,潜在的なメタデータ(例えば,数字であれば数値)を含む. 71 | はじめに,その一覧を下記に示す. 72 | 73 | ``` 74 | // 字句解析器は,トークンのコードとして,[0-255]を返す. 75 | enum Token { 76 | tok_eof = -1, 77 | 78 | // コマンド 79 | tok_def = -2, 80 | tok_extern = -3, 81 | 82 | // 主表現 83 | tok_identifier = -4, 84 | tok_number = -5, 85 | }; 86 | ``` 87 | 88 | ``` 89 | // トークンがtok_identifierなら,この変数に値を入れる 90 | static std::string IdentifierStr; 91 | // トークンがtok_numberなら,この変数に値を入れる 92 | static double NumVal; 93 | ``` 94 | 95 | 字句解析器によって返されるトークンは,`Token`の列挙型のひとつか,`+`のような"未知"の文字コード(ASCIIコードの値)となる. 96 | 現在のトークンが何らかの識別名である場合,グローバル変数の`IdentifierStr`に,その識別名が保存される. 97 | もし,現在のトークンが,数字リテラルである場合は,グローバル変数の`NumVal`にその値を保存する. 98 | ここで,注意してほしいのは,グローバル変数を使っているのは,簡単のためであって,それが実際に言語を実装するにあたってベストな選択ではないことだ. 99 | 100 | 字句解析器の実際の実装は,`gettok`という一つの関数でなされる. 101 | `gettok`関数は,標準入力から得た次のトークンを返すために呼ばれる. 102 | その実装は,以下のように始まる. 103 | 104 | ``` 105 | // gettok - 標準入力から次のトークンを返す 106 | static int gettok() { 107 | static int LastChar = ' '; 108 | 109 | // 空白はスキップされる 110 | while (isspace(LastChar)) 111 | LastChar = getchar(); 112 | ``` 113 | 114 | `gettok`は,C言語の`getchar()`を呼び出し,一文字ずつ標準入力から読み取る. 115 | それは,読み出した文字を認識した後,最後の文字を読み取り保存するが,処理はしない. 116 | その最後の文字は,`LastChar`に保存される. 117 | 上のコードで最初に実装されているのは,トークン間の空白が無視することで,これは,ループの最初に実行される. 118 | 119 | 次に,`gettok`がやるべきことは,`def`のような特定のキーワードを識別,認識することである. 120 | Kaleidoscopeは,以下のシンプルなループでそれを実行する. 121 | 122 | ``` 123 | if (isalpha(LastChar)) { // [a-zA-Z][a-zA-Z0-9]*の正規表現を識別する 124 | IdentifierStr = LastChar; 125 | while (isalnum((LastChar = getchar()))) 126 | IdentifierStr += LastChar; 127 | 128 | if (IdentifierStr == "def") 129 | return tok_def; 130 | if (IdentifierStr == "extern") 131 | return tok_extern; 132 | return tok_identifier; 133 | } 134 | ``` 135 | 136 | このコードは,識別子をパースする時は常に,`IdentifierStr`をグローバル変数として取り扱うことに注意する. 137 | また,予約語が同じループでマッチするため,ここでは,予約語をインラインで取り扱う. 138 | 数値も同様に, 139 | 140 | ``` 141 | if (isdigit(LastChar) || LastChar == '.') { // Number: [0-9.]+ 142 | std::string NumStr; 143 | do { 144 | NumStr += LastChar; 145 | LastChar = getchar(); 146 | } while (isdigit(LastChar) || LastChar == '.'); 147 | 148 | NumVal = strtod(NumStr.c_str(), 0); 149 | return tok_number; 150 | } 151 | ``` 152 | 153 | のようにして取り扱う. 154 | 155 | これは,入力を扱うための,かなり愚直なコードだ. 156 | 入力から数値をよみとるとき,我々は,C言語の`strtod`関数を使い,その結果を数値に変換し,`NumVal`に保存する. 157 | これは,十分なエラーチェックを実行しないため,`1.23.45.67`のような文字列を間違って読み込んでしまい,それを`1.23`の数値として扱ってしまう. 158 | 是非それは,改善したいもらいたい. 159 | 次にコメントの取り扱いである. 160 | 161 | ``` 162 | if (LastChar == '#') { 163 | // コメントは,改行の位置まで. 164 | do 165 | LastChar = getchar(); 166 | while (LastChar != EOF && LastChar != '\n' && LastChar != '\r'); 167 | 168 | if (LastChar != EOF) 169 | return gettok(); 170 | } 171 | ``` 172 | 173 | コメントは,その行の終わりまでスキップすることで処理し,次のトークンを返す. 174 | 最終的に,もし入力が上のケースのどれにも該当しない場合,トークンは,`+`のような文字やファイル終端に該当する. 175 | それらは,以下のように取り扱われる. 176 | 177 | ``` 178 | // ファイル終端(EOF)をチェックする.EOFは,処理しない. 179 | if (LastChar == EOF) 180 | return tok_eof; 181 | // 一方,EOFでない場合は,ASCIIの値をそのまま文字として返す. 182 | int ThisChar = LastChar; 183 | LastChar = getchar(); 184 | return ThisChar; 185 | } 186 | ``` 187 | 188 | このコードで,基本的なKaleidoscope言語のためのlexer(lexerのコード全部は,チュートリルの次章で紹介する)が完成した. 189 | 次に,これを使い,抽象構文木を構築するパーサを作る. 190 | パーサができると,lexerとパーサを一緒に使うためのドライバを作っていくことになる. 191 | -------------------------------------------------------------------------------- /KaleidoscopeJP/src/include/KaleidoscopeJIT.h: -------------------------------------------------------------------------------- 1 | //===- KaleidoscopeJIT.h - A simple JIT for Kaleidoscope --------*- C++ -*-===// 2 | // 3 | // The LLVM Compiler Infrastructure 4 | // 5 | // This file is distributed under the University of Illinois Open Source 6 | // License. See LICENSE.TXT for details. 7 | // 8 | //===----------------------------------------------------------------------===// 9 | // 10 | // Contains a simple JIT definition for use in the kaleidoscope tutorials. 11 | // 12 | //===----------------------------------------------------------------------===// 13 | 14 | #ifndef LLVM_EXECUTIONENGINE_ORC_KALEIDOSCOPEJIT_H 15 | #define LLVM_EXECUTIONENGINE_ORC_KALEIDOSCOPEJIT_H 16 | 17 | #include "llvm/ADT/iterator_range.h" 18 | #include "llvm/ADT/STLExtras.h" 19 | #include "llvm/ExecutionEngine/ExecutionEngine.h" 20 | #include "llvm/ExecutionEngine/JITSymbol.h" 21 | #include "llvm/ExecutionEngine/RTDyldMemoryManager.h" 22 | #include "llvm/ExecutionEngine/SectionMemoryManager.h" 23 | #include "llvm/ExecutionEngine/Orc/CompileUtils.h" 24 | #include "llvm/ExecutionEngine/Orc/IRCompileLayer.h" 25 | #include "llvm/ExecutionEngine/Orc/LambdaResolver.h" 26 | #include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h" 27 | #include "llvm/IR/DataLayout.h" 28 | #include "llvm/IR/Mangler.h" 29 | #include "llvm/Support/DynamicLibrary.h" 30 | #include "llvm/Support/raw_ostream.h" 31 | #include "llvm/Target/TargetMachine.h" 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | namespace llvm { 38 | namespace orc { 39 | 40 | class KaleidoscopeJIT { 41 | public: 42 | using ObjLayerT = RTDyldObjectLinkingLayer; 43 | using CompileLayerT = IRCompileLayer; 44 | using ModuleHandleT = CompileLayerT::ModuleHandleT; 45 | 46 | KaleidoscopeJIT() 47 | : TM(EngineBuilder().selectTarget()), DL(TM->createDataLayout()), 48 | ObjectLayer([]() { return std::make_shared(); }), 49 | CompileLayer(ObjectLayer, SimpleCompiler(*TM)) { 50 | llvm::sys::DynamicLibrary::LoadLibraryPermanently(nullptr); 51 | } 52 | 53 | TargetMachine &getTargetMachine() { return *TM; } 54 | 55 | ModuleHandleT addModule(std::unique_ptr M) { 56 | // We need a memory manager to allocate memory and resolve symbols for this 57 | // new module. Create one that resolves symbols by looking back into the 58 | // JIT. 59 | auto Resolver = createLambdaResolver( 60 | [&](const std::string &Name) { 61 | if (auto Sym = findMangledSymbol(Name)) 62 | return Sym; 63 | return JITSymbol(nullptr); 64 | }, 65 | [](const std::string &S) { return nullptr; }); 66 | auto H = cantFail(CompileLayer.addModule(std::move(M), 67 | std::move(Resolver))); 68 | 69 | ModuleHandles.push_back(H); 70 | return H; 71 | } 72 | 73 | void removeModule(ModuleHandleT H) { 74 | ModuleHandles.erase(find(ModuleHandles, H)); 75 | cantFail(CompileLayer.removeModule(H)); 76 | } 77 | 78 | JITSymbol findSymbol(const std::string Name) { 79 | return findMangledSymbol(mangle(Name)); 80 | } 81 | 82 | private: 83 | std::string mangle(const std::string &Name) { 84 | std::string MangledName; 85 | { 86 | raw_string_ostream MangledNameStream(MangledName); 87 | Mangler::getNameWithPrefix(MangledNameStream, Name, DL); 88 | } 89 | return MangledName; 90 | } 91 | 92 | JITSymbol findMangledSymbol(const std::string &Name) { 93 | #ifdef LLVM_ON_WIN32 94 | // The symbol lookup of ObjectLinkingLayer uses the SymbolRef::SF_Exported 95 | // flag to decide whether a symbol will be visible or not, when we call 96 | // IRCompileLayer::findSymbolIn with ExportedSymbolsOnly set to true. 97 | // 98 | // But for Windows COFF objects, this flag is currently never set. 99 | // For a potential solution see: https://reviews.llvm.org/rL258665 100 | // For now, we allow non-exported symbols on Windows as a workaround. 101 | const bool ExportedSymbolsOnly = false; 102 | #else 103 | const bool ExportedSymbolsOnly = true; 104 | #endif 105 | 106 | // Search modules in reverse order: from last added to first added. 107 | // This is the opposite of the usual search order for dlsym, but makes more 108 | // sense in a REPL where we want to bind to the newest available definition. 109 | for (auto H : make_range(ModuleHandles.rbegin(), ModuleHandles.rend())) 110 | if (auto Sym = CompileLayer.findSymbolIn(H, Name, ExportedSymbolsOnly)) 111 | return Sym; 112 | 113 | // If we can't find the symbol in the JIT, try looking in the host process. 114 | if (auto SymAddr = RTDyldMemoryManager::getSymbolAddressInProcess(Name)) 115 | return JITSymbol(SymAddr, JITSymbolFlags::Exported); 116 | 117 | #ifdef LLVM_ON_WIN32 118 | // For Windows retry without "_" at beginning, as RTDyldMemoryManager uses 119 | // GetProcAddress and standard libraries like msvcrt.dll use names 120 | // with and without "_" (for example "_itoa" but "sin"). 121 | if (Name.length() > 2 && Name[0] == '_') 122 | if (auto SymAddr = 123 | RTDyldMemoryManager::getSymbolAddressInProcess(Name.substr(1))) 124 | return JITSymbol(SymAddr, JITSymbolFlags::Exported); 125 | #endif 126 | 127 | return nullptr; 128 | } 129 | 130 | std::unique_ptr TM; 131 | const DataLayout DL; 132 | ObjLayerT ObjectLayer; 133 | CompileLayerT CompileLayer; 134 | std::vector ModuleHandles; 135 | }; 136 | 137 | } // end namespace orc 138 | } // end namespace llvm 139 | 140 | #endif // LLVM_EXECUTIONENGINE_ORC_KALEIDOSCOPEJIT_H 141 | -------------------------------------------------------------------------------- /KaleidoscopeJP/chap10.md: -------------------------------------------------------------------------------- 1 | # まとめ,その他のちょっとしたこと 2 | ## チュートリアルの結論 3 | LLVMで言語を実装するチュートリアルの最終章にようこそ. 4 | このチュートリアルのコースでは,使いようのないおもちゃであった小さなKaleidoscopeという言語を,多少はおもしろい言語に(しかし,まだ使えないけど)まで育ててきました. 5 | 6 | ここがどれくらい遠いのか,ここまででどれくらいコードが必要だったかを考えるのはおもしろいことです. 7 | 我々は,lexer,パーサ,抽象構文木,コード生成器,インタラクティブな処理のためのループを開発し,また,スタンドアローンで動作するデバッグ情報を発行するようにしたりしてきた. 8 | これらのコードは,1000行足らずのものでした. 9 | 10 | 我々の小さな言語は,いくつかのおもしろい機能をサポートします. 11 | その機能とは,ユーザ定義の二項あるいは単項演算子,即時評価されるJITコンパイラ,SSA構造で動作する制御フローなどです. 12 | 13 | このチュートリアルのアイデアは,言語を定義し,構築し,それで遊ぶことがいかに簡単であるかを示すためのものです. 14 | コンパイラを作ることために,恐ろしい,あるいは意味不明なプロセスは必要ありません. 15 | 今,あなたは,基本をほぼ理解したはずです. 16 | 私は,Kaleidoscopeのコードをハックしていくことをお勧めします. 17 | 例えば,以下のような機能を追加してみていかがでしょうか. 18 | 19 | ### グローバル変数 20 | グローバル変数は,現代のソフトウェアエンジニアリングにおいては,議論の対象となるものではあるが,Kaleidoscopeのコンパイラそれ自身のように.ちょっとしたハックを手短に済ませたいときには便利なものである. 21 | 幸運にも,現在のコードは,グローバル変数を追加するのは簡単だ. 22 | つまり,解決できない変数があった場合,それを拒絶する前に,グローバル変数のシンボルテーブルをチェックするようにすればよいのである. 23 | 新しいグローバル変数を作るためには,LLVMの`GlobalVariable`クラスのインスタンスを作ればよい. 24 | 25 | ### 型付変数 26 | Kaleidoscopeは,今の所,double型の変数のみをサポートしている. 27 | これは,言語をとても美しくします. 28 | なぜなら,一つの型のみをサポートするということは,型を指定する必要がないということだからです. 29 | 他の言語は,難しい方法で,これを取り扱います. 30 | 最も簡単な方法は,それぞれの変数定義に型を明示させるようにすることです. 31 | そして,シンボルテーブルで変数の型も`Value*`と一緒に保存するようにすることです. 32 | 33 | ### 配列,構造体,ベクトルなどのデータ構造 34 | 一度,複数の型を追加すると,いくつかの,そして,多くのおもしろい方法で,型システムを拡張することができるようになります. 35 | 単純な配列はとても簡単ですが,多くのアプリケーションにとって,当然とても便利なものです. 36 | それらを追加するのは,ほとんどの場合,LLVMの`getelementptr`命令がどうやって動作しているのかを学ぶいい練習になります. 37 | つまり,それは,便利で,ちょっと風変わりなもので,`getelementptr`命令自体がFAQの対象となります. 38 | 39 | ### 標準的なランタイム 40 | 現状の言語は,ユーザに任意の外部関数にアクセスすることを許容します. 41 | 我々は,その特徴を`printd`や`putchard`のような形で使ってきました. 42 | よりハイレベルな言語構成に拡張しようとするときに,より低レベルな命令を呼び出すならば,この構成は非常に理にかなったものと言えます. 43 | 例えば,もし,言語にハッシュテーブルを追加するなら,インラインで処理を書くのではなく,ランタイムに処理を追加する方がおそらく理にかなっています. 44 | 45 | ### メモリ管理 46 | 現状では,Kaleidoscope上のスタックにのみアクセスできます. 47 | それは,ヒープメモリを確保するのに便利ですが,`malloc/free`のようなインタフェースを呼び出したり,ガーベッジコレクタを使って,確保することもできます. 48 | もし,ガーベッジコレクションを使ってみたいのであれば,LLVMは,オブジェクトの移動,スタックを`scan/update`する必要があるアルゴリズムを含む,"Accurate Garbage Collection"をサポートすることを覚えておいてください. 49 | 50 | ### 例外 51 | LLVMは,他の言語で,コンパイルされたコードに,まったくコストをかけることなく割り込みをかける例外の生成をサポートします. 52 | 暗黙に各々の関数にエラーを返させたり,それをチェックすることによって,コードを生成することもできます. 53 | また,`setjmp/longjmp`を明示的に使うこともできます. 54 | そうするために,色々な種類の方法があるということです. 55 | 56 | ### オブジェクト指向,ジェネリクス,データベースへのアクセス,虚数,幾何・・・・ 57 | 本当に,際限がないくらい,たくさんの機能を追加することができます. 58 | 59 | ### 特殊な分野に特化させる 60 | ここまでは,特定の言語のためのコンパイラを作る,という多くの人が興味を持つエリアに,LLVMを応用することについて話してきました. 61 | しかし,典型的ではない,特殊なコンパイラ技術を使う分野も他にたくさんあります. 62 | 例えば,LLVMは,OpenGLのグラフィックスのアクセラレーションを実装するのに使われたり,C++のコードをActionScriptに変換したりなど,色々な賢いアイデアを実現するために使われています. 63 | 正規表現のインタプリタをネイティブコードに,LLVMを使ってJITコンパイルするのは,あなたになるかもしれません. 64 | 65 | ### 楽しもう! 66 | めちゃくちゃなことや,普通じゃないことに挑戦しましょう. 67 | みんなが使っていたりする言語を作るのは,ちょっとおかしい言語を作ったり,普通とは違うことをしたり,それがどう出てきたかを理解したりすることに比べるといささか退屈です. 68 | もし,そういったことに行き詰まっていたり,何か話したいことがあるなら,llvm-devのメーリングリストに気軽にメールするとよいでしょう. 69 | つまり,そのメーリングリストは,言語に興味のある人がたくさん参加しており,さらに,彼らは,よろこんで,手助けをしてくれます. 70 | 71 | このチュートリアルを終える前に,LLVM IRを生成するための,いくつかの"テク"について話したいと思います. 72 | これらは,わかりきったことではないものの,割と微妙なものです. 73 | しかし,LLVMの能力を利用したい人にとっては,非常に使いやすいものです. 74 | 75 | ## LLVM IRの性質 76 | ここで,LLVM IR形式で書かれたコードについて,2,3のありふれた質問をします. 77 | さあ,用意はいいですか? 78 | 79 | ### ターゲットからの独立 80 | Kaleidoscopeは,"移植しやすい言語"の例でした. 81 | つまり,Kaleidoscopeで書かれたプログラムは,他のターゲット上であっても,同じ方法で動作させることができます. 82 | lisp, java, haskell, js, pythonなどの多くの他の言語は,この性質を備えています(これらの言語は移植しやすいのですが,そのライブラリが・・・そうではないのです・・・・). 83 | 84 | LLVMのよい一面として,IR上ではターゲットの独立性が維持されることです. 85 | つまり,LLVMがサポートするターゲットであれば,Kaleidoscopeをコンパイルされたコードを動作させることができます. 86 | 他には,Cのコードを生成したり,LLVMが現状サポートしないターゲット上で動作するコードを実行させられます. 87 | Cのコードを生成し,LLVMが現状サポートしないターゲットにコンパイルすることさえできます. 88 | 89 | Kaleidoscopeのコンパイラがターゲットに依存しないコードを生成することを簡単に理解できるはずです. 90 | なぜなら,コードが生成されるときにターゲットの情報を聞かれなかったからです. 91 | 92 | 事実,LLVMは,多くの人が楽しくなるような,コンパクトで,ターゲットに非依存な表現を提供します. 93 | 不幸にも,こういった人々は,言語の移植性について考える時に,C言語やC言語の派生言語についていつも考えているようです. 94 | 95 | 私が"不幸にも"といったのは,Cのコードを,配布する以外に,本当に移植性を高くする方法が存在しないためです. 96 | もちろん,Cのソースコードは,実際には一般的に言って移植性がありません.例えば,32bitから64bitへ,本当に古いアプリケーションを移植できるでしょうか. 97 | 98 | C言語の問題は(繰り返しますが,これは一般的にも言えることです),ターゲット固有の仮定でいっぱいになっているからです. 99 | ひとつシンプルな例を挙げると,プリプロセッサは,それが入力されたテキストを処理するときに,しばしば,コードから,破壊的にターゲットからの独立性を取り去ってしまいます. 100 | 101 | ``` 102 | #ifdef __i386__ 103 | int X = 1; 104 | #else 105 | int X = 42; 106 | #endif 107 | ``` 108 | 109 | このような問題は,より複雑な解決方法をエンジニアに引き起こす一方で,ソースコードのままアプリケーションを出荷するよりも良い,十分に一般的な方法では,解決できません. 110 | 111 | つまり,移植が容易ではないC言語のおもしろいサブセットがあります. 112 | もし,プリミティブな型のサイズを固定にしたい場合,既に存在するバイナリとのABIとの互換性を気にせず,そして,いくつかのマイナーな機能は喜んで諦めるべきです.そうすれば,コードの移植性を高めることができます. 113 | これは,カーネル用の言語のような特殊なドメインにおいても,成立します. 114 | 115 | ### 安全性の保証 116 | 上で述べた多くの言語もまた,"安全"な言語です. 117 | つまり,メモリのアドレス空間を破壊したり,プロセスをクラッシュさせるようなJavaで書かれたプログラムを作ることはできません(JVMにバグないと仮定して). 118 | 安全性は,言語設計,ランタイムサポート,そしてOSのサポートを組み合わせる必要があるすおもしろい性質です. 119 | 120 | それは,LLVMで書かれた安全な言語を実装することは,あきらか可能です. 121 | しかし,LLVM IRは,それ自身に安全性を保証していません. 122 | LLVM IRは,危険性のあるポインターのキャストを許そうしますし,バグもありますし,バッファオーバーランも,その他,色々な問題を内包しています. 123 | 安全性は,LLVMの上位のレイヤーとして,実装されるべきで,都合のいいことに,いくつかの開発者グループで,この課題についての調査が始まっています. 124 | これについて,もっと詳細を知りたい場合は,llvm-devのメーリングリストに尋ねてみてください. 125 | 126 | ### 言語固有の最適化 127 | LLVMが多くの人をがっかりさせてしまうひとつの理由に,あるシステムにある問題の全てを解決してくれないという点にあります(すみません,"世界の飢餓"のような問題は,いつか,あなたが解決していかねばならないでしょう). 128 | ある特定の不満は,人々がLLVMが高レベル言語を最適化できないと受けてめていることにあります. 129 | つまり,LLVMは,"多くの情報を失っている"と. 130 | 131 | 不幸にも,この文章の目的は,クリス ラートナーが書いた"コンパイラ設計論"のすべてを解説することではありませんが,代わりに,いくつかの意見を述べたいと思います. 132 | 133 | はじめに,LLVMが情報を失っているという考えは正しいです. 134 | 例えば,この文章にもあったように,(デバッグ情報というより)あるLLVMの表現が,C言語の`int`から来たSSAの値なのか,C言語のILP32マシン上での`long`からきたものなのかを,識別する方法はありません. 135 | これらの両方が,コンパイル時に`i32`の値へキャストされ,本来持っていた情報は失われます. 136 | ここでの,もっと一般的な問題は,LLVMの型システムが,"名前の同一性"の代わりに,"構造的な等価性"を使っていることです. 137 | 言い換えると,高レベル言語では,異なる二つの型として扱われる変数が,LLVMでは,同じ構造を持つということに驚かされます(例えば,二つの違う構造体が一つの`int`フィールドを持っているときなど). 138 | つまり,これらの型は一つのLLVMの型にコンパイル時にキャストされ,もはや,それがどこから来たものなのか問い合わせる手段がなくなってしまいます. 139 | 140 | 二点目は,LLVMは情報を失っているにも関わらず,LLVMは,ターゲットが固定されません. 141 | 我々は,多くのいろんな方法で,LLVMを改善したり,拡張したりしつづけます. 142 | 追加されていく多くの新しい機能に加えて,我々は,IRが最適化に必要かつ重要な情報を捉えられるように拡張していきます(例えば,引数が符号が付いているか,ゼロであるか,ポインタのエイリアスについての情報など). 143 | 拡張の多くは,ユーザが推し進めるものです. 144 | つまり,人々は,LLVMがいくつかの特定の機能をサポートしてほしいと望んでいるので,彼らがそれを推し進め,拡張することになるのです. 145 | 146 | 三点目は,LLVMを使えば,言語固有の最適化を追加するのが可能かつ簡単であるということです. 147 | そして,それをやるために多くの選択肢があります. 148 | ちょっとした例として,その言語のためにコンパイルされたコードについて"知っている"ことを,言語特定の最適化Passを追加するのが簡単です. 149 | 言語のためにコンパイルされたコードについてのことを知っている言語固有の最適化パスを追加するのが簡単です. 150 | C言語の場合,一般的なC言語のライブラリについて知っている最適化Passがあります. 151 | `main()`の中で`exit(0)`をコールする場合,C言語,`exit`関数が行うことを明記しているため,その最適化Passは,そのコードを`return 0;`に最適化しても安全です. 152 | 153 | シンプルなライブラリに関する知識に加えて,LLVM IRへ他の言語特有の情報を埋め込むこともできます. 154 | もし,あなたは,何か特定の必要性,やらないといけないことがあり,壁にぶつかっているなら,そのトピックをllvm-devのメーリングリストに投げかけてください. 155 | 最悪の場合,常にLLVMをクソコード生成器として扱い,それをカバーするために,言語固有の抽象構文木上で,フロントエンドで高レベルの最適化を実装することになってしまいます. 156 | 157 | ## Tips 158 | 初めて見て,すぐにわかるわけではないLLVMを使い仕事し始めると,色々なtipsを知り始めます. 159 | 各員にそれらを再発見させるのではなく,本節では,これらの課題について説明したいと思います. 160 | 161 | ### 移植性の高い`offsetof/sizeof`の実装 162 | ひとつおもしろいことは,もし,あなたが,あなたのコンパイラによって生成されたコードがターゲット非依存になるようにしようとするなら,LLVMの型のデータサイズや,構造体やクラスのフィールドのオフセットのサイズを知る必要があります. 163 | 例えば,メモリを確保する関数に型のサイズを渡す必要があるかもしれません. 164 | 165 | 不幸にも,これは,複数のターゲットに広くまたがった話です. 166 | つまり,例えば,ポインタの幅は,ささいなことですがターゲットに固有なものです. 167 | しかし,これを移植するときにも計算できるようにする`getelementptr`命令を使う賢い方法があります. 168 | 169 | ### スタックフレームのガーベッジコレクション 170 | いくつかの言語は,明確にそれ自身のスタックフレームを管理しようとし,このため,しばしば,スタックフレームがガーベッジコレクトされたり,クロージャの実装が簡単になるようにしようとします. 171 | 明示的なスタックフレームよりも,これらの機能を実装した方法がよいことがありますが,もし,あなたがスタックフレームを実装したい場合,LLVMはそれをサポートします. 172 | スタックフレームは,フロントエンドにコードを継続渡しスタイル(Continuation Passing Style)に変換すること,末尾呼び出しの使用を要求する. -------------------------------------------------------------------------------- /BuildingAJITJP/chap01.md: -------------------------------------------------------------------------------- 1 | [オリジナル](https://llvm.org/docs/tutorial/BuildingAJIT1.html) 2 | 3 | # Introduction 4 | 5 | 注意: このチュートリアルは,最近のORCのアップデートに追いついていません.1〜2章までの文章は更新されていますが,3〜5章のコードは,コンパイル・実行できますが,更新されていません. 6 | 7 | LLVMでORCベースのJITを作ろうにようこそ. 8 | このチュートリアルは,LLVMのOn-Request-Compilation (ORC) APIを使って,JITコンパイラを作ることを目的にしています. 9 | LLVMチュートリアルの言語の実装に使われている`KaleidoscopeJIT`クラスの簡単なバージョンから初めて,並列コンパイル,最適化,遅延コンパイル,遠隔実行などの新しい機能を紹介します. 10 | 11 | このチュートリアルの目標は,ORC JIT APIを紹介し,LLVMの他の機能とこれらのAPIのinteractのやり方や,読者のユースケースにマッチしたカスタムJITを構築するために,それらをどうやって結合すればいいのかを説明します. 12 | 13 | チュートリアルの構成は,以下のようになります. 14 | 15 | * 1章:簡単な`KaleidoscopeJIT`クラスの解説.ここでは,ORCレイヤーの考え方を含む,ORC JIT APIの基本的な概念を紹介します. 16 | * 2章:LLVM IRを最適化したり,コードを生成する新しいレイヤーを基本の`KaleidoscopeJIT`クラスに追加します. 17 | * 3章:IRを遅延コンパイルするためのCompile-On-Demandレイヤーを追加します. 18 | * 4章:関数が呼ばれるまで,IRの生成を遅延させるために,ORCのコンパイルコールバックAPIを直接使うカスタムレイヤーで,Compile-On-Demandレイヤーを置き換えることによって,自作のJITの遅延を実現します. 19 | * 5章:JITリモートAPIを使って,限定された特権でリモートプロセスに,コードをJITコンパイルすることで,"process isolation"を追加します. 20 | 21 | 別ドキュメントの7章の“Implementing a language in LLVM tutorial”から,Kaleidoscope REPLをちょっと改造したバージョンを使うことになります. 22 | 23 | 最終的に,APIの世代で言うと,ORCは,LLVMのJIT APIの第三世代になります. 24 | ORCは,MCJITに続く世代です. 25 | このチュートリアルは,以前のバージョンのJIT APIでの開発経験を前提に書いたものではないですが,そういった経験や知識があると,よりそれぞれの要素がわかりやすくなるでしょう. 26 | 古いバージョンからORCへの移行が楽になるように,説明では,折りを見て,明確に古いAPIとORCの関連について説明することにします. 27 | 28 | # JIT APIの基礎 29 | 30 | JITコンパイラの目的は,古くからあるコンパイラのように先にコンパイルしておくのではなく,必要に応じて,その場でコードをコンパイルすることです. 31 | これらの基本的な目的をサポートするために,二つの生のJIT APIがあります. 32 | 33 | ``` 34 | // IRモジュールを実行可能にする 35 | Error addModule(std::unique_ptr M); 36 | // JITに追加されるシンボル(関数や変数)へのポインタを探す 37 | Expected lookup(); 38 | ``` 39 | 40 | このAPIの基本的な使い方は,実行するmain関数を実行する時に使います. 41 | 例えば,以下のような形式になります. 42 | 43 | ``` 44 | JIT J; 45 | J.addModule(buildModule()); 46 | auto *Main = (int(*)(int, char*[]))J.lookup("main").getAddress(); 47 | int Result = Main(); 48 | ``` 49 | 50 | このチュートリアルで作っていくAPIは,このシンプルなテーマの派生です. 51 | このAPIを使って,並列コンパイル,最適化,遅延コンパイルをサポートするためのJITの実装を洗練させていくことになります. 52 | 最後に,JITに抽象構文木のような高いレベルの表現を可能にするように拡張していくことになります. 53 | 54 | # KaleidoscopeJIT 55 | 56 | 前節で紹介したAPIを使って,ここで,LLVMチュートリアルの言語を実装するために使った`KaleidoscopeJIT`クラスを調べていくことにします. 57 | 今から作っていくJITへの入力として,チュートリアルの7章からREPLコードを使います. 58 | このチュートリアルでは,ユーザがexpressionを入力するたびに,REPLが,JITにその表現に該当するコードを含むIRモジュールを追加していくものでした. 59 | もし,そのexpressionが,`1+1`や`sin(x)`のようなtop-level expressionである場合,REPLは,JITクラスのルックアップメソッドを,expressionに対するコードを探したり,実行したりするために使います. 60 | さらに進んだ章では,JITとインタラクションできるようにREPLを改造していきますが,今のところ,この設定を前提として,JITの実装に注目していくことにします. 61 | 62 | `KaleidoscopeJIT`は,[KaleidoscopeJIT.h](https://github.com/llvm-mirror/llvm/blob/master/examples/Kaleidoscope/include/KaleidoscopeJIT.h)で定義されます. 63 | 64 | ``` 65 | 66 | #ifndef LLVM_EXECUTIONENGINE_ORC_KALEIDOSCOPEJIT_H 67 | #define LLVM_EXECUTIONENGINE_ORC_KALEIDOSCOPEJIT_H 68 | 69 | #include "llvm/ADT/StringRef.h" 70 | #include "llvm/ExecutionEngine/JITSymbol.h" 71 | #include "llvm/ExecutionEngine/Orc/CompileUtils.h" 72 | #include "llvm/ExecutionEngine/Orc/Core.h" 73 | #include "llvm/ExecutionEngine/Orc/ExecutionUtils.h" 74 | #include "llvm/ExecutionEngine/Orc/IRCompileLayer.h" 75 | #include "llvm/ExecutionEngine/Orc/JITTargetMachineBuilder.h" 76 | #include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h" 77 | #include "llvm/ExecutionEngine/SectionMemoryManager.h" 78 | #include "llvm/IR/DataLayout.h" 79 | #include "llvm/IR/LLVMContext.h" 80 | #include 81 | 82 | namespace llvm { 83 | namespace orc { 84 | 85 | class KaleidoscopeJIT { 86 | private: 87 | ExecutionSession ES; 88 | RTDyldObjectLinkingLayer ObjectLayer; 89 | IRCompileLayer CompileLayer; 90 | 91 | DataLayout DL; 92 | MangleAndInterner Mangle; 93 | ThreadSafeContext Ctx; 94 | 95 | public: 96 | KaleidoscopeJIT(JITTargetMachineBuilder JTMB, DataLayout DL) 97 | : ObjectLayer(ES, 98 | []() { return llvm::make_unique(); }), 99 | CompileLayer(ES, ObjectLayer, ConcurrentIRCompiler(std::move(JTMB))), 100 | DL(std::move(DL)), Mangle(ES, this->DL), 101 | Ctx(llvm::make_unique()) { 102 | ES.getMainJITDylib().setGenerator( 103 | cantFail(DynamicLibrarySearchGenerator::GetForCurrentProcess(DL))); 104 | } 105 | ``` 106 | 107 | このクラスには,6つのメンバがあります. 108 | 109 | 1. `ExecutionSession` as `ES`.文字列プール,グローバルのmutex,エラー報告のための基盤などを含むJITコンパイルされたコードのためのコンテキストを供給するためのものです. 110 | 2. `RTDyldObjectLinkingLayer` as `ObjectLayer`. (直接使うことはないですが)JITにオブジェクトファイルを追加するために使います. 111 | 3. `IRCompileLayer` as `CompileLayer`.JITにLLVMモジュールを追加するために使います.モジュールは,ObjectLayer上に構築されます. 112 | 4. `DataLayout` as `DL`,`MangleAndInterner` as `Mangle`.これは,シンボルをマングリングするために使われます. 113 | 5. `ThreadSafeContext` as `Ctx`.JITのためのIRファイルを構築するときにクライアントが使用します. 114 | 115 | 次にクラスのコンストラクタを見ていきます. 116 | コンストラクタは,`JITTargetMachineBuilder`と`DataLayout`を引数にとります. 117 | `JITTargetMachineBuilder`は,`IRCompiler`によって使われ,`DataLayout`は,メンバ変数`DL`の初期化に使われます. 118 | コンストラクタは,`ObjectLayer`の初期化から始まります. 119 | `ObjectLayer`は,`ExecutionSession`へのリンクと,追加されるそれぞれのモジュールに対応するJITのメモリマネージャをビルドする関数オブジェクトを必要とします. 120 | (JITメモリマネージャは,メモリ確保,メモリのアクセス権,JITコンパイルされたコードへの例外ハンドラの登録などを管理します.) 121 | ここでは,この章で学ぶことに十分な基本的なメモリマネージメント機能を持ったそのまま使えるユーティリティである`SectionMemoryManager`を返すラムダ式を使うことにします. 122 | 次に,`CompileLayer`を初期化します. 123 | `CompileLayer`は,以下の三つを必要とします. 124 | 125 | 1. `ExecutionSession`への参照. 126 | 2. オブジェクトレイヤーへの参照. 127 | 3. IRからオブジェクトファイルへの実際のコンパイルを実行するために使われるコンパイラのインスタンス 128 | 129 | `ConcurrentIRCompiler`ユーティリティをそのまま使います. 130 | これは,コンストラクタの`JITTargetMachineBuilder`引数を使って初期化できます. 131 | `ConcurrentIRCompiler`ユーティリティは,コンパイラに必要とされる`llvm TargetMachines`(これはスレッドセーフでありません)をビルドするために,`JITTargetMachineBuilder`を使います. 132 | この後,`DataLayout`,`MangleAndInterner`,`ThreadSafeContext`などのサポートメンバ変数を初期化します. 133 | `DL`は,引数の`DataLayout`で,`Mangler`は,`ExecutionSession`とメンバ変数の`DL`,`Ctx`は,デフォルトのコストラクタで初期化します. 134 | これで,全部のメンバ変数が初期化されたので,残るやるべきことをひとつやります. 135 | それは,コード保存する`JITDylib`の設定の微調整です. 136 | この`dylib`が,そこに追加していくシンボルだけではなく,REPLのプロセスから入力されるシンボルも保持できるように,それを改造したい訳です. 137 | ここで,`DynamicLibrarySearchGenerator::GetForCurrentProcess`メソッドを使って,`DynamicLibrarySearchGenerator`を,`dylib`にアタッチすることでこれを実現します. 138 | 139 | ``` 140 | static Expected> Create() { 141 | auto JTMB = JITTargetMachineBuilder::detectHost(); 142 | 143 | if (!JTMB) 144 | return JTMB.takeError(); 145 | 146 | auto DL = JTMB->getDefaultDataLayoutForTarget(); 147 | if (!DL) 148 | return DL.takeError(); 149 | 150 | return llvm::make_unique(std::move(*JTMB), std::move(*DL)); 151 | } 152 | 153 | const DataLayout &getDataLayout() const { return DL; } 154 | 155 | LLVMContext &getContext() { return *Ctx.getContext(); } 156 | ``` 157 | 158 | 次に,名前のついたコンストラクタ`Create`について説明します. 159 | `Create`は,ホストのプロセスのためのコードを生成するために`KaleidoscopeJIT`のインスタンスをビルドします. 160 | この関数は,クラスの`detectHost`メソッドを使って,`JITTargetMachineBuilder`をはじめに生成し,ターゲットプロセスのためのデータレイアウトを生成するインスタンスを使い,それを実行します. 161 | これらの処理は失敗する可能性があり,`Expected`型の値にラップされて返されるため,続ける前にエラーをチェックしなければなりません. 162 | 両方の操作が成功した場合,dereferenceオペレータを使って,結果をアンラップし,関数の最後の行にあるKaleidoscopeJITのコンストラクタに渡すことできます. 163 | 164 | 名前のついたコンストラクタに続いて,`getDataLayout()`と`getContext()`についてです. 165 | これらは,JITによって(特にコンテキスト)作成・管理されるデータ構造を,IRモジュールをビルドするREPLコードが利用できるようにするために使われます. 166 | 167 | ``` 168 | void addModule(std::unique_ptr M) { 169 | cantFail(CompileLayer.add(ES.getMainJITDylib(), 170 | ThreadSafeModule(std::move(M), Ctx))); 171 | } 172 | 173 | Expected lookup(StringRef Name) { 174 | return ES.lookup({&ES.getMainJITDylib()}, Mangle(Name.str())); 175 | } 176 | ``` 177 | 178 | `addModule`が,初めてのJIT APIのメソッドになります. 179 | このメソッドは,IRをJITに追加したり,実行可能にする機能を担います. 180 | 今回のJITの初期実装では,`CompileLayer`に,IRを追加することで,作ったモジュール"実行可能に"します. 181 | この実装は,その`Module`をメインの`JITDylib`に保存します. 182 | この処理は,モジュールにあるそれぞれの定義のための`JITDylib`にある新しいシンボルテーブルのエントリを作成し,その定義のどれかがルックアップされるまで,モジュールのコンパイルを遅延させます. 183 | 注意して欲しいのは,これは遅延コンパイルではなく,それが使われたときに初めて定義を参照するというだけです. 184 | 関数が実際呼ばれるまで,コンパイルを遅延させるのは,もっと後の章になってから説明します. 185 | `Module`を追加するためには,そのインスタンスを,`ThreadSafeModule`でラップする必要があります. 186 | `ThreadSafeModule`は,`Module`の`LLVMContext`のライフタイムをスレッドセーフに管理します. 187 | このサンプルでは,すべてのモジュールは,`Ctx`メンバーを共有することになります. 188 | JITが生きている間,`Ctx`は,存在することになります. 189 | 後の章で,並列コンパイルに切り替えるときには,我々は,モジュールごとに新しいコンテキストを使うことになります. 190 | 191 | 最後は`lookup`です. 192 | `lookup`は,関数や変数のシンボル名を使って,JITに追加された,それらの定義のアドレスをルックアップできるようにします. 193 | 上で書いたように,`lookup`は,そのときにまだコンパイルされていない暗黙にシンボルに対するコンパイルのトリガーとなります. 194 | 実装する`lookup`は,検索するための`dylib`(今回のサンプルではメインのひとつだけ)のリスト,検索するシンボルの名前を`ExecutionSession::lookup`に渡します. 195 | この名前には,ひと工夫必要で,はじめに検索しようとしているシンボルの名前をマングリングする必要があります. 196 | ORC JITのコンポーネントは,平文で書かれたIRのシンボル名ではなく,内部的にマングリングされたシンボルを使います. 197 | これは,静的コンパイラもリンカも同様です. 198 | この仕組みのおかげで,JITコンパイルされたコードは,事前にコンパイルされたアプリケーション内のコードや他の共有ライブラリと,簡単に相互運用できます. 199 | マングリングの種類は,ターゲットのプラットホームに依存する,`DataLayout`に依存することになります. 200 | 移植性を維持したり,マングリングされていない名前で検索できるようにするためには,`Mangle`のメンバ関数を使って,自分自身でマングリングする必要があります. 201 | 202 | "Building a JIT"の第1章は,これで終わりです. 203 | このコードは,基本ではありますが,JITが動いてるプロセスのコンテキストで,LLVM IRを実行可能し,利用するのに十分な機能を備えています. 204 | 次章では,より良いコードを生成するためにJITを拡張していく方法を紹介するとともに,ORCのレイヤーの概念についてより深く入り込んでいきます. -------------------------------------------------------------------------------- /va_list/variable_arguments.cpp: -------------------------------------------------------------------------------- 1 | // MIT License 2 | // 3 | // Copyright (c) 2020 sonson 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 W ARRANTY 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 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "llvm/ExecutionEngine/Orc/LLJIT.h" 29 | #include "llvm/IR/IRBuilder.h" 30 | #include "llvm/IR/Verifier.h" 31 | #include "llvm/Support/TargetSelect.h" 32 | #include "llvm/Support/InitLLVM.h" 33 | 34 | // http://llvm.org/docs/LangRef.html#int-varargs 35 | 36 | // base code 37 | 38 | // ; This struct is different for every platform. For most platforms, 39 | // ; it is merely an i8*. 40 | // %struct.va_list = type { i8* } 41 | 42 | // ; For Unix x86_64 platforms, va_list is the following struct: 43 | // ; %struct.va_list = type { i32, i32, i8*, i8* } 44 | 45 | // define i32 @test(i32 %X, ...) { 46 | // ; Initialize variable argument processing 47 | // %ap = alloca %struct.va_list 48 | // %ap2 = bitcast %struct.va_list* %ap to i8* 49 | // call void @llvm.va_start(i8* %ap2) 50 | 51 | // ; Read a single integer argument 52 | // %tmp = va_arg i8* %ap2, i32 53 | 54 | // ; Demonstrate usage of llvm.va_copy and llvm.va_end 55 | // %aq = alloca i8* 56 | // %aq2 = bitcast i8** %aq to i8* 57 | // call void @llvm.va_copy(i8* %aq2, i8* %ap2) 58 | // call void @llvm.va_end(i8* %aq2) 59 | 60 | // ; Stop processing of arguments. 61 | // call void @llvm.va_end(i8* %ap2) 62 | // ret i32 %tmp 63 | // } 64 | 65 | // declare void @llvm.va_start(i8*) 66 | // declare void @llvm.va_copy(i8*, i8*) 67 | // declare void @llvm.va_end(i8*) 68 | 69 | // C 70 | extern "C" void print_int(int i) { 71 | printf("%d\n", i); 72 | } 73 | 74 | // C 75 | extern "C" void print_double(double i) { 76 | printf("%f\n", i); 77 | } 78 | 79 | using llvm::LLVMContext; 80 | using llvm::StructType; 81 | using llvm::Type; 82 | using llvm::Module; 83 | using llvm::FunctionType; 84 | using llvm::Function; 85 | 86 | StructType* register_struct_va_list(LLVMContext* context) { 87 | std::vector members; 88 | members.push_back(Type::getInt32Ty(*context)); 89 | members.push_back(Type::getInt32Ty(*context)); 90 | members.push_back(Type::getInt8PtrTy(*context)); 91 | members.push_back(Type::getInt8PtrTy(*context)); 92 | 93 | StructType *const struct_va_list = StructType::create(*context, "struct.va_list"); 94 | struct_va_list->setBody(members); 95 | 96 | return struct_va_list; 97 | } 98 | 99 | void add_functions(LLVMContext* context, Module *module) { 100 | // load functions for variable arguments. 101 | FunctionType *function_type_print 102 | = FunctionType::get(Type::getVoidTy(*context), Type::getInt8PtrTy(*context), false); 103 | module->getOrInsertFunction("llvm.va_start", function_type_print); 104 | module->getOrInsertFunction("llvm.va_end", function_type_print); 105 | 106 | FunctionType *printIntFunctionType 107 | = FunctionType::get(Type::getVoidTy(*context), Type::getInt64Ty(*context), false); 108 | module->getOrInsertFunction("print_int", printIntFunctionType); 109 | 110 | FunctionType *printDoubleFunctionType 111 | = FunctionType::get(Type::getVoidTy(*context), Type::getDoubleTy(*context), false); 112 | module->getOrInsertFunction("print_double", printDoubleFunctionType); 113 | } 114 | 115 | Function* define_main_function(LLVMContext* context, Module *module) { 116 | // define function 117 | auto main_function_name = "originalFunction"; 118 | std::vector args(1, Type::getInt64Ty(*context)); 119 | FunctionType *functionType = FunctionType::get(Type::getDoubleTy(*context), args, true); 120 | return Function::Create(functionType, Function::ExternalLinkage, main_function_name, module); 121 | } 122 | 123 | int main(int argc, char *argv[]) { 124 | using llvm::IRBuilder; 125 | using llvm::BasicBlock; 126 | using llvm::ConstantFP; 127 | using llvm::Value; 128 | 129 | // Init LLVM 130 | llvm::InitLLVM X(argc, argv); 131 | 132 | // create context 133 | auto context = std::make_unique(); 134 | 135 | llvm::InitializeNativeTarget(); 136 | llvm::InitializeNativeTargetAsmPrinter(); 137 | 138 | // Create a new module 139 | std::unique_ptr module(new Module("originalModule", *context)); 140 | // LLVM IR builder 141 | IRBuilder<> builder(*context); 142 | 143 | StructType *const struct_va_list = register_struct_va_list(context.get()); 144 | add_functions(context.get(), module.get()); 145 | 146 | Function *function = define_main_function(context.get(), module.get()); 147 | 148 | // Create a new basic block to start insertion into. 149 | BasicBlock *basicBlock = BasicBlock::Create(*context, "entry", function); 150 | builder.SetInsertPoint(basicBlock); 151 | 152 | // allocate mutable variables 153 | llvm::AllocaInst *a_va_list = builder.CreateAlloca(struct_va_list, 0, "va_list"); 154 | llvm::AllocaInst *a_count = builder.CreateAlloca(llvm::Type::getInt64Ty(*context), 0, "count"); 155 | llvm::AllocaInst *a_summation = builder.CreateAlloca(llvm::Type::getDoubleTy(*context), 0, "summation"); 156 | 157 | // load the first argument. 158 | llvm::Value* arg = (function->arg_begin()); 159 | builder.CreateStore(arg, a_count); 160 | // initialize summation 161 | builder.CreateStore(llvm::ConstantFP::get(*context, llvm::APFloat(0.0)), a_summation); 162 | // get pointer to va_list struct 163 | auto pointer_va_list = builder.CreateBitCast(a_va_list, llvm::Type::getInt8PtrTy(*context), "&va_list"); 164 | 165 | Function *func_va_start = module->getFunction("llvm.va_start"); 166 | Function *func_va_end = module->getFunction("llvm.va_end"); 167 | 168 | builder.CreateCall(func_va_start, pointer_va_list); 169 | 170 | BasicBlock *loop_block = BasicBlock::Create(*context, "loop", function); 171 | builder.CreateBr(loop_block); 172 | builder.SetInsertPoint(loop_block); 173 | 174 | auto step = llvm::ConstantInt::get(Type::getInt64Ty(*context), 1); 175 | 176 | // for loop 177 | auto current_count = builder.CreateLoad(a_count, "current_count"); 178 | auto updated_count = builder.CreateSub(current_count, step, "updated_count"); 179 | builder.CreateStore(updated_count, a_count); 180 | 181 | auto *value_from_va_list 182 | = new llvm::VAArgInst(pointer_va_list, llvm::Type::getDoubleTy(*context), "value", loop_block); 183 | llvm::Value *value = llvm::dyn_cast_or_null(value_from_va_list); 184 | 185 | auto current_summation = builder.CreateLoad(a_summation, "current_summation"); 186 | auto updated_summation = builder.CreateFAdd(current_summation, value, "updated_summation"); 187 | builder.CreateStore(updated_summation, a_summation); 188 | auto loop_flag 189 | = builder.CreateICmpSGT(updated_count, llvm::ConstantInt::get(Type::getInt64Ty(*context), 0), "loop_flag"); 190 | 191 | BasicBlock *after_loop_block = BasicBlock::Create(*context, "afterloop", function); 192 | 193 | builder.CreateCondBr(loop_flag, loop_block, after_loop_block); 194 | 195 | builder.SetInsertPoint(after_loop_block); 196 | builder.CreateCall(func_va_end, pointer_va_list); 197 | 198 | auto result = builder.CreateLoad(a_summation, "result"); 199 | 200 | builder.CreateRet(result); 201 | 202 | if (verifyFunction(*function)) { 203 | std::cout << ": Error constructing function!\n" << std::endl; 204 | return 1; 205 | } 206 | 207 | module->print(llvm::outs(), nullptr); 208 | 209 | if (verifyModule(*module)) { 210 | std::cout << ": Error module!\n" << std::endl; 211 | return 1; 212 | } 213 | 214 | auto jit = llvm::orc::LLJITBuilder().create(); 215 | 216 | 217 | if (jit) { 218 | auto thread_safe_module = llvm::orc::ThreadSafeModule(std::move(module), std::move(context)); 219 | auto error = jit->get()->addIRModule(std::move(thread_safe_module)); 220 | assert(!error && "LLJIT can not add handle module."); 221 | llvm::orc::JITDylib &dylib = jit->get()->getMainJITDylib(); 222 | const llvm::DataLayout &dataLayout = jit->get()->getDataLayout(); 223 | auto prefix = dataLayout.getGlobalPrefix(); 224 | auto generator = llvm::cantFail(llvm::orc::DynamicLibrarySearchGenerator::GetForCurrentProcess(prefix)); 225 | dylib.addGenerator(std::move(generator)); 226 | auto symbol = jit->get()->lookup("originalFunction"); 227 | auto f = reinterpret_cast(symbol->getAddress()); 228 | std::cout << "Evaluated to " << f(5, 19.1, 3.1, 1.4, 10.1, 11.1) << std::endl; 229 | } else { 230 | std::cout << "Error - LLJIT can not be initialized." << std::endl; 231 | } 232 | 233 | return 0; 234 | } -------------------------------------------------------------------------------- /BuildingAJITJP/chap04.md: -------------------------------------------------------------------------------- 1 | # Building a JIT: Extreme Laziness - Using LazyReexports to JIT from ASTs 2 | 1. Chapter 4 Introduction 3 | 1. Full Code Listing 4 | 5 | This tutorial is under active development. It is incomplete and details may change frequently. Nonetheless we invite you to try it out as it stands, and we welcome any feedback. 6 | 7 | ### Chapter 4 Introduction 8 | Welcome to Chapter 4 of the “Building an ORC-based JIT in LLVM” tutorial. This chapter introduces custom MaterializationUnits and Layers, and the lazy reexports API. Together these will be used to replace the CompileOnDemandLayer from Chapter 3 with a custom lazy-JITing scheme that JITs directly from Kaleidoscope ASTs. 9 | 10 | To be done: 11 | 12 | (1) Describe the drawbacks of JITing from IR (have to compile to IR first, which reduces the benefits of laziness). 13 | 14 | (2) Describe CompileCallbackManagers and IndirectStubManagers in detail. 15 | 16 | (3) Run through the implementation of addFunctionAST. 17 | 18 | ### Full Code Listing 19 | Here is the complete code listing for our running example that JITs lazily from Kaleidoscope ASTS. To build this example, use: 20 | 21 | # Compile 22 | clang++ -g toy.cpp `llvm-config --cxxflags --ldflags --system-libs --libs core orcjit native` -O3 -o toy 23 | # Run 24 | ./toy 25 | Here is the code: 26 | 27 | ``` 28 | //===- KaleidoscopeJIT.h - A simple JIT for Kaleidoscope --------*- C++ -*-===// 29 | // 30 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 31 | // See https://llvm.org/LICENSE.txt for license information. 32 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 33 | // 34 | //===----------------------------------------------------------------------===// 35 | // 36 | // Contains a simple JIT definition for use in the kaleidoscope tutorials. 37 | // 38 | //===----------------------------------------------------------------------===// 39 | 40 | #ifndef LLVM_EXECUTIONENGINE_ORC_KALEIDOSCOPEJIT_H 41 | #define LLVM_EXECUTIONENGINE_ORC_KALEIDOSCOPEJIT_H 42 | 43 | #include "llvm/ADT/StringRef.h" 44 | #include "llvm/ExecutionEngine/JITSymbol.h" 45 | #include "llvm/ExecutionEngine/Orc/CompileOnDemandLayer.h" 46 | #include "llvm/ExecutionEngine/Orc/CompileUtils.h" 47 | #include "llvm/ExecutionEngine/Orc/Core.h" 48 | #include "llvm/ExecutionEngine/Orc/ExecutionUtils.h" 49 | #include "llvm/ExecutionEngine/Orc/IRCompileLayer.h" 50 | #include "llvm/ExecutionEngine/Orc/IRTransformLayer.h" 51 | #include "llvm/ExecutionEngine/Orc/JITTargetMachineBuilder.h" 52 | #include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h" 53 | #include "llvm/ExecutionEngine/Orc/TPCIndirectionUtils.h" 54 | #include "llvm/ExecutionEngine/Orc/TargetProcessControl.h" 55 | #include "llvm/ExecutionEngine/SectionMemoryManager.h" 56 | #include "llvm/IR/DataLayout.h" 57 | #include "llvm/IR/LLVMContext.h" 58 | #include "llvm/IR/LegacyPassManager.h" 59 | #include "llvm/Transforms/InstCombine/InstCombine.h" 60 | #include "llvm/Transforms/Scalar.h" 61 | #include "llvm/Transforms/Scalar/GVN.h" 62 | #include 63 | 64 | class PrototypeAST; 65 | class ExprAST; 66 | 67 | /// FunctionAST - This class represents a function definition itself. 68 | class FunctionAST { 69 | std::unique_ptr Proto; 70 | std::unique_ptr Body; 71 | 72 | public: 73 | FunctionAST(std::unique_ptr Proto, 74 | std::unique_ptr Body) 75 | : Proto(std::move(Proto)), Body(std::move(Body)) {} 76 | 77 | const PrototypeAST& getProto() const; 78 | const std::string& getName() const; 79 | llvm::Function *codegen(); 80 | }; 81 | 82 | /// This will compile FnAST to IR, rename the function to add the given 83 | /// suffix (needed to prevent a name-clash with the function's stub), 84 | /// and then take ownership of the module that the function was compiled 85 | /// into. 86 | llvm::orc::ThreadSafeModule irgenAndTakeOwnership(FunctionAST &FnAST, 87 | const std::string &Suffix); 88 | 89 | namespace llvm { 90 | namespace orc { 91 | 92 | class KaleidoscopeASTLayer; 93 | class KaleidoscopeJIT; 94 | 95 | class KaleidoscopeASTMaterializationUnit : public MaterializationUnit { 96 | public: 97 | KaleidoscopeASTMaterializationUnit(KaleidoscopeASTLayer &L, 98 | std::unique_ptr F); 99 | 100 | StringRef getName() const override { 101 | return "KaleidoscopeASTMaterializationUnit"; 102 | } 103 | 104 | void materialize(std::unique_ptr R) override; 105 | 106 | private: 107 | void discard(const JITDylib &JD, const SymbolStringPtr &Sym) override { 108 | llvm_unreachable("Kaleidoscope functions are not overridable"); 109 | } 110 | 111 | KaleidoscopeASTLayer &L; 112 | std::unique_ptr F; 113 | }; 114 | 115 | class KaleidoscopeASTLayer { 116 | public: 117 | KaleidoscopeASTLayer(IRLayer &BaseLayer, const DataLayout &DL) 118 | : BaseLayer(BaseLayer), DL(DL) {} 119 | 120 | Error add(ResourceTrackerSP RT, std::unique_ptr F) { 121 | return RT->getJITDylib().define( 122 | std::make_unique(*this, 123 | std::move(F)), 124 | RT); 125 | } 126 | 127 | void emit(std::unique_ptr MR, 128 | std::unique_ptr F) { 129 | BaseLayer.emit(std::move(MR), irgenAndTakeOwnership(*F, "")); 130 | } 131 | 132 | SymbolFlagsMap getInterface(FunctionAST &F) { 133 | MangleAndInterner Mangle(BaseLayer.getExecutionSession(), DL); 134 | SymbolFlagsMap Symbols; 135 | Symbols[Mangle(F.getName())] = 136 | JITSymbolFlags(JITSymbolFlags::Exported | JITSymbolFlags::Callable); 137 | return Symbols; 138 | } 139 | 140 | private: 141 | IRLayer &BaseLayer; 142 | const DataLayout &DL; 143 | }; 144 | 145 | KaleidoscopeASTMaterializationUnit::KaleidoscopeASTMaterializationUnit( 146 | KaleidoscopeASTLayer &L, std::unique_ptr F) 147 | : MaterializationUnit(L.getInterface(*F), nullptr), L(L), F(std::move(F)) {} 148 | 149 | void KaleidoscopeASTMaterializationUnit::materialize( 150 | std::unique_ptr R) { 151 | L.emit(std::move(R), std::move(F)); 152 | } 153 | 154 | class KaleidoscopeJIT { 155 | private: 156 | std::unique_ptr TPC; 157 | std::unique_ptr ES; 158 | std::unique_ptr TPCIU; 159 | 160 | DataLayout DL; 161 | MangleAndInterner Mangle; 162 | 163 | RTDyldObjectLinkingLayer ObjectLayer; 164 | IRCompileLayer CompileLayer; 165 | IRTransformLayer OptimizeLayer; 166 | KaleidoscopeASTLayer ASTLayer; 167 | 168 | JITDylib &MainJD; 169 | 170 | static void handleLazyCallThroughError() { 171 | errs() << "LazyCallThrough error: Could not find function body"; 172 | exit(1); 173 | } 174 | 175 | public: 176 | KaleidoscopeJIT(std::unique_ptr TPC, 177 | std::unique_ptr ES, 178 | std::unique_ptr TPCIU, 179 | JITTargetMachineBuilder JTMB, DataLayout DL) 180 | : TPC(std::move(TPC)), ES(std::move(ES)), TPCIU(std::move(TPCIU)), 181 | DL(std::move(DL)), Mangle(*this->ES, this->DL), 182 | ObjectLayer(*this->ES, 183 | []() { return std::make_unique(); }), 184 | CompileLayer(*this->ES, ObjectLayer, 185 | std::make_unique(std::move(JTMB))), 186 | OptimizeLayer(*this->ES, CompileLayer, optimizeModule), 187 | ASTLayer(OptimizeLayer, this->DL), 188 | MainJD(this->ES->createBareJITDylib("
")) { 189 | MainJD.addGenerator( 190 | cantFail(DynamicLibrarySearchGenerator::GetForCurrentProcess( 191 | DL.getGlobalPrefix()))); 192 | } 193 | 194 | ~KaleidoscopeJIT() { 195 | if (auto Err = ES->endSession()) 196 | ES->reportError(std::move(Err)); 197 | if (auto Err = TPCIU->cleanup()) 198 | ES->reportError(std::move(Err)); 199 | } 200 | 201 | static Expected> Create() { 202 | auto SSP = std::make_shared(); 203 | auto TPC = SelfTargetProcessControl::Create(SSP); 204 | if (!TPC) 205 | return TPC.takeError(); 206 | 207 | auto ES = std::make_unique(std::move(SSP)); 208 | 209 | auto TPCIU = TPCIndirectionUtils::Create(**TPC); 210 | if (!TPCIU) 211 | return TPCIU.takeError(); 212 | 213 | (*TPCIU)->createLazyCallThroughManager( 214 | *ES, pointerToJITTargetAddress(&handleLazyCallThroughError)); 215 | 216 | if (auto Err = setUpInProcessLCTMReentryViaTPCIU(**TPCIU)) 217 | return std::move(Err); 218 | 219 | JITTargetMachineBuilder JTMB((*TPC)->getTargetTriple()); 220 | 221 | auto DL = JTMB.getDefaultDataLayoutForTarget(); 222 | if (!DL) 223 | return DL.takeError(); 224 | 225 | return std::make_unique(std::move(*TPC), std::move(ES), 226 | std::move(*TPCIU), std::move(JTMB), 227 | std::move(*DL)); 228 | } 229 | 230 | const DataLayout &getDataLayout() const { return DL; } 231 | 232 | JITDylib &getMainJITDylib() { return MainJD; } 233 | 234 | Error addModule(ThreadSafeModule TSM, ResourceTrackerSP RT = nullptr) { 235 | if (!RT) 236 | RT = MainJD.getDefaultResourceTracker(); 237 | 238 | return OptimizeLayer.add(RT, std::move(TSM)); 239 | } 240 | 241 | Error addAST(std::unique_ptr F, ResourceTrackerSP RT = nullptr) { 242 | if (!RT) 243 | RT = MainJD.getDefaultResourceTracker(); 244 | return ASTLayer.add(RT, std::move(F)); 245 | } 246 | 247 | Expected lookup(StringRef Name) { 248 | return ES->lookup({&MainJD}, Mangle(Name.str())); 249 | } 250 | 251 | private: 252 | static Expected 253 | optimizeModule(ThreadSafeModule TSM, const MaterializationResponsibility &R) { 254 | TSM.withModuleDo([](Module &M) { 255 | // Create a function pass manager. 256 | auto FPM = std::make_unique(&M); 257 | 258 | // Add some optimizations. 259 | FPM->add(createInstructionCombiningPass()); 260 | FPM->add(createReassociatePass()); 261 | FPM->add(createGVNPass()); 262 | FPM->add(createCFGSimplificationPass()); 263 | FPM->doInitialization(); 264 | 265 | // Run the optimizations over all functions in the module being added to 266 | // the JIT. 267 | for (auto &F : M) 268 | FPM->run(F); 269 | }); 270 | 271 | return std::move(TSM); 272 | } 273 | }; 274 | 275 | } // end namespace orc 276 | } // end namespace llvm 277 | 278 | #endif // LLVM_EXECUTIONENGINE_ORC_KALEIDOSCOPEJIT_H 279 | ``` -------------------------------------------------------------------------------- /va_list/README.md: -------------------------------------------------------------------------------- 1 | ## Build a function with Variable Length Arguments on LLVM in C++ 2 | 3 | You can find the documents about this at [here](https://llvm.org/docs/LangRef.html#int-varargs). User has to use intrinsic functions and type of LLVM, `va_start`, `va_end`, `va_list` and `va_copy`. But, LLVM has not prepared direct API to handle these functions and types so user handle these functins basic APIs. 4 | 5 | ### Goal 6 | 7 | Our goal is to build a function which sums all arguments(type is `double`.). But, due to the constraints of variable length arguments, the first argument of a function means the number of variables.(I think variable length arguments should not be used readily in so many cases in C/C++ becase it can not handle each type of variable directly.) 8 | 9 | If this function is implemented in C++, the code would look like the following. 10 | 11 | ``` 12 | double summation_va(int count,...) { 13 | double value = 0; 14 | double summation = 0; 15 | va_list vl; 16 | va_start(vl, count); 17 | for (int i = 0; i < count; i++) { 18 | value = va_arg(vl, double); 19 | summation += value; 20 | } 21 | va_end(vl); 22 | return summation; 23 | } 24 | ``` 25 | 26 | One can compile this code to llvm IR using clang. 27 | 28 | ``` 29 | ; Function Attrs: nounwind 30 | declare void @llvm.va_start(i8*) #1 31 | 32 | ; Function Attrs: nounwind 33 | declare void @llvm.va_end(i8*) #1 34 | 35 | ; Function Attrs: noinline nounwind optnone ssp uwtable 36 | define i32 @_Z4sum2iz(i32, ...) #0 { 37 | %2 = alloca i32, align 4 38 | %3 = alloca i32, align 4 39 | %4 = alloca i32, align 4 40 | %5 = alloca i32, align 4 41 | %6 = alloca [1 x %struct.__va_list_tag], align 16 42 | store i32 %0, i32* %2, align 4 43 | store i32 0, i32* %3, align 4 44 | store i32 0, i32* %4, align 4 45 | store i32 0, i32* %5, align 4 46 | %7 = getelementptr inbounds [1 x %struct.__va_list_tag], [1 x %struct.__va_list_tag]* %6, i64 0, i64 0 47 | %8 = bitcast %struct.__va_list_tag* %7 to i8* 48 | call void @llvm.va_start(i8* %8) 49 | store i32 0, i32* %3, align 4 50 | br label %9 51 | 52 | 9: ; preds = %35, %1 53 | %10 = load i32, i32* %3, align 4 54 | %11 = load i32, i32* %2, align 4 55 | %12 = icmp slt i32 %10, %11 56 | br i1 %12, label %13, label %38 57 | 58 | 13: ; preds = %9 59 | %14 = getelementptr inbounds [1 x %struct.__va_list_tag], [1 x %struct.__va_list_tag]* %6, i64 0, i64 0 60 | %15 = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* %14, i32 0, i32 0 61 | %16 = load i32, i32* %15, align 16 62 | %17 = icmp ule i32 %16, 40 63 | br i1 %17, label %18, label %24 64 | 65 | 18: ; preds = %13 66 | %19 = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* %14, i32 0, i32 3 67 | %20 = load i8*, i8** %19, align 16 68 | %21 = getelementptr i8, i8* %20, i32 %16 69 | %22 = bitcast i8* %21 to i32* 70 | %23 = add i32 %16, 8 71 | store i32 %23, i32* %15, align 16 72 | br label %29 73 | 74 | 24: ; preds = %13 75 | %25 = getelementptr inbounds %struct.__va_list_tag, %struct.__va_list_tag* %14, i32 0, i32 2 76 | %26 = load i8*, i8** %25, align 8 77 | %27 = bitcast i8* %26 to i32* 78 | %28 = getelementptr i8, i8* %26, i32 8 79 | store i8* %28, i8** %25, align 8 80 | br label %29 81 | 82 | 29: ; preds = %24, %18 83 | %30 = phi i32* [ %22, %18 ], [ %27, %24 ] 84 | %31 = load i32, i32* %30, align 4 85 | store i32 %31, i32* %4, align 4 86 | %32 = load i32, i32* %4, align 4 87 | %33 = load i32, i32* %5, align 4 88 | %34 = add nsw i32 %33, %32 89 | store i32 %34, i32* %5, align 4 90 | br label %35 91 | 92 | 35: ; preds = %29 93 | %36 = load i32, i32* %3, align 4 94 | %37 = add nsw i32 %36, 1 95 | store i32 %37, i32* %3, align 4 96 | br label %9 97 | 98 | 38: ; preds = %9 99 | %39 = getelementptr inbounds [1 x %struct.__va_list_tag], [1 x %struct.__va_list_tag]* %6, i64 0, i64 0 100 | %40 = bitcast %struct.__va_list_tag* %39 to i8* 101 | call void @llvm.va_end(i8* %40) 102 | %41 = load i32, i32* %5, align 4 103 | ret i32 %41 104 | } 105 | ``` 106 | 107 | This code is incredibly long. I suppose, this IR means that the code to get multiple variable length arguments from va_list has been expanded into native code. I want to implement a variable length arguments function with shorter IR codes. 108 | 109 | ### `struct.va_list` 110 | 111 | User must define `struct.va_list` before building a variable length arguments fuction. But, `va_list` structure depends on the environment. By above documents, 112 | 113 | ``` 114 | // ; This struct is different for every platform. For most platforms, 115 | // ; it is merely an i8*. 116 | // %struct.va_list = type { i8* } 117 | 118 | // ; For Unix x86_64 platforms, va_list is the following struct: 119 | // ; %struct.va_list = type { i32, i32, i8*, i8* } 120 | ``` 121 | 122 | I choose `type { i32, i32, i8*, i8* }`. 123 | 124 | In C++, user can define the struct `va_list` with the following codes. 125 | 126 | ``` 127 | std::vector members; 128 | members.push_back(Type::getInt32Ty(*context)); 129 | members.push_back(Type::getInt32Ty(*context)); 130 | members.push_back(Type::getInt8PtrTy(*context)); 131 | members.push_back(Type::getInt8PtrTy(*context)); 132 | 133 | StructType *const struct_va_list = StructType::create(*context, "struct.va_list"); 134 | struct_va_list->setBody(members); 135 | ``` 136 | 137 | User prepares `va_start` and `va_end` functions. These functions can be called via `Function` type. 138 | 139 | ``` 140 | Function *func_va_start = module->getFunction("llvm.va_start"); 141 | Function *func_va_end = module->getFunction("llvm.va_end"); 142 | ``` 143 | 144 | And, `va_list` should be allocated and got the pointer to itself using `CreateBitCast`. Besides, a loop counter and buffer to be saved the summation are allocated here. `a_count` holds the value of the count and `a_summation` does the value of the summation. 145 | 146 | ``` 147 | // allocate mutable variables 148 | llvm::AllocaInst *a_va_list = builder.CreateAlloca(struct_va_list, 0, "va_list"); 149 | llvm::AllocaInst *a_count = builder.CreateAlloca(llvm::Type::getInt64Ty(*context), 0, "count"); 150 | llvm::AllocaInst *a_summation = builder.CreateAlloca(llvm::Type::getDoubleTy(*context), 0, "summation"); 151 | 152 | // load the first argument. 153 | llvm::Value* arg = (function->arg_begin()); 154 | builder.CreateStore(arg, a_count); 155 | // initialize summation 156 | builder.CreateStore(llvm::ConstantFP::get(*context, llvm::APFloat(0.0)), a_summation); 157 | // get pointer to va_list struct 158 | auto pointer_va_list = builder.CreateBitCast(a_va_list, llvm::Type::getInt8PtrTy(*context), "&va_list"); 159 | 160 | // call va_start 161 | builder.CreateCall(func_va_start, pointer_va_list); 162 | ``` 163 | 164 | The codes to this point is implemented in the initialization block. And call `va_start` to start loading variable length arguments. In C++, 165 | 166 | ``` 167 | double summation_va(int count,...) { 168 | double value = 0; 169 | double summation = 0; 170 | va_list vl; 171 | va_start(vl, count); 172 | ``` 173 | 174 | ### Loop 175 | 176 | Next, build a loop block. Define a constant value to update loop counter as `step`. The block loads the current counter value from buffer which is allocated by `alloca` and increments and stores it to the buffer to update the loop counter. 177 | 178 | ``` 179 | BasicBlock *loop_block = BasicBlock::Create(*context, "loop", function); 180 | builder.CreateBr(loop_block); 181 | builder.SetInsertPoint(loop_block); 182 | 183 | auto step = llvm::ConstantInt::get(Type::getInt64Ty(*context), 1); 184 | 185 | // for loop 186 | auto current_count = builder.CreateLoad(a_count); 187 | auto updated_count = builder.CreateSub(current_count, step); 188 | builder.CreateStore(updated_count, a_count); 189 | ``` 190 | 191 | After handling the loop counter, implment the main task. One is allowed to fetch arguments using LLVM API `VAArgInst` from variable length arguments. This API works as `va_arg` in C/C++, the second argument have to be strictly matched the type of argument which you are fetching. `VAArgInst` can be cast into `Value` using `llvm::dyn_cast_or_null`(is like C++’s `dynamic_cast<>`). 192 | 193 | In case that you want to check type of the value which is fetched from `va_list` strictly, you have to add a code to check and cast, and pass the original struct which includes types of arguments from a callee. 194 | 195 | ``` 196 | auto *value_from_va_list = new llvm::VAArgInst(pointer_va_list, llvm::Type::getDoubleTy(*context), "value", loop_block); 197 | llvm::Value *value = llvm::dyn_cast_or_null(value_from_va_list); 198 | 199 | // summation 200 | auto current_summation = builder.CreateLoad(a_summation, "current_summation"); 201 | auto updated_summation = builder.CreateFAdd(current_summation, value, "updated_summation"); 202 | builder.CreateStore(updated_summation, a_summation); 203 | ``` 204 | 205 | At the end of the loop block, it checks if the counter is greater than 0 or not. The result of comparation is saved to `loop_flag`. Finally, add a conditional branch to check whether the code returns to the beginning of the loop block or skips to the last block. In the last block, load and return the result of summation. 206 | 207 | ``` 208 | auto loop_flag = builder.CreateICmpSGT(updated_count, llvm::ConstantInt::get(Type::getInt64Ty(*context), 0), "loop_flag"); 209 | 210 | BasicBlock *after_loop_block = BasicBlock::Create(*context, "afterloop", function); 211 | 212 | builder.CreateCondBr(loop_flag, loop_block, after_loop_block); 213 | 214 | builder.SetInsertPoint(after_loop_block); 215 | builder.CreateCall(func_va_end, pointer_va_list); 216 | 217 | auto result = builder.CreateLoad(a_summation, "result"); 218 | 219 | builder.CreateRet(result); 220 | ``` 221 | 222 | Finally, the following LLVM IR codes are generate accoriding to the above codes. I suppose that the code is reasonable. 223 | 224 | ``` 225 | ; ModuleID = 'originalModule' 226 | source_filename = "originalModule" 227 | 228 | %struct.va_list = type { i32, i32, i8*, i8* } 229 | 230 | ; Function Attrs: nounwind 231 | declare void @llvm.va_start(i8*) #0 232 | 233 | ; Function Attrs: nounwind 234 | declare void @llvm.va_end(i8*) #0 235 | 236 | define double @originalFunction(i64 %0, ...) { 237 | entry: 238 | %va_list = alloca %struct.va_list, align 8 239 | %count = alloca i64, align 8 240 | %summation = alloca double, align 8 241 | store i64 %0, i64* %count, align 4 242 | store double 0.000000e+00, double* %summation, align 8 243 | %"&va_list" = bitcast %struct.va_list* %va_list to i8* 244 | call void @llvm.va_start(i8* %"&va_list") 245 | br label %loop 246 | 247 | loop: ; preds = %loop, %entry 248 | %current_count = load i64, i64* %count, align 4 249 | %updated_count = sub i64 %current_count, 1 250 | store i64 %updated_count, i64* %count, align 4 251 | %value = va_arg i8* %"&va_list", double 252 | %current_summation = load double, double* %summation, align 8 253 | %updated_summation = fadd double %current_summation, %value 254 | store double %updated_summation, double* %summation, align 8 255 | %loop_flag = icmp sgt i64 %updated_count, 0 256 | br i1 %loop_flag, label %loop, label %afterloop 257 | 258 | afterloop: ; preds = %loop 259 | call void @llvm.va_end(i8* %"&va_list") 260 | %result = load double, double* %summation, align 8 261 | ret double %result 262 | } 263 | 264 | attributes #0 = { nounwind } 265 | ``` 266 | 267 | ### Summary 268 | 269 | When you want to use variable length arugments, you have to define `va_list`,`va_start` and `va_end`. But, LLVM API provides only the `va_arg` API as `VAArgInst` class, unlike the rest of the functions. I think that no one should use readily use functions with variable length arguments from point of the view of the type check safety, but it is very useful in some cases. 270 | 271 | [source code](./variable_arguments.cpp) -------------------------------------------------------------------------------- /KaleidoscopeJP/chap09.md: -------------------------------------------------------------------------------- 1 | # デバッグ情報の追加 2 | ## はじめに 3 | 9章にようこそ. 4 | ここまでで,まずまずな,ちょっとしたプログラミング言語を作ってきた. 5 | もし,その中で何か変なことがおこったとき,あなたはどうやって,作ってきたコードをデバッグしただろうか. 6 | 7 | ソースレベルのデバッグは,デバッガがバイナリを翻訳することを助けるフォーマットされたデータを使い,プログラマが書いたコードへマシンの状態をトレースする. 8 | LLVMでは,一般的にDWARFと呼ばれるフォーマットを使う. 9 | DWARFは,コンパクトに型やソースの位置,変数の位置を表現するエンコード方式である. 10 | 11 | 本章を簡単にまとめると,プログラミング言語にデバッグ情報を追加するためにしないといけないこと,DWARFに,デバッグ情報を変換する方法について学ぶことになる. 12 | 13 | ### 警告 14 | JITを通してデバッグすることはできない. 15 | このため,我々は,プログラムを小さく,スタンドアローンで動くようにコンパイルする必要がある. 16 | このため,言語を実行するために,コードをコンパイルする方法に多少,変更を加えることになる. 17 | これは,インタラクティブなJITというより,Kaleidoscopeで作ったシンプルなソースをコンパイルする何かを作ることになる. 18 | それは,たくさんコードを変更する必要をさけるため,今はひとつの"top level"コマンドしか持つことができない限界を受け入れる必要がある. 19 | 20 | ここに,これからコンパイルするアプリケーションで,コンパイルするコードがある. 21 | 22 | ``` 23 | def fib(x) 24 | if x < 3 then 25 | 1 26 | else 27 | fib(x-1)+fib(x-2); 28 | 29 | fib(10) 30 | ``` 31 | 32 | ## 何故これが難しいのか? 33 | デバッグ情報は,いくつかの理由で非常に取り扱いが難しいーほとんどの場合,最適化されたコードが原因だが. 34 | はじめに,最適化は.ソースコードの位置を維持することを難しくする. 35 | LLVM IRでは,オリジナルソースコードの位置関係を,それぞれのIRレベルの命令になっても,保持させることができる. 36 | 最適化パスは,新しく生成された命令に対しても,ソースコードの位置を保持すべきであるが,命令がマージされると,ひとつの位置しか保持できない. 37 | これは,最適化プログラムを通して,ステップするとき,コードの周りでジャンプすることの理由である. 38 | 二つ目に,最適化は,最適化によって変数を消去する方法,他の変数をメモリを共有する方法,あるいは追跡できないような方法,いずれかの方法で,変数を動かす可能性がある. 39 | このチュートリアルの目的のために,ここでは,最適化は避けることにする(あとで,パッチのセットのひとつとして,最適化のあとにデバッグ情報を追加する方法を説明する?). 40 | 41 | ## 先行コンパイル 42 | JITの複雑さに思い悩まず,ソースコードへデバッグ情報を追加する様子のみにハイライトを当てるため,フロントエンドによって発行されたIRを,実行,デバッグ,結果の確認ができるようなシンプルなスタンドアローンアプリケーションへとコンパイルできるように,Kaleidoscopeに2,3の修正を加えようと思う. 43 | 44 | はじめに,無名関数を"main"関数にする. 45 | この無名関数は,我々のtop level文を含む. 46 | 47 | ``` 48 | - auto Proto = llvm::make_unique("", std::vector()); 49 | + auto Proto = llvm::make_unique("main", std::vector()); 50 | ``` 51 | 52 | これは,関数に,"main"という名前を与えるだけのシンプルなものだ. 53 | 54 | 次に,コマンドラインとして動作するためのコードを削除する. 55 | 56 | ``` 57 | @@ -1129,7 +1129,6 @@ static void HandleTopLevelExpression() { 58 | /// top ::= definition | external | expression | ';' 59 | static void MainLoop() { 60 | while (1) { 61 | - fprintf(stderr, "ready> "); 62 | switch (CurTok) { 63 | case tok_eof: 64 | return; 65 | @@ -1184,7 +1183,6 @@ int main() { 66 | BinopPrecedence['*'] = 40; // highest. 67 | // Prime the first token. 68 | - fprintf(stderr, "ready> "); 69 | getNextToken(); 70 | ``` 71 | 72 | 最後に,すべての最適化のためのPassとJITをdisableする. 73 | そのため,パースとコード生成をやった後に,LLVM IRがエラーを起こすようになる. 74 | 75 | ``` 76 | @@ -1108,17 +1108,8 @@ static void HandleExtern() { 77 | static void HandleTopLevelExpression() { 78 | // Evaluate a top-level expression into an anonymous function. 79 | if (auto FnAST = ParseTopLevelExpr()) { 80 | - if (auto *FnIR = FnAST->codegen()) { 81 | - // We're just doing this to make sure it executes. 82 | - TheExecutionEngine->finalizeObject(); 83 | - // JIT the function, returning a function pointer. 84 | - void *FPtr = TheExecutionEngine->getPointerToFunction(FnIR); 85 | - 86 | - // Cast it to the right type (takes no arguments, returns a double) so we 87 | - // can call it as a native function. 88 | - double (*FP)() = (double (*)())(intptr_t)FPtr; 89 | - // Ignore the return value for this. 90 | - (void)FP; 91 | + if (!F->codegen()) { 92 | + fprintf(stderr, "Error generating code for top level expr"); 93 | } 94 | } else { 95 | // Skip token for error recovery. 96 | @@ -1439,11 +1459,11 @@ int main() { 97 | // target lays out data structures. 98 | TheModule->setDataLayout(TheExecutionEngine->getDataLayout()); 99 | OurFPM.add(new DataLayoutPass()); 100 | +#if 0 101 | OurFPM.add(createBasicAliasAnalysisPass()); 102 | // Promote allocas to registers. 103 | OurFPM.add(createPromoteMemoryToRegisterPass()); 104 | @@ -1218,7 +1210,7 @@ int main() { 105 | OurFPM.add(createGVNPass()); 106 | // Simplify the control flow graph (deleting unreachable blocks, etc). 107 | OurFPM.add(createCFGSimplificationPass()); 108 | - 109 | + #endif 110 | OurFPM.doInitialization(); 111 | 112 | // Set the global so the code gen can use this. 113 | ``` 114 | 115 | この相対的に小さい小さいセットの変化は,我々に,Kaleidoscope言語の一部を,コマンドラインを通して,実行可能なプログラムにコンパイルできるというポイントをもたらす. 116 | 117 | ``` 118 | Kaleidoscope-Ch9 < fib.ks | & clang -x ir - 119 | ``` 120 | 121 | これによって,現在の作業中のディレクトリに`a.out/a.exe`が作成される. 122 | 123 | ## コンパイル単位 124 | DWARFで書かれたコードのセクションのためのtop-levelコンテナは,コンパイル単位である. 125 | これは,個々の翻訳されたユニットに対する型と関数のデータを含む(ソースコードでいうひとつのファイル). 126 | だから,我々がする必要があることは,`fib.ks`ファイルのための,コンパイル単位を構築することである. 127 | 128 | ## DWARFの発行のためのセットアップ 129 | `IRBuilder`クラスに似た,LLVM IRファイルのためのデバッグ用のメタデータを構築するのを助ける`DIBuilder`クラスがある. 130 | そのクラスは,`IRBuilder`とLLVM IRに同じように対応するが,良い名前が付けられている. 131 | それを使うことは,あなたが`IRBuilder`や`Instruction`の名前に親しんでいるというより,DWARFの専門用語に慣れていることを要求する. 132 | しかし,もしあなたがメタデータのフォーマットについて一般できなドキュメントを通して読んだことがあるなら,それはもうちょっとわかりやすくなる. 133 | IRレベルの説明を構築するために,このクラスを使うことにする. 134 | `IRBuilder`クラスの構築は,モジュールを引数にとるため,モジュールを構築した後に,手短に,構築する必要がある. 135 | 多少使いやすくするために,グローバルのstatic変数として,`IRBuilder`クラスを構築する. 136 | 137 | 次に,我々がよく使うデータのいくつかをキャッシュするために,小さなコンテナを作る. 138 | 最初は,コンパイル単位である.しかし,複数型付けされた表現に困るべきではないため,我々の一つの型のために,ちょっとしたコードを書くことになる. 139 | 140 | ``` 141 | static DIBuilder *DBuilder; 142 | 143 | struct DebugInfo { 144 | DICompileUnit *TheCU; 145 | DIType *DblTy; 146 | 147 | DIType *getDoubleTy(); 148 | } KSDbgInfo; 149 | 150 | DIType *DebugInfo::getDoubleTy() { 151 | if (DblTy) 152 | return DblTy; 153 | 154 | DblTy = DBuilder->createBasicType("double", 64, dwarf::DW_ATE_float); 155 | return DblTy; 156 | } 157 | ``` 158 | 159 | メイン文の最後の方で,モジュールを構築するときに,以下のようなコードを書く. 160 | 161 | ``` 162 | DBuilder = new DIBuilder(*TheModule); 163 | 164 | KSDbgInfo.TheCU = DBuilder->createCompileUnit( 165 | dwarf::DW_LANG_C, DBuilder->createFile("fib.ks", "."), 166 | "Kaleidoscope Compiler", 0, "", 0); 167 | ``` 168 | 169 | ここで,気をつけるべきことがいくつかある. 170 | 初めの一つは,Kaleidoscopeと呼ばれる言語のためのコンパイル単位を作成しようとしている間,Cのための言語定数を使った. 171 | これは,デバッガが,それが認識しない言語の呼び出し規則やデフォルトABI(Application binary interface)を理解しているとは言えないため,我々は,LLVMのコード生成におけるCのABIをフォローするので,その方法はかなり精確ではある. 172 | これは,デバッガから関数を呼び出したり,実行できることから明らかである. 173 | 174 | 二つ目は,`createCompileUnit`を呼び出している`fib.ks`を見てもらいたい. 175 | Kaleidoscopeコンパイラへソースコードを押し付けるためのにシェルのリダイレクトを利用するために,これはデフォルトのハードコードされた値である. 176 | 普通のフロントエンドでは,名前を入力し,その値が,そこへ保存される. 177 | 178 | 最後のひとつは,`DIBuilder`を通してデバッグ情報を発行するパートの部分が,デバッグ情報を"finalized"する必要があるということである. 179 | `main`関数の最後の方で,モジュールをダンプす流前に,これをちゃんとやっているかを確認するが,その理由は,`DIBuilder`のための基本的なAPIの一部である. 180 | 181 | ``` 182 | DBuilder->finalize(); 183 | ``` 184 | 185 | ## 関数 186 | 今,コンパイル単位と,ソースコードの位置が与えられているとき,関数定義をデバッグ情報へ追加できる. 187 | `PrototypeAST::codegen()`の中で,我々は,プログラムの一部のコンテキストを記述するためのコードを2,3行追加する. 188 | この場合,関数自身の実際の定義とファイルそのものである. 189 | 190 | ``` 191 | DIFile *Unit = DBuilder->createFile( 192 | KSDbgInfo.TheCU.getFilename(), 193 | KSDbgInfo.TheCU.getDirectory() 194 | ); 195 | ``` 196 | 197 | 上で作った`Compile Unit`と,今開いているコードのファイル名を引数に与えて,`DIFile`を取得する. 198 | このとき,ソースの位置として0と(現在の抽象構文木には,ソースコードの行番号が保存されていない),関数定義を利用する. 199 | 200 | ``` 201 | DIScope *FContext = Unit; 202 | unsigned LineNo = 0; 203 | unsigned ScopeLine = 0; 204 | DISubprogram *SP = DBuilder->createFunction( 205 | FContext, 206 | P.getName(), 207 | StringRef(), 208 | Unit, 209 | LineNo, 210 | CreateFunctionType( 211 | TheFunction->arg_size(), 212 | Unit 213 | ), 214 | false /* internal linkage */, true /* definition */, ScopeLine, 215 | DINode::FlagPrototyped, 216 | false 217 | ); 218 | TheFunction->setSubprogram(SP); 219 | ``` 220 | 221 | 今,関数のメタデータすべてへの参照を持つ`DISubprogram`を持っている. 222 | 223 | ## ソースコードの行番号 224 | デバッグ情報でももっとも重要なのが,正確なソースコードの位置だ. 225 | つまり,これは,生成されたIRやバイナリをソースコードにマッピングできるようにする. 226 | けれども,ここで,我々は,Kaleidoscopeが.lexerやパーサにソースコードの一の情報を処理するように実装していないという問題がある. 227 | 228 | ``` 229 | struct SourceLocation { 230 | int Line; 231 | int Col; 232 | }; 233 | static SourceLocation CurLoc; 234 | static SourceLocation LexLoc = {1, 0}; 235 | 236 | static int advance() { 237 | int LastChar = getchar(); 238 | if (LastChar == '\n' || LastChar == '\r') { 239 | LexLoc.Line++; 240 | LexLoc.Col = 0; 241 | } else { 242 | LexLoc.Col++; 243 | } 244 | return LastChar; 245 | } 246 | ``` 247 | 248 | コードのこのセットの中で,ソースファイルの行と列を追跡する機能を追加した. 249 | すべてのトークンを分けるときに,トークンが始まるところの行と列を"語彙の位置"としてセットする. 250 | これを実行するため,`getchar()`を呼び出していたコードを,新しく作った関数,抽象構文木のノードにソースの位置を追加する`advance()`で書き換える. 251 | 252 | ``` 253 | class ExprAST { 254 | SourceLocation Loc; 255 | 256 | public: 257 | ExprAST(SourceLocation Loc = CurLoc) : Loc(Loc) {} 258 | virtual ~ExprAST() {} 259 | virtual Value* codegen() = 0; 260 | int getLine() const { return Loc.Line; } 261 | int getCol() const { return Loc.Col; } 262 | virtual raw_ostream &dump(raw_ostream &out, int ind) { 263 | return out << ':' << getLine() << ':' << getCol() << '\n'; 264 | } 265 | ``` 266 | 267 | ``` 268 | LHS = llvm::make_unique( 269 | BinLoc, 270 | BinOp, 271 | std::move(LHS), 272 | std::move(RHS) 273 | ); 274 | ``` 275 | 276 | 新しい表現を生成したときは,表現と変数の位置を,関数に引き渡していく. 277 | 278 | それぞれの命令が,正しいソースコードの位置情報を取得することを確実にするため,`Builder`に,今見ているところが新しいソースコードの位置であるかを問い合わせる必要がある. 279 | それには,ちょっとしたヘルパー関数を使えばよい. 280 | 281 | ``` 282 | void DebugInfo::emitLocation(ExprAST *AST) { 283 | DIScope *Scope; 284 | if (LexicalBlocks.empty()) 285 | Scope = TheCU; 286 | else 287 | Scope = LexicalBlocks.back(); 288 | 289 | Builder.SetCurrentDebugLocation( 290 | DebugLoc::get( 291 | AST->getLine(), 292 | AST->getCol(), 293 | Scope 294 | ) 295 | ); 296 | } 297 | ``` 298 | 299 | このコードは,同時にメインの`IRBuilder`にもどこを見ているかを問い合わせているが,スコープについても問い合わせている. 300 | スコープは,コンパイル単位レベルになりうるし,現在の関数のような語彙的なブロックに近いものとすることもできる. 301 | これを表現するために,以下のスコープのスタックを作る. 302 | 303 | ``` 304 | std::vector LexicalBlocks; 305 | ``` 306 | 307 | そして,それぞれの関数のコードを生成を始める時に,スタックの一番上に関数のスコープを置く. 308 | 309 | ``` 310 | KSDbgInfo.LexicalBlocks.push_back(SP); 311 | ``` 312 | 313 | また,関数のコード生成が終わったタイミングで,スコープスタックを積み降ろすことを忘れてはならない. 314 | 315 | ``` 316 | // Pop off the lexical block for the function since we added it 317 | // unconditionally. 318 | KSDbgInfo.LexicalBlocks.pop_back(); 319 | ``` 320 | 321 | そのとき,新しい抽象構文木のノードのためのコードを生成し始めるタイミングで,ソースコードの位置を発行するようにする. 322 | 323 | ``` 324 | KSDbgInfo.emitLocation(this); 325 | ``` 326 | 327 | ## 変数 328 | 関数があるとすると,スコープ内にある変数をプリントアウトできるようにする必要がある. 329 | 関数の引数をセットアップしよう. 330 | そうすれば,そこそこのバックトレースができるし,関数がどのように呼び出されているかを理解できる. 331 | 実際に,多くのコードは必要がないし,一般的に,`FunctionAST::codegen`内で引数の`alloca`を作っているときに,それをうまく処理しているのである. 332 | 333 | ``` 334 | // Record the function arguments in the NamedValues map. 335 | NamedValues.clear(); 336 | unsigned ArgIdx = 0; 337 | for (auto &Arg : TheFunction->args()) { 338 | // Create an alloca for this variable. 339 | AllocaInst *Alloca = CreateEntryBlockAlloca( 340 | TheFunction, 341 | Arg.getName() 342 | ); 343 | 344 | // Create a debug descriptor for the variable. 345 | DILocalVariable *D = DBuilder->createParameterVariable( 346 | SP, 347 | Arg.getName(), 348 | ++ArgIdx, 349 | Unit, 350 | LineNo, 351 | KSDbgInfo.getDoubleTy(), 352 | true 353 | ); 354 | 355 | DBuilder->insertDeclare( 356 | Alloca, 357 | D, 358 | DBuilder->createExpression(), 359 | DebugLoc::get(LineNo, 0, SP), 360 | Builder.GetInsertBlock() 361 | ); 362 | 363 | // Store the initial value into the alloca. 364 | Builder.CreateStore(&Arg, Alloca); 365 | 366 | // Add arguments to variable symbol table. 367 | NamedValues[Arg.getName()] = Alloca; 368 | } 369 | ``` 370 | 371 | ここで,はじめに変数を作り,それにスコープ(`SP`)を与え,名前,ソースコードの位置,型を与える.そして,それが引数や引数のインデックスになる. 372 | 次に,`llvm.dbg.declare`呼び出しを生成することで,`alloca`内で変数をIRレベルの表現で取得することを示し(さらに,そのIRは,変数の位置も与える),宣言において,スコープの開始位置のソースコードでの位置を示します. 373 | 374 | ここで特筆すべきおもしろいことは,様々なデバッガは,過去に,コードやデバッグ情報がどうやって生成されたか依存する仮説を持っているということである. 375 | この場合,関数の頭出しのための行情報を生成するのを避けるハックをする必要がある. 376 | このため,デバッガは,ブレイクポイントを設定するときに.これらの命令をスキップすることを知らなければならない. 377 | このため,`FunctionAST::CodeGen`の中で,数行,書き足す必要がある. 378 | 379 | ``` 380 | // Unset the location for the prologue emission (leading instructions with no 381 | // location in a function are considered part of the prologue and the debugger 382 | // will run past them when breaking on a function) 383 | KSDbgInfo.emitLocation(nullptr); 384 | ``` 385 | 386 | そして,実際に,関数の実装してのコードの生成が始まった行番号を発行する. 387 | 388 | ``` 389 | KSDbgInfo.emitLocation(Body.get()); 390 | ``` 391 | 392 | これによって,我々は,関数内でブレイクポイントをセットするためのデバッグ情報を十分に持たせることができ,引数の変数をプリントアウトしたり,関数を呼び出しできるようになる. 393 | このデバッガは,数行のコードを書くくらいなら,そんなに悪くないものです. 394 | -------------------------------------------------------------------------------- /BuildingAJITJP/chap02.md: -------------------------------------------------------------------------------- 1 | [オリジナル](https://llvm.org/docs/tutorial/BuildingAJIT2.html) 2 | 3 | # Building a JIT: Adding Optimizations – An introduction to ORC Layers 4 | 5 | Chapter 2 Introduction 6 | Optimizing Modules using the IRTransformLayer 7 | Full Code Listing 8 | This tutorial is under active development. It is incomplete and details may change frequently. Nonetheless we invite you to try it out as it stands, and we welcome any feedback. 9 | 10 | # JITを作ろう:最適化を加える - ORCレイヤーの紹介 11 | 12 | ## 第二章 IRTransformLayerを使ったモジュールの最適化 13 | 14 | このチュートリアルは鋭意執筆中です. 15 | 完全ではありませんし,たびたび,詳細が変わりうります. 16 | しかしながら,このまま読んで,試していただき,フィードバックを待ちたいと思います. 17 | 18 | ## Introduction 19 | 20 | > 注意: このチュートリアルは,最近のORCのアップデートに追いついていません.1〜2章までの文章は更新されていますが,3〜5章のコードは,コンパイル・実行できますが,更新されていません. 21 | 22 | 第二章にようこそ. 23 | このシリーズの第一章では,基本的なJITクラス`KaleidoscopeJIT`を取り上げました. 24 | `KaleidoscopeJIT`クラスは,LLVM IRモジュールを引数にとり,メモリ上に実行可能なコードを生成します. 25 | KaleidoscopeJITは,`IRCompileLayer`と`ObjectLinkingLayer`というORCのレイヤーをそのまま組み合わせることで,従来は,重労働であったのに,短いコードでこれを実現します. 26 | この章では,`KaleidoscopeJIT`にIRの最適化機能を追加するために,`IRTransformLayer`という新しいレイヤーを使って,ORCのレイヤーの概念をさらに学んでいきます. 27 | 28 | ## `IRTransformLayer`を使ったモジュールの最適化 29 | 30 | “Implementing a language with LLVM”チュートリアルの四章で,llvmの`FunctionPassManager`をLLVM IRを最適化するための手段として紹介しました. 31 | この章の細部まで読んだ読者は,それが最適化の集合体であり,`Module`上でそのPassManagerを走らせると,意味的には同じ形式で,モジュールをより最適なものに変換するものである(つまり手短に言うと,我々は,モジュールを最適化するために`llvm::FunctionPassManager`のインスタンスを作成する)理解していると思います. 32 | チュートリアルでは,`FunctionPassManager`は,`KaleidoscopeJIT`の外側で作成され,モジュールは,それが追加される前に最適化されました. 33 | この章では,代わりに,JITのタイミングで最適化するようにします. 34 | 35 | ここから,この章では,ORCのレイヤーについて学ぶ動機を解説しますが,長期的な観点から,最適化をJITの一部とすることは,重要な利益になります. 36 | つまり,遅延コンパイルを始める時に(例えば,それぞれの関数のコンパイルを初めて実行される時まで遅延するなど),JITによって最適化が管理されているということは,すべての最適化を事前に行うのではなく,最適化も遅延実行できることを意味します. 37 | 38 | JITに最適化のサポートを追加するため、`KaleidoscopeJIT`を第一章から取得し、ORCの`IRTransformLayer`をトップに組み込む。まず、`IRTransformLayer`がどのように動作するのかを見ていくことにします。しかし、そのインタフェースは、シンプルです。つまり、このレイヤーのためのコンストラクタは、execution sessionとレイヤー、さらにIR最適化関数への参照を引数に取ります。 39 | そのIR最適化関数は、`addModule`通して、それぞれの`Module`へ適応されます。 40 | 41 | ``` 42 | class KaleidoscopeJIT { 43 | private: 44 | ExecutionSession ES; 45 | RTDyldObjectLinkingLayer ObjectLayer; 46 | IRCompileLayer CompileLayer; 47 | IRTransformLayer TransformLayer; 48 | 49 | DataLayout DL; 50 | MangleAndInterner Mangle; 51 | ThreadSafeContext Ctx; 52 | 53 | public: 54 | 55 | KaleidoscopeJIT(JITTargetMachineBuilder JTMB, DataLayout DL) 56 | : ObjectLayer(ES, 57 | []() { return llvm::make_unique(); }), 58 | CompileLayer(ES, ObjectLayer, ConcurrentIRCompiler(std::move(JTMB))), 59 | TransformLayer(ES, CompileLayer, optimizeModule), 60 | DL(std::move(DL)), Mangle(ES, this->DL), 61 | Ctx(llvm::make_unique()) { 62 | ES.getMainJITDylib().setGenerator( 63 | cantFail(DynamicLibrarySearchGenerator::GetForCurrentProcess(DL))); 64 | } 65 | ``` 66 | 67 | 拡張した`KaleidoscopeJIT`クラスは,第一章と同じところから始まるが,`CompileLayer`の後に,`TransformLayer`を新しいメンバとして加える. 68 | `OptimizeLayer`を`ExecutionSession`とアウトプットレイヤー,変換の関数への参照で初期化する. 69 | 我々が使う変換の関数は,`optimizeModule`のstaticメソッドとして定義する. 70 | 71 | ``` 72 | // ... 73 | return cantFail(OptimizeLayer.addModule(std::move(M), 74 | 75 | ``` std::move(Resolver))); 76 | // ... 77 | ``` 78 | 79 | 次に,`CompileLayer::add`の呼び出しを,`OptimizeLayer::add`の呼び出しに置き換えるため,`addModule`メソッドを更新する必要がある. 80 | 81 | ``` 82 | static Expected 83 | optimizeModule(ThreadSafeModule M, const MaterializationResponsibility &R) { 84 | // Create a function pass manager. 85 | auto FPM = llvm::make_unique(M.get()); 86 | 87 | // Add some optimizations. 88 | FPM->add(createInstructionCombiningPass()); 89 | FPM->add(createReassociatePass()); 90 | FPM->add(createGVNPass()); 91 | FPM->add(createCFGSimplificationPass()); 92 | FPM->doInitialization(); 93 | 94 | // Run the optimizations over all functions in the module being added to 95 | // the JIT. 96 | for (auto &F : *M) 97 | FPM->run(F); 98 | 99 | return M; 100 | } 101 | ``` 102 | 103 | JITの最後の部分で,実際の最適化を実行するためのプライベートメソッド`optimizeModule`を追加します. 104 | この関数は,`ThreadSafeModule`型で,入力して変換されるモジュールと,新しいクラス`MaterializationResponsibility`への参照を引数に取ります. 105 | `MaterializationResponsibility`引数は,例えば,JITコンパイルされたコードが呼び出し,アクセスを試みる,モジュール内の定義と言った,モジュールを変換するために,JITの状態を問い合わせるために使われます. 106 | ここからは,この引数は無視することにして,一般的な最適化のパイプラインを使います. 107 | これをするために, `FunctionPassManager`をセットアップし,いくつかのパスをそれに追加し,モジュールにある関数ごとに最適化を実行させ,変換されたモジュールを受け取ります. 108 | 指定した最適化は,“Implementing a language with LLVM”の4章で使ったものと同じです. 109 | これらの議論や一般的なIRの最適化を深く理解するなら,その章を読み直した方がよいでしょう. 110 | 111 | `KaleidoscopeJIT`に加える変更という観点では,モジュールが`addModule`を通して追加されるとき,`OptimizeLayer`は,以下で説明する`CompileLayer`へ変換されたモジュールを渡す前に,`optimizeModule`関数を呼ぶことになります. 112 | もちろん,わざわざ`IRTransformLayer`を使わずに,`addModule`関数の中で`optimizeModule`を直接呼び出すこともできますが,そうすることで,レイヤーがどのように構成されているかを確認することができます. 113 | そして,それは,レイヤーのコンセプトにわかりやすい取っ付きを与えてくれます. 114 | なぜなら,`IRTransformLayer`は,その中でももっとも実装しやすい,シンプルなレイヤーだからです. 115 | 116 | ``` 117 | // From IRTransformLayer.h: 118 | class IRTransformLayer : public IRLayer { 119 | public: 120 | using TransformFunction = std::function( 121 | ThreadSafeModule, const MaterializationResponsibility &R)>; 122 | 123 | IRTransformLayer(ExecutionSession &ES, IRLayer &BaseLayer, 124 | TransformFunction Transform = identityTransform); 125 | 126 | void setTransform(TransformFunction Transform) { 127 | this->Transform = std::move(Transform); 128 | } 129 | 130 | static ThreadSafeModule 131 | identityTransform(ThreadSafeModule TSM, 132 | const MaterializationResponsibility &R) { 133 | return TSM; 134 | } 135 | 136 | void emit(MaterializationResponsibility R, ThreadSafeModule TSM) override; 137 | 138 | private: 139 | IRLayer &BaseLayer; 140 | TransformFunction Transform; 141 | }; 142 | ``` 143 | 144 | ``` 145 | // From IRTransfomrLayer.cpp: 146 | 147 | IRTransformLayer::IRTransformLayer(ExecutionSession &ES, 148 | IRLayer &BaseLayer, 149 | TransformFunction Transform) 150 | : IRLayer(ES), BaseLayer(BaseLayer), Transform(std::move(Transform)) {} 151 | 152 | void IRTransformLayer::emit(MaterializationResponsibility R, 153 | ThreadSafeModule TSM) { 154 | assert(TSM.getModule() && "Module must not be null"); 155 | 156 | if (auto TransformedTSM = Transform(std::move(TSM), R)) 157 | BaseLayer.emit(std::move(R), std::move(*TransformedTSM)); 158 | else { 159 | R.failMaterialization(); 160 | getExecutionSession().reportError(TransformedTSM.takeError()); 161 | } 162 | } 163 | ``` 164 | 165 | これが,`llvm/include/llvm/ExecutionEngine/Orc/IRTransformLayer.h`と`llvm/lib/ExecutionEngine/Orc/IRTransformLayer.cpp`にある`IRTransformLayer`の実装です. 166 | このクラスは,ふたつとてもシンプルな仕事にかかわっています. 167 | 一つ目は,変換関数のオブジェクトを介し,このレイヤーを通じて発行されたIRモジュールを実行することです. 168 | ふたつ目は,ORCの`IRLayer`のインタフェースを実装することです(このクラス自身がORCレイヤーのコンセプトに沿ったものになっています.詳細は後で解説します). 169 | 170 | クラスのほとんどは,安直に実装されています. 171 | 変換の関数のためのtypedef,メンバーの初期化をしてるコンストラクタ,変換関数のセッター,デフォルトの最適化なしの変換です. 172 | 最も重要なメソッドは,`emit`で,このメソッドは,`IRLayer`のインタフェースの半分を占めます. 173 | `emit`は,我々の変換をそれぞれのモジュールに施す. 174 | `emit`が呼ばれたときに,変換が成功すると,ベースレイヤーに変換されたモジュールを渡す. 175 | また,変換が失敗したときには,この関数は,そのスレッドを終了する前に,`MaterializationResponsibility::failMaterialization`(このJITクライアントは,コンパイルを待っているコードがコンパイルに失敗したことを他の待機中のスレッド上,知ることになる)を呼び,エラーログを出します. 176 | 177 | `IRLayer`のインタフェースの残りの半分は,`IRLayer`クラスから,何も変えずに流用することにします. 178 | 179 | ``` 180 | Error IRLayer::add(JITDylib &JD, ThreadSafeModule TSM, VModuleKey K) { 181 | return JD.define(llvm::make_unique( 182 | *this, std::move(K), std::move(TSM))); 183 | } 184 | ``` 185 | 186 | `llvm/lib/ExecutionEngine/Orc/Layer.cpp`からとってきたこのコードは,`ThreadSafeModule`を`MaterializationUnit`の中に(このケースでは,`BasicIRLayerMaterializationUnit`)くるんで,与えられた`JITDylib`に追加します. 187 | ほとんどのレイヤーは,`IRLayer`から派生したもので,`add`メソッドのデフォルト実装をベースにしています. 188 | 189 | `add`と`emit`というふたつの操作は,一緒にレイヤーのコンセプトを構成しています. 190 | つまり,レイヤーは,コンパイラのパイプラインの一部をラップしたものであり,そのパイプラインのAPIは,ORCに対して透過的ではないが,インタフェースを使って,必要な時にORCから呼び出すことができる. 191 | `add`メソッドは,入力として,コードの表現を`module`で受け取り(今回は,LLVM IR module),ターゲットとなる`JITDylib`に保存する.また,`add`メソッドは,受け取ったモジュール内で定義されたシンボルが要求されたときに,レイヤーの`emit`メソッドに,モジュールに引き渡すように処理する. 192 | この一連のタスクは,ベースレイヤーの`emit`メソッドを呼び出すことで,完了する. 193 | 例えば,このチュートリアルの`IRTransformLayer`は,変換されたIRをコンパイルするために,`IRCompileLayer`に引き渡し,`IRCompileLayer`は,コンパイラによって生成されたオブジェクトファイルをその場でリンクするために,`ObjectLayer`へ引き渡すといった風になる. 194 | 195 | ここまで,LLVM IRを最適化し,コンパイルする方法について学んできたが,コンパイルが必要になるタイミングについては,考えてこなかった. 196 | 我々が作ったREPLは,それぞれの関数が他のコードから参照されると,それが実行時に実際に呼ばれるかどうかにかかわらず,すぐに最適化され,コンパイルされます. 197 | 次章では,我々は,その関数が実行時に初めて呼ばれるまでコンパイルしない,完全な遅延コンパイルを紹介します. 198 | ここで,面白いトレードオフが発生する. 199 | それは,遅延コンパイルを行うと,一番最初の関数を実行するまでの時間は短縮されるが,新しい関数が呼び出されるたびにコンパイルのために一時的に処理を停止する必要が出てくることだ. 200 | もし,コード生成だけを遅延化し,最適化は念入りにやりたいのであれば,起動時にすべてを最適化するように長い処理時間を使うことになるが,関数呼び出し時には,それぞれの関数のコード生成だけをやることになるので,相対的に停止時間が短くなるかもしれない. 201 | 最適化とコード生成の両方を遅延したいときには,最初の関数呼び出しは,かなり高速化できるが,ある関数が最初に実行されたときに,最適化とコード生成の両方を行う必要があるので,停止時間は,より長くなる. 202 | インラインのような手続き間の最適化を考慮する場合は,問題はよりおもしろいものになる. 203 | これらは,複雑なトレードオフであり,それらに合うすべての解決方法は存在しないが,構成可能なレイヤーを提供することで,JITを実装しようとする人に,その決定権を渡すことはできる. 204 | そして,その実装する人は,レイヤーをうまく組み合わせ,異なる状況下での実験を簡単に組み立てられる. 205 | 206 | ## コードリスト 207 | 208 | # コンパイル 209 | 210 | ``` 211 | clang++ -g toy.cpp `llvm-config --cxxflags --ldflags --system-libs --libs core orcjit native` -O3 -o toy 212 | ``` 213 | 214 | # 実行 215 | 216 | ``` 217 | ./toy 218 | ``` 219 | 220 | ``` 221 | //===- KaleidoscopeJIT.h - A simple JIT for Kaleidoscope --------*- C++ -*-===// 222 | // 223 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 224 | // See https://llvm.org/LICENSE.txt for license information. 225 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 226 | // 227 | //===----------------------------------------------------------------------===// 228 | // 229 | // Contains a simple JIT definition for use in the kaleidoscope tutorials. 230 | // 231 | //===----------------------------------------------------------------------===// 232 | 233 | #ifndef LLVM_EXECUTIONENGINE_ORC_KALEIDOSCOPEJIT_H 234 | #define LLVM_EXECUTIONENGINE_ORC_KALEIDOSCOPEJIT_H 235 | 236 | #include "llvm/ADT/StringRef.h" 237 | #include "llvm/ExecutionEngine/JITSymbol.h" 238 | #include "llvm/ExecutionEngine/Orc/CompileUtils.h" 239 | #include "llvm/ExecutionEngine/Orc/Core.h" 240 | #include "llvm/ExecutionEngine/Orc/ExecutionUtils.h" 241 | #include "llvm/ExecutionEngine/Orc/IRCompileLayer.h" 242 | #include "llvm/ExecutionEngine/Orc/IRTransformLayer.h" 243 | #include "llvm/ExecutionEngine/Orc/JITTargetMachineBuilder.h" 244 | #include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h" 245 | #include "llvm/ExecutionEngine/SectionMemoryManager.h" 246 | #include "llvm/IR/DataLayout.h" 247 | #include "llvm/IR/LLVMContext.h" 248 | #include "llvm/IR/LegacyPassManager.h" 249 | #include "llvm/Transforms/InstCombine/InstCombine.h" 250 | #include "llvm/Transforms/Scalar.h" 251 | #include "llvm/Transforms/Scalar/GVN.h" 252 | #include 253 | 254 | namespace llvm { 255 | namespace orc { 256 | 257 | class KaleidoscopeJIT { 258 | private: 259 | ExecutionSession ES; 260 | RTDyldObjectLinkingLayer ObjectLayer; 261 | IRCompileLayer CompileLayer; 262 | IRTransformLayer OptimizeLayer; 263 | 264 | DataLayout DL; 265 | MangleAndInterner Mangle; 266 | ThreadSafeContext Ctx; 267 | 268 | public: 269 | KaleidoscopeJIT(JITTargetMachineBuilder JTMB, DataLayout DL) 270 | : ObjectLayer(ES, 271 | []() { return llvm::make_unique(); }), 272 | CompileLayer(ES, ObjectLayer, ConcurrentIRCompiler(std::move(JTMB))), 273 | OptimizeLayer(ES, CompileLayer, optimizeModule), 274 | DL(std::move(DL)), Mangle(ES, this->DL), 275 | Ctx(llvm::make_unique()) { 276 | ES.getMainJITDylib().setGenerator( 277 | cantFail(DynamicLibrarySearchGenerator::GetForCurrentProcess( 278 | DL.getGlobalPrefix()))); 279 | } 280 | 281 | const DataLayout &getDataLayout() const { return DL; } 282 | 283 | LLVMContext &getContext() { return *Ctx.getContext(); } 284 | 285 | static Expected> Create() { 286 | auto JTMB = JITTargetMachineBuilder::detectHost(); 287 | 288 | if (!JTMB) 289 | return JTMB.takeError(); 290 | 291 | auto DL = JTMB->getDefaultDataLayoutForTarget(); 292 | if (!DL) 293 | return DL.takeError(); 294 | 295 | return llvm::make_unique(std::move(*JTMB), std::move(*DL)); 296 | } 297 | 298 | Error addModule(std::unique_ptr M) { 299 | return OptimizeLayer.add(ES.getMainJITDylib(), 300 | ThreadSafeModule(std::move(M), Ctx)); 301 | } 302 | 303 | Expected lookup(StringRef Name) { 304 | return ES.lookup({&ES.getMainJITDylib()}, Mangle(Name.str())); 305 | } 306 | 307 | private: 308 | static Expected 309 | optimizeModule(ThreadSafeModule TSM, const MaterializationResponsibility &R) { 310 | // Create a function pass manager. 311 | auto FPM = llvm::make_unique(TSM.getModule()); 312 | 313 | // Add some optimizations. 314 | FPM->add(createInstructionCombiningPass()); 315 | FPM->add(createReassociatePass()); 316 | FPM->add(createGVNPass()); 317 | FPM->add(createCFGSimplificationPass()); 318 | FPM->doInitialization(); 319 | 320 | // Run the optimizations over all functions in the module being added to 321 | // the JIT. 322 | for (auto &F : *TSM.getModule()) 323 | FPM->run(F); 324 | 325 | return TSM; 326 | } 327 | }; 328 | 329 | } // end namespace orc 330 | } // end namespace llvm 331 | 332 | #endif // LLVM_EXECUTIONENGINE_ORC_KALEIDOSCOPEJIT_H 333 | ``` -------------------------------------------------------------------------------- /KaleidoscopeJP/src/chap02/main.cpp: -------------------------------------------------------------------------------- 1 | #include "llvm/ADT/STLExtras.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | //===----------------------------------------------------------------------===// 12 | // Lexer 13 | //===----------------------------------------------------------------------===// 14 | 15 | // The lexer returns tokens [0-255] if it is an unknown character, otherwise one 16 | // of these for known things. 17 | enum Token { 18 | tok_eof = -1, 19 | 20 | // commands 21 | tok_def = -2, 22 | tok_extern = -3, 23 | 24 | // primary 25 | tok_identifier = -4, 26 | tok_number = -5 27 | }; 28 | 29 | static std::string IdentifierStr; // Filled in if tok_identifier 30 | static double NumVal; // Filled in if tok_number 31 | 32 | /// gettok - Return the next token from standard input. 33 | static int gettok() { 34 | static int LastChar = ' '; 35 | 36 | // Skip any whitespace. 37 | while (isspace(LastChar)) 38 | LastChar = getchar(); 39 | 40 | if (isalpha(LastChar)) { // identifier: [a-zA-Z][a-zA-Z0-9]* 41 | IdentifierStr = LastChar; 42 | while (isalnum((LastChar = getchar()))) 43 | IdentifierStr += LastChar; 44 | 45 | if (IdentifierStr == "def") 46 | return tok_def; 47 | if (IdentifierStr == "extern") 48 | return tok_extern; 49 | return tok_identifier; 50 | } 51 | 52 | if (isdigit(LastChar) || LastChar == '.') { // Number: [0-9.]+ 53 | std::string NumStr; 54 | do { 55 | NumStr += LastChar; 56 | LastChar = getchar(); 57 | } while (isdigit(LastChar) || LastChar == '.'); 58 | 59 | NumVal = strtod(NumStr.c_str(), nullptr); 60 | return tok_number; 61 | } 62 | 63 | if (LastChar == '#') { 64 | // Comment until end of line. 65 | do 66 | LastChar = getchar(); 67 | while (LastChar != EOF && LastChar != '\n' && LastChar != '\r'); 68 | 69 | if (LastChar != EOF) 70 | return gettok(); 71 | } 72 | 73 | // Check for end of file. Don't eat the EOF. 74 | if (LastChar == EOF) 75 | return tok_eof; 76 | 77 | // Otherwise, just return the character as its ascii value. 78 | int ThisChar = LastChar; 79 | LastChar = getchar(); 80 | return ThisChar; 81 | } 82 | 83 | //===----------------------------------------------------------------------===// 84 | // Abstract Syntax Tree (aka Parse Tree) 85 | //===----------------------------------------------------------------------===// 86 | 87 | namespace { 88 | 89 | /// ExprAST - Base class for all expression nodes. 90 | class ExprAST { 91 | public: 92 | virtual ~ExprAST() = default; 93 | }; 94 | 95 | /// NumberExprAST - Expression class for numeric literals like "1.0". 96 | class NumberExprAST : public ExprAST { 97 | double Val; 98 | 99 | public: 100 | NumberExprAST(double Val) : Val(Val) {} 101 | }; 102 | 103 | /// VariableExprAST - Expression class for referencing a variable, like "a". 104 | class VariableExprAST : public ExprAST { 105 | std::string Name; 106 | 107 | public: 108 | VariableExprAST(const std::string &Name) : Name(Name) {} 109 | }; 110 | 111 | /// BinaryExprAST - Expression class for a binary operator. 112 | class BinaryExprAST : public ExprAST { 113 | char Op; 114 | std::unique_ptr LHS, RHS; 115 | 116 | public: 117 | BinaryExprAST(char Op, std::unique_ptr LHS, 118 | std::unique_ptr RHS) 119 | : Op(Op), LHS(std::move(LHS)), RHS(std::move(RHS)) {} 120 | }; 121 | 122 | /// CallExprAST - Expression class for function calls. 123 | class CallExprAST : public ExprAST { 124 | std::string Callee; 125 | std::vector> Args; 126 | 127 | public: 128 | CallExprAST(const std::string &Callee, 129 | std::vector> Args) 130 | : Callee(Callee), Args(std::move(Args)) {} 131 | }; 132 | 133 | /// PrototypeAST - This class represents the "prototype" for a function, 134 | /// which captures its name, and its argument names (thus implicitly the number 135 | /// of arguments the function takes). 136 | class PrototypeAST { 137 | std::string Name; 138 | std::vector Args; 139 | 140 | public: 141 | PrototypeAST(const std::string &Name, std::vector Args) 142 | : Name(Name), Args(std::move(Args)) {} 143 | 144 | const std::string &getName() const { return Name; } 145 | }; 146 | 147 | /// FunctionAST - This class represents a function definition itself. 148 | class FunctionAST { 149 | std::unique_ptr Proto; 150 | std::unique_ptr Body; 151 | 152 | public: 153 | FunctionAST(std::unique_ptr Proto, 154 | std::unique_ptr Body) 155 | : Proto(std::move(Proto)), Body(std::move(Body)) {} 156 | }; 157 | 158 | } // end anonymous namespace 159 | 160 | //===----------------------------------------------------------------------===// 161 | // Parser 162 | //===----------------------------------------------------------------------===// 163 | 164 | /// CurTok/getNextToken - Provide a simple token buffer. CurTok is the current 165 | /// token the parser is looking at. getNextToken reads another token from the 166 | /// lexer and updates CurTok with its results. 167 | static int CurTok; 168 | static int getNextToken() { return CurTok = gettok(); } 169 | 170 | /// BinopPrecedence - This holds the precedence for each binary operator that is 171 | /// defined. 172 | static std::map BinopPrecedence; 173 | 174 | /// GetTokPrecedence - Get the precedence of the pending binary operator token. 175 | static int GetTokPrecedence() { 176 | if (!isascii(CurTok)) 177 | return -1; 178 | 179 | // Make sure it's a declared binop. 180 | int TokPrec = BinopPrecedence[CurTok]; 181 | if (TokPrec <= 0) 182 | return -1; 183 | return TokPrec; 184 | } 185 | 186 | /// LogError* - These are little helper functions for error handling. 187 | std::unique_ptr LogError(const char *Str) { 188 | fprintf(stderr, "Error: %s\n", Str); 189 | return nullptr; 190 | } 191 | std::unique_ptr LogErrorP(const char *Str) { 192 | LogError(Str); 193 | return nullptr; 194 | } 195 | 196 | static std::unique_ptr ParseExpression(); 197 | 198 | /// numberexpr ::= number 199 | static std::unique_ptr ParseNumberExpr() { 200 | auto Result = llvm::make_unique(NumVal); 201 | getNextToken(); // consume the number 202 | return std::move(Result); 203 | } 204 | 205 | /// parenexpr ::= '(' expression ')' 206 | static std::unique_ptr ParseParenExpr() { 207 | getNextToken(); // eat (. 208 | auto V = ParseExpression(); 209 | if (!V) 210 | return nullptr; 211 | 212 | if (CurTok != ')') 213 | return LogError("expected ')'"); 214 | getNextToken(); // eat ). 215 | return V; 216 | } 217 | 218 | /// identifierexpr 219 | /// ::= identifier 220 | /// ::= identifier '(' expression* ')' 221 | static std::unique_ptr ParseIdentifierExpr() { 222 | std::string IdName = IdentifierStr; 223 | 224 | getNextToken(); // eat identifier. 225 | 226 | if (CurTok != '(') // Simple variable ref. 227 | return llvm::make_unique(IdName); 228 | 229 | // Call. 230 | getNextToken(); // eat ( 231 | std::vector> Args; 232 | if (CurTok != ')') { 233 | while (true) { 234 | if (auto Arg = ParseExpression()) 235 | Args.push_back(std::move(Arg)); 236 | else 237 | return nullptr; 238 | 239 | if (CurTok == ')') 240 | break; 241 | 242 | if (CurTok != ',') 243 | return LogError("Expected ')' or ',' in argument list"); 244 | getNextToken(); 245 | } 246 | } 247 | 248 | // Eat the ')'. 249 | getNextToken(); 250 | 251 | return llvm::make_unique(IdName, std::move(Args)); 252 | } 253 | 254 | /// primary 255 | /// ::= identifierexpr 256 | /// ::= numberexpr 257 | /// ::= parenexpr 258 | static std::unique_ptr ParsePrimary() { 259 | switch (CurTok) { 260 | default: 261 | return LogError("unknown token when expecting an expression"); 262 | case tok_identifier: 263 | return ParseIdentifierExpr(); 264 | case tok_number: 265 | return ParseNumberExpr(); 266 | case '(': 267 | return ParseParenExpr(); 268 | } 269 | } 270 | 271 | /// binoprhs 272 | /// ::= ('+' primary)* 273 | static std::unique_ptr ParseBinOpRHS(int ExprPrec, 274 | std::unique_ptr LHS) { 275 | // If this is a binop, find its precedence. 276 | while (true) { 277 | int TokPrec = GetTokPrecedence(); 278 | 279 | // If this is a binop that binds at least as tightly as the current binop, 280 | // consume it, otherwise we are done. 281 | if (TokPrec < ExprPrec) 282 | return LHS; 283 | 284 | // Okay, we know this is a binop. 285 | int BinOp = CurTok; 286 | getNextToken(); // eat binop 287 | 288 | // Parse the primary expression after the binary operator. 289 | auto RHS = ParsePrimary(); 290 | if (!RHS) 291 | return nullptr; 292 | 293 | // If BinOp binds less tightly with RHS than the operator after RHS, let 294 | // the pending operator take RHS as its LHS. 295 | int NextPrec = GetTokPrecedence(); 296 | if (TokPrec < NextPrec) { 297 | RHS = ParseBinOpRHS(TokPrec + 1, std::move(RHS)); 298 | if (!RHS) 299 | return nullptr; 300 | } 301 | 302 | // Merge LHS/RHS. 303 | LHS = llvm::make_unique(BinOp, std::move(LHS), 304 | std::move(RHS)); 305 | } 306 | } 307 | 308 | /// expression 309 | /// ::= primary binoprhs 310 | /// 311 | static std::unique_ptr ParseExpression() { 312 | auto LHS = ParsePrimary(); 313 | if (!LHS) 314 | return nullptr; 315 | 316 | return ParseBinOpRHS(0, std::move(LHS)); 317 | } 318 | 319 | /// prototype 320 | /// ::= id '(' id* ')' 321 | static std::unique_ptr ParsePrototype() { 322 | if (CurTok != tok_identifier) 323 | return LogErrorP("Expected function name in prototype"); 324 | 325 | std::string FnName = IdentifierStr; 326 | getNextToken(); 327 | 328 | if (CurTok != '(') 329 | return LogErrorP("Expected '(' in prototype"); 330 | 331 | std::vector ArgNames; 332 | while (getNextToken() == tok_identifier) 333 | ArgNames.push_back(IdentifierStr); 334 | if (CurTok != ')') 335 | return LogErrorP("Expected ')' in prototype"); 336 | 337 | // success. 338 | getNextToken(); // eat ')'. 339 | 340 | return llvm::make_unique(FnName, std::move(ArgNames)); 341 | } 342 | 343 | /// definition ::= 'def' prototype expression 344 | static std::unique_ptr ParseDefinition() { 345 | getNextToken(); // eat def. 346 | auto Proto = ParsePrototype(); 347 | if (!Proto) 348 | return nullptr; 349 | 350 | if (auto E = ParseExpression()) 351 | return llvm::make_unique(std::move(Proto), std::move(E)); 352 | return nullptr; 353 | } 354 | 355 | /// toplevelexpr ::= expression 356 | static std::unique_ptr ParseTopLevelExpr() { 357 | if (auto E = ParseExpression()) { 358 | // Make an anonymous proto. 359 | auto Proto = llvm::make_unique("__anon_expr", std::vector()); 360 | return llvm::make_unique(std::move(Proto), std::move(E)); 361 | } 362 | return nullptr; 363 | } 364 | 365 | /// external ::= 'extern' prototype 366 | static std::unique_ptr ParseExtern() { 367 | getNextToken(); // eat extern. 368 | return ParsePrototype(); 369 | } 370 | 371 | //===----------------------------------------------------------------------===// 372 | // Top-Level parsing 373 | //===----------------------------------------------------------------------===// 374 | 375 | static void HandleDefinition() { 376 | if (ParseDefinition()) { 377 | fprintf(stderr, "Parsed a function definition.\n"); 378 | } else { 379 | // Skip token for error recovery. 380 | getNextToken(); 381 | } 382 | } 383 | 384 | static void HandleExtern() { 385 | if (ParseExtern()) { 386 | fprintf(stderr, "Parsed an extern\n"); 387 | } else { 388 | // Skip token for error recovery. 389 | getNextToken(); 390 | } 391 | } 392 | 393 | static void HandleTopLevelExpression() { 394 | // Evaluate a top-level expression into an anonymous function. 395 | if (ParseTopLevelExpr()) { 396 | fprintf(stderr, "Parsed a top-level expr\n"); 397 | } else { 398 | // Skip token for error recovery. 399 | getNextToken(); 400 | } 401 | } 402 | 403 | /// top ::= definition | external | expression | ';' 404 | static void MainLoop() { 405 | while (true) { 406 | fprintf(stderr, "ready> "); 407 | switch (CurTok) { 408 | case tok_eof: 409 | return; 410 | case ';': // ignore top-level semicolons. 411 | getNextToken(); 412 | break; 413 | case tok_def: 414 | HandleDefinition(); 415 | break; 416 | case tok_extern: 417 | HandleExtern(); 418 | break; 419 | default: 420 | HandleTopLevelExpression(); 421 | break; 422 | } 423 | } 424 | } 425 | 426 | //===----------------------------------------------------------------------===// 427 | // Main driver code. 428 | //===----------------------------------------------------------------------===// 429 | 430 | int main() { 431 | // Install standard binary operators. 432 | // 1 is lowest precedence. 433 | BinopPrecedence['<'] = 10; 434 | BinopPrecedence['+'] = 20; 435 | BinopPrecedence['-'] = 20; 436 | BinopPrecedence['*'] = 40; // highest. 437 | 438 | // Prime the first token. 439 | fprintf(stderr, "ready> "); 440 | getNextToken(); 441 | 442 | // Run the main "interpreter loop" now. 443 | MainLoop(); 444 | 445 | return 0; 446 | } -------------------------------------------------------------------------------- /KaleidoscopeJP/chap03.md: -------------------------------------------------------------------------------- 1 | # LLVM IRのためのコード生成 2 | 3 | ## はじめに 4 | 5 | 3章にようこそ. 6 | 本章では,抽象構文木をLLVM IRにどうやって変換するかを説明する. 7 | ここでは,LLVMがどのように何をしているか?について多少なりとともに,それがいかに使いやすいかを解説することになる. 8 | LLVM IRを生成するよりも,lexerやパーサを作る方がよっぽど骨の折れる作業なのである. 9 | 10 | ## コード生成のためのセットアップ 11 | 12 | LLVM IRを生成するために,まず,シンプルなセットアップを実行する必要がある. 13 | はじめに,それぞれのASTクラスにコード生成のための仮想関数(`codegen`)を定義する. 14 | 15 | ``` 16 | /// ExprAST - Base class for all expression nodes. 17 | class ExprAST 18 | { 19 | public: 20 | virtual ~ExprAST() {} 21 | virtual Value *codegen() = 0; 22 | }; 23 | 24 | /// NumberExprAST - Expression class for numeric literals like "1.0". 25 | class NumberExprAST : public ExprAST 26 | { 27 | double Val; 28 | 29 | public: 30 | NumberExprAST(double Val) : Val(Val) {} 31 | virtual Value *codegen(); 32 | }; 33 | ... 34 | ``` 35 | 36 | `codegen()`は,ASTノードに沿い,依存しながら,IRを出力する. 37 | そして,出力は,すべて`LLVM Value`オブジェクトである. 38 | `Value`は,"静的単一代入レジスタ(Static Single Assignmentレジスタ)"あるいは,LLVMにおけるSSA型の値を表現するために使われるクラスである. 39 | SSA値型の厳密な捉え方は,それらの値は,関連する指示実行のように計算され,指示が再度実行されない限り,新しい値がセットされることはない. 40 | 言い換えると,SSA値型を変化させる方法はない. 41 | 詳しくは,"静的単一代入(Static Single Assignment)"を読んでいただきたい. 42 | SSAのコンセプトは,非常に自然なので,完全に理解するのは難しくない. 43 | 44 | `ExprAST`クラスの階層に仮想関数を追加する代わりに,ビジターパターンを使うこともできるし,それ以外の方法で実装しても構わない. 45 | 繰り返すが,このチュートリアルは,ソフトウェアエンジニアのベストプラクティスを目指したものではない. 46 | 今回の目的に対しては,仮想関数を使うのがもっともシンプルだと考える. 47 | 48 | 二つ目に必要なものは,パーサを作るときにも使った`LogError`関数だ. 49 | この関数は,コード生成のときにエラーを出力するために使う. 50 | 51 | ``` 52 | static LLVMContext TheContext; 53 | static IRBuilder<> Builder(TheContext); 54 | static std::unique_ptr TheModule; 55 | static std::map NamedValues; 56 | 57 | Value *LogErrorV(const char *Str) { 58 | LogError(Str); 59 | return nullptr; 60 | } 61 | ``` 62 | 63 | コード生成の間に静的変数を使うことになる. 64 | `TheContext`は,llvmのコアのデータ構造や型,定数テーブルなど多くの要素を保持する隠蔽されたオブジェクトである. 65 | 利用するためには,ただ一つのインスタンスを生成し,APIにそれを引き渡す必要性だけがあることを知っておけば,その詳細について,詳しく理解する必要性はない. 66 | 67 | `Builder`オブジェクトは,llvm命令セットの生成を簡単にするためのヘルパーオブジェクトである. 68 | `IRBuilder`クラステンプレートのインスタンスは,命令を追加した場所を追跡したり,新しい命令セットを作るためのメソッドを持つ. 69 | 70 | `TheModule`は,関数やグローバル変数を保持するllvmの構造体である. 71 | 色々な方法で,それは,LLVM IRがコードを保持するために利用するトップレベル構造である. 72 | この構造体は,我々が生成するIRのオブジェクトのすべてのメモリを保持することになる. 73 | ゆえに,`codegen`関数が返す値は,ユニークポインタではなく,ただの名前のポインタである. 74 | **IRのオブジェクトは,llvmがどうやらメモリ管理するようである.** 75 | 76 | `NamedValues`マップは,現在のスコープで定義されている値と,それらがLLVMでどう表現されているかを追跡するためにある. 77 | Kaleidscopeのこの形式では,参照されうるのは,関数のパラメータのみである. 78 | 関数のパラメータは,その関数の実装のためのコードが生成されるとき,このマップに作られる. 79 | 80 | これらの基本を押さえた上で,それぞれの表現に対するコード生成の仕方について説明していくことにする. 81 | これが`Builder`が何かにコードを生成するために生成されたように仮定していることに注意してほしい. 82 | 今,セットアップはすでに完了しており,それを使ってコードを生成していく. 83 | 84 | ## 表現からコードを生成する 85 | 86 | 表現ノードに対するllvmコードを生成することは,とても愚直である. 87 | つまり,コメントを入れても45行足らずのコードで,4つの主表現のノードからllvmコードを生成することができる. 88 | 89 | ``` 90 | Value *NumberExprAST::codegen() { 91 | return ConstantFP::get(TheContext, APFloat(Val)); 92 | } 93 | ``` 94 | 95 | LLVM IRにおいて,定数の数値は,`ConstantFP`クラスで表現される. 96 | `ConstantFP`クラスは,内部的に`APFloat`の中に数値を保存する(`APFloat`は,任意の精度で浮動小数点の値を保持できる能力を持つ). 97 | このコードは,基本的に`ConstantFP`のインスタンスを返す. 98 | LLVM IRでは,定数は,すべてユニークでかつ共有される. 99 | **このため,このAPIは,新しいインスタンスを返すときに,`new`や`create`にのようなメソッドや文法ではなく,`foo::get(…)`というメソッドを使っているのである(つまりこの場でインスタンスが生成されたとは限らないということ).** 100 | 101 | ``` 102 | Value *VariableExprAST::codegen() { 103 | // Look this variable up in the function. 104 | Value *V = NamedValues[Name]; 105 | if (!V) 106 | LogErrorV("Unknown variable name"); 107 | return V; 108 | } 109 | ``` 110 | 111 | 変数への参照もまた,llvmを使うとシンプルに実装できる. 112 | Kaleidoscopeのシンプルバージョンでは,値は,どこかで生成され,その値は常に有効であると仮定する. 113 | 実践的には,`NamedValues`マップにある値だけが関数の引数である. 114 | このコードは,簡単に指定された名前が,マップに含まれているかを(あるいは,含まれていないか,全く知らない変数が参照されているか)確認する. 115 | そして,その値を返す. 116 | 将来的には,ループの中だけで有効な変数やローカル変数もサポートする. 117 | 118 | ``` 119 | Value *BinaryExprAST::codegen() { 120 | Value *L = LHS->codegen(); 121 | Value *R = RHS->codegen(); 122 | if (!L || !R) 123 | return nullptr; 124 | switch (Op) { 125 | case '+': 126 | return Builder.CreateFAdd(L, R, "addtmp"); 127 | case '-': 128 | return Builder.CreateFSub(L, R, "subtmp"); 129 | case '*': 130 | return Builder.CreateFMul(L, R, "multmp"); 131 | case '<': 132 | L = Builder.CreateFCmpULT(L, R, "cmptmp"); 133 | // Convert bool 0/1 to double 0.0 or 1.0 134 | return Builder.CreateUIToFP( 135 | L, 136 | Type::getDoubleTy(TheContext), 137 | "booltmp" 138 | ); 139 | default: 140 | return LogErrorV("invalid binary operator"); 141 | } 142 | } 143 | ``` 144 | 145 | 二項演算子からのコード生成は,ちょっとおもしろい. 146 | ここでの,基本的なアイデアは,表現の左辺のためのコードを再帰的に生成し,そのあと,右辺のコードを生成し,最終的に二項演算の表現のためのコード生成を完了する. 147 | このコードでは,単純なswitch文で,二項演算子の右辺のinstructionを生成する. 148 | 149 | 上の例では,llvmビルダークラスは,その値を表示し始める. 150 | `IRBuilder`は,新しく生成された命令をどこに挿入すべきかを知っている. 151 | ここであなたがすべきことは,どの命令を作るべきか,どのオペランドを使うべきか,生成される命令の名前を補助的に提供したりすることである. 152 | 153 | llvmのいいところは,名前自体がヒントになっている点である. 154 | 例えば,もし上のコードが,複数の`addtmp`変数を生成するなら,llvmは,自動的に末尾の番号をインクリメントしながら,それらに自動的に名前を割り振っていく. 155 | **命令に対するローカル変数の名前は,純粋に任意だが,IRがダンプされたときに読みやすいようにしておくべきである.** 156 | 157 | llvm命令は,厳密なルールに従わなければならない.例えば,加算命令の`Left`と`Right`のオペレータは,同じ型を持たねばならず,加算の結果を保存する型と,オペレータの型も同じでなければならない. 158 | Kaleidoscopeのすべての変数は,double型なので,これらの制約をあまり考える必要はない. 159 | 160 | 一方で,llvmでは,fcmp命令は,いつも`i1`型(1ビットの整数値)を返すことになっている. 161 | これに付随する問題は,Kaleidoscopeは,値が0.0から1.0の値しか持たないということである. 162 | つまり,double型しかサポートしないためである. 163 | これらの文法を理解するため,我々は,fcmp命令に,uitofp命令をくっつけて使う. 164 | uitofp命令は,入力の整数値を,それを符号なし浮動小数点値に変換する. 165 | 対照的に,もし我々がsiotfp命令を使うときは,Kaleidoscopeの'<'オペレータは,その入力の値に依存して,0.0と-1.0を返す. 166 | 167 | ``` 168 | Value *CallExprAST::codegen() { 169 | // Look up the name in the global module table. 170 | Function *CalleeF = TheModule->getFunction(Callee); 171 | if (!CalleeF) 172 | return LogErrorV("Unknown function referenced"); 173 | 174 | // If argument mismatch error. 175 | if (CalleeF->arg_size() != Args.size()) 176 | return LogErrorV("Incorrect # arguments passed"); 177 | 178 | std::vector ArgsV; 179 | for (unsigned i = 0, e = Args.size(); i != e; ++i) { 180 | ArgsV.push_back(Args[i]->codegen()); 181 | if (!ArgsV.back()) 182 | return nullptr; 183 | } 184 | 185 | return Builder.CreateCall(CalleeF, ArgsV, "calltmp"); 186 | } 187 | ``` 188 | 189 | 関数呼び出しのためのコード生成は,率直に実装できる. 190 | 上のコードは,最初にllvmのモジュールシンボルテーブルから,関数名を見つけ出す. 191 | llvmモジュールは,我々がJITで実行する関数を保存するコンテナであることを思い出そう. 192 | それぞれ,ユーザが指定しているものと同じ名前関数が与えられえおり,我々は,llvmシンボルテーブルを使って,関数の名前を解決する. 193 | 194 | ここまでで,4つの基本的な表現を処理してきた. 195 | llvmでは,これ以上のことを付け加えるのも簡単だ. 196 | 197 | ## 関数コード生成 198 | 199 | プロトタイプ宣言や関数のためのコード生成は、細かいことをたくさん処理しなければならないため、ここまでの表現からコード生成するためのコードよりも、汚くなってしまう。 200 | しかし、いくつかの重要な点を説明させてもらいたい。 201 | はじめに、関数宣言のためのコード生成について説明する。 202 | すなわち、それは、関数の実装と、外部のライブラリにある関数宣言のためのコードである。 203 | 204 | ``` 205 | Function *PrototypeAST::codegen() { 206 | // Make the function type: double(double,double) etc. 207 | std::vector Doubles( 208 | Args.size(), 209 | Type::getDoubleTy(TheContext) 210 | ); 211 | FunctionType *FT = FunctionType::get( 212 | Type::getDoubleTy(TheContext), 213 | Doubles, 214 | false 215 | ); 216 | Function *F = Function::Create( 217 | FT, 218 | Function::ExternalLinkage, 219 | Name, 220 | TheModule 221 | ); 222 | ``` 223 | 224 | この中の2,3行のコードの中に多くの重要な要素が詰め込まれている。 225 | まず、この関数は、`Value*`ではなく、`Function*`を返す。 226 | プロトタイプは、関数のための外部とのインタフェースを提供するため、コード生成時には、関数に対応するLLVM Functionを返すと考えると理解しやすい。 227 | 228 | `FunctionType::get`の呼び出しは、与えられたプロトタイプのために使われる`FunctionType`を作成する。 229 | Kaleidoscopeにおけるすべての関数の引数は、`double`型であるため、初めの行は、`LLVM double`型であるNのベクトルを作る。 230 | そのとき、コードは、(可変長引数ではない、つまり`false`パラメータがこれを意味する)引数としてNという`double`型を取り、戻り値として`double`を一つ返し、関数型を作るための`FunctionType::get`メソッドを使う。 231 | LLVMにおける型は、定数と同じようにユニークではないといけないため、型はnewするのではなく、LLVMランタイムが作った型をgetすることになる。 232 | 233 | 最終行で、プロトタイプに対応するIR関数を作る。 234 | これは、関数に利用される型、リンク、名前に加えて,関数がどのモジュールに挿入されるかを明らかにする. 235 | "External linkage"は,現在のモジュール外で関数が定義される,あるいは,モジュール外の関数から呼ばれることを意味する. 236 | ここで渡される名前は,ユーザが指定する名前であり,`TheModule`が指定されるため,この名前は,`TheModule`のシンボルテーブルに登録されることになる. 237 | 238 | ``` 239 | // Set names for all arguments. 240 | unsigned Idx = 0; 241 | for (auto &Arg : F->args()) 242 | Arg.setName(Args[Idx++]); 243 | return F; 244 | ``` 245 | 246 | 最後に,プロトタイプ内で与えられた名前に従って,関数の引数それぞれの名前をセットする. 247 | このステップは,厳密には必要ではないが,命名規則に一貫性を持たせるとIRを読みやすくし,後に続くコードがその名前を引数として考えられるにする.これは,**プロトタイプの解析木で名前を見つけなければならないからそうするという意味ではない**のである. 248 | 249 | この点において,関数のプロトタイプは,実体を持っていない. 250 | これは,LLVM IRが関数宣言をどう表現するかを示している. 251 | Kaleidoscopeにおける`extern`をサポートするにためにこうする必要がある. 252 | しかし,関数定義は,コードを生成し,関数の実態をアタッチする必要がある. 253 | 254 | ``` 255 | Function *FunctionAST::codegen() { 256 | // First, check for an existing function from a previous 'extern' declaration. 257 | Function *TheFunction = TheModule->getFunction( 258 | Proto->getName() 259 | ); 260 | if (!TheFunction) 261 | TheFunction = Proto->codegen(); 262 | if (!TheFunction) 263 | return nullptr; 264 | if (!TheFunction->empty()) 265 | return (Function*)LogErrorV("Function cannot be redefined."); 266 | ``` 267 | 268 | 関数宣言のため,この指定された関数がすでに`TheModule`のシンボルテーブルに存在しないかを確認する. 269 | このケースの場合には,一つの関数は,すでに`extern`を使って作られている. 270 | `Module::getFunction`は,事前に関数が存在しない場合は,`null`を返し,`Prototype`から,関数のコードを生成する. 271 | そうではない場合,まだ実体が作られていない状態などの関数が空であることを明言しないといけない. 272 | 273 | ``` 274 | // Create a new basic block to start insertion into. 275 | BasicBlock *BB = BasicBlock::Create(TheContext, "entry", TheFunction); 276 | Builder.SetInsertPoint(BB); 277 | 278 | // Record the function arguments in the NamedValues map. 279 | NamedValues.clear(); 280 | for (auto &Arg : TheFunction->args()) 281 | NamedValues[Arg.getName()] = &Arg; 282 | ``` 283 | 284 | 今,`Builder`のセットアップが完了するところまできた. 285 | 初めの行は,"entry"という名前の新しい`basic block`を作る. 286 | そして,このブロックは,`TheFunction`に挿入される. 287 | 二行目は,builderに新しい命令を新しいブロックの終わりに挿入すべきであることを伝えている. 288 | LLVMにおけるBasic blocksは,制御フローグラフを定義する関数群の重要なパートとなる. 289 | 現在,我々は,制御フローを持たないので,この関数は,このポイントに,ひとつだけブロックを持つことになる. 290 | この制御フローの問題は,5章でさらに改善していく. 291 | 292 | 次,`NamedValues`マップに関数の引数を追加する(はじめにマップをクリアした後). 293 | なので,引数は,`VariableExprAST`ノードからアクセス可能なのである. 294 | 295 | ``` 296 | if (Value *RetVal = Body->codegen()) { 297 | // Finish off the function. 298 | Builder.CreateRet(RetVal); 299 | 300 | // Validate the generated code, checking for consistency. 301 | verifyFunction(*TheFunction); 302 | 303 | return TheFunction; 304 | } 305 | ``` 306 | 307 | 一度,挿入するべきポイントがセットアップされ,`NamedValues`マップにデータが入力されると,次に,関数のルート表現のための`codegen()`を呼び出す. 308 | もしエラーがなければ,このコードは,エントリーブロックに表現を計算するためのコードを出力し,計算すべき値を返す. 309 | エラーを仮定しないとき,我々は,関数を完結するLLVM ret 命令を作成する. 310 | 一度,関数が作られると,我々は,LLVMから提供される`verifyFunction`をコールする. 311 | この関数は,我々の作ったコンパイラがすべてを正しく実行しているかをチェックするため,生成されたコードの多様な一貫性のあるチェックを実行する. 312 | この関数は,色々なバグを検出してくれるので,非常に重要である. 313 | 一度,関数のチェックが終了すると,それを返す. 314 | 315 | ``` 316 | // Error reading body, remove function. 317 | TheFunction->eraseFromParent(); 318 | return nullptr; 319 | } 320 | ``` 321 | 322 | ここでやり残したことは,エラーハンドリングである. 323 | 簡単のため,我々は,`eraseFromParent`を呼び出して,愚直に関数自体を消すことでエラーをハンドリングすることにする. 324 | これは,ユーザにコードが出てくる前にミスタイプした関数を再定義することを許してしまう.つまり,エラーが出た関数を消さない場合,シンボルテーブルは生き続け,その後に再定義できなくしてしまう. 325 | 326 | このコードには,バグがある. 327 | つまり,`FunctionAST::codegen()`がすでにあるIR関数を見つけた場合,定義自身のプロトタイプにそって署名が正しいかをチェックしない. 328 | これは,関数定義の署名について,関数の引数名が間違っているような原因でコード生成に失敗するような,早い段階で`extern`で宣言された関数の優先度が高くなる. 329 | これを修正する方法は,たくさんある. 330 | 以下のようなコードをちゃんと処理できる必要があるのだが,その修正方法を考えてほしい. 331 | 332 | ``` 333 | extern foo(a); # ok, defines foo. 334 | def foo(b) b; # Error: Unknown variable name. (decl using 'a' takes precedence). 335 | ``` 336 | 337 | ## ドライバーの更新・・・まとめ 338 | 339 | ここでは,LLVMに対してのコード生成を十分に理解したとは言えない. 340 | IRコールを色々,見てきたというだけである. 341 | サンプルコードに,`HandleDefinition`, `HandleExtern`などの関数を加えることで,LLVM IRをダンプルすることができる. 342 | これは,シンプルな関数に対するLLVM IRを見ていくのに非常に便利である. 343 | 例えば,以下のような実行結果が得られる. 344 | 345 | ``` 346 | ready> 4+5; 347 | Read top-level expression: 348 | define double @0() { 349 | entry: 350 | ret double 9.000000e+00 351 | } 352 | ``` 353 | 354 | パーサがトップレベルの表現で無名関数をどう処理するかを示している. 355 | これは,次章で,JITをサポートするときに扱いやすい問題でもある. 356 | コードは,文学的に説明しやすいだけでなく,`IRBuilder`によって定数が畳まれる以外の最適化が実行されていない. 357 | 次章では,ここに最適化を加えていくことになる. 358 | 359 | ``` 360 | ready> def foo(a b) a*a + 2*a*b + b*b; 361 | Read function definition: 362 | define double @foo(double %a, double %b) { 363 | entry: 364 | %multmp = fmul double %a, %a 365 | %multmp1 = fmul double 2.000000e+00, %a 366 | %multmp2 = fmul double %multmp1, %b 367 | %addtmp = fadd double %multmp, %multmp2 368 | %multmp3 = fmul double %b, %b 369 | %addtmp4 = fadd double %addtmp, %multmp3 370 | ret double %addtmp4 371 | } 372 | ``` 373 | 374 | これは,単純な算術計算の例である. 375 | LLVM builderで呼び出した関数と,実際に出力される命令が似ていることに注意してほしい. 376 | 377 | ``` 378 | ready> def bar(a) foo(a, 4.0) + bar(31337); 379 | Read function definition: 380 | define double @bar(double %a) { 381 | entry: 382 | %calltmp = call double @foo(double %a, double 4.000000e+00) 383 | %calltmp1 = call double @bar(double 3.133700e+04) 384 | %addtmp = fadd double %calltmp, %calltmp1 385 | ret double %addtmp 386 | } 387 | ``` 388 | 389 | これは,関数呼び出しの例である. 390 | この関数の呼び出しには,多様時間がかかる. 391 | ここに,将来的に,再帰を使いやすくするため,条件付きの制御フローを追加していく. 392 | 393 | ``` 394 | ready> extern cos(x); 395 | Read extern: 396 | declare double @cos(double) 397 | 398 | ready> cos(1.234); 399 | Read top-level expression: 400 | define double @1() { 401 | entry: 402 | %calltmp = call double @cos(double 1.234000e+00) 403 | ret double %calltmp 404 | } 405 | ``` 406 | 407 | これは,外部の`cos`関数を呼び出す例である. 408 | 409 | ``` 410 | ready> ^D 411 | ; ModuleID = 'my cool jit' 412 | 413 | define double @0() { 414 | entry: 415 | %addtmp = fadd double 4.000000e+00, 5.000000e+00 416 | ret double %addtmp 417 | } 418 | 419 | define double @foo(double %a, double %b) { 420 | entry: 421 | %multmp = fmul double %a, %a 422 | %multmp1 = fmul double 2.000000e+00, %a 423 | %multmp2 = fmul double %multmp1, %b 424 | %addtmp = fadd double %multmp, %multmp2 425 | %multmp3 = fmul double %b, %b 426 | %addtmp4 = fadd double %addtmp, %multmp3 427 | ret double %addtmp4 428 | } 429 | 430 | define double @bar(double %a) { 431 | entry: 432 | %calltmp = call double @foo(double %a, double 4.000000e+00) 433 | %calltmp1 = call double @bar(double 3.133700e+04) 434 | %addtmp = fadd double %calltmp, %calltmp1 435 | ret double %addtmp 436 | } 437 | 438 | declare double @cos(double) 439 | 440 | define double @1() { 441 | entry: 442 | %calltmp = call double @cos(double 1.234000e+00) 443 | ret double %calltmp 444 | } 445 | ``` 446 | 447 | 今作っているデモアプリを終了すると,アプリは,今作っているモジュールの全体のIRをダンプする. 448 | ここで,それぞれに参照しあう,すべての関数の全体像を確認できる. 449 | 450 | ここで,Kaleidoscopeの3章はおしまいです. 451 | 次は,JITによるコード生成と,コードの最適化について説明していきます. 452 | -------------------------------------------------------------------------------- /BuildingAJITJP/chap03.md: -------------------------------------------------------------------------------- 1 | ## Building a JIT: Per-function Lazy Compilation 2 | 3 | 1. Chapter 3 Introduction 4 | 1. Lazy Compilation 5 | 1. Full Code Listing 6 | 7 | This tutorial is under active development. It is incomplete and details may change frequently. Nonetheless we invite you to try it out as it stands, and we welcome any feedback. 8 | 9 | ### Chapter 3 Introduction 10 | Warning: This text is currently out of date due to ORC API updates. 11 | 12 | The example code has been updated and can be used. The text will be updated once the API churn dies down. 13 | 14 | Welcome to Chapter 3 of the “Building an ORC-based JIT in LLVM” tutorial. This chapter discusses lazy JITing and shows you how to enable it by adding an ORC CompileOnDemand layer the JIT from Chapter 2. 15 | 16 | ### Lazy Compilation 17 | When we add a module to the KaleidoscopeJIT class from Chapter 2 it is immediately optimized, compiled and linked for us by the IRTransformLayer, IRCompileLayer and RTDyldObjectLinkingLayer respectively. This scheme, where all the work to make a Module executable is done up front, is simple to understand and its performance characteristics are easy to reason about. However, it will lead to very high startup times if the amount of code to be compiled is large, and may also do a lot of unnecessary compilation if only a few compiled functions are ever called at runtime. A truly “just-in-time” compiler should allow us to defer the compilation of any given function until the moment that function is first called, improving launch times and eliminating redundant work. In fact, the ORC APIs provide us with a layer to lazily compile LLVM IR: CompileOnDemandLayer. 18 | 19 | The CompileOnDemandLayer class conforms to the layer interface described in Chapter 2, but its addModule method behaves quite differently from the layers we have seen so far: rather than doing any work up front, it just scans the Modules being added and arranges for each function in them to be compiled the first time it is called. To do this, the CompileOnDemandLayer creates two small utilities for each function that it scans: a stub and a compile callback. The stub is a pair of a function pointer (which will be pointed at the function’s implementation once the function has been compiled) and an indirect jump through the pointer. By fixing the address of the indirect jump for the lifetime of the program we can give the function a permanent “effective address”, one that can be safely used for indirection and function pointer comparison even if the function’s implementation is never compiled, or if it is compiled more than once (due to, for example, recompiling the function at a higher optimization level) and changes address. The second utility, the compile callback, represents a re-entry point from the program into the compiler that will trigger compilation and then execution of a function. By initializing the function’s stub to point at the function’s compile callback, we enable lazy compilation: The first attempted call to the function will follow the function pointer and trigger the compile callback instead. The compile callback will compile the function, update the function pointer for the stub, then execute the function. On all subsequent calls to the function, the function pointer will point at the already-compiled function, so there is no further overhead from the compiler. We will look at this process in more detail in the next chapter of this tutorial, but for now we’ll trust the CompileOnDemandLayer to set all the stubs and callbacks up for us. All we need to do is to add the CompileOnDemandLayer to the top of our stack and we’ll get the benefits of lazy compilation. We just need a few changes to the source: 20 | 21 | ``` 22 | #include "llvm/ExecutionEngine/SectionMemoryManager.h" 23 | #include "llvm/ExecutionEngine/Orc/CompileOnDemandLayer.h" 24 | #include "llvm/ExecutionEngine/Orc/CompileUtils.h" 25 | ``` 26 | 27 | ``` 28 | class KaleidoscopeJIT { 29 | private: 30 | std::unique_ptr TM; 31 | const DataLayout DL; 32 | RTDyldObjectLinkingLayer ObjectLayer; 33 | IRCompileLayer CompileLayer; 34 | 35 | using OptimizeFunction = 36 | std::function(std::shared_ptr)>; 37 | 38 | IRTransformLayer OptimizeLayer; 39 | 40 | std::unique_ptr CompileCallbackManager; 41 | CompileOnDemandLayer CODLayer; 42 | 43 | public: 44 | using ModuleHandle = decltype(CODLayer)::ModuleHandleT; 45 | First we need to include the CompileOnDemandLayer.h header, then add two new members: a std::unique_ptr and a CompileOnDemandLayer, to our class. The CompileCallbackManager member is used by the CompileOnDemandLayer to create the compile callback needed for each function. 46 | 47 | KaleidoscopeJIT() 48 | : TM(EngineBuilder().selectTarget()), DL(TM->createDataLayout()), 49 | ObjectLayer([]() { return std::make_shared(); }), 50 | CompileLayer(ObjectLayer, SimpleCompiler(*TM)), 51 | OptimizeLayer(CompileLayer, 52 | [this](std::shared_ptr M) { 53 | return optimizeModule(std::move(M)); 54 | }), 55 | CompileCallbackManager( 56 | orc::createLocalCompileCallbackManager(TM->getTargetTriple(), 0)), 57 | CODLayer(OptimizeLayer, 58 | [this](Function &F) { return std::set({&F}); }, 59 | *CompileCallbackManager, 60 | orc::createLocalIndirectStubsManagerBuilder( 61 | TM->getTargetTriple())) { 62 | llvm::sys::DynamicLibrary::LoadLibraryPermanently(nullptr); 63 | } 64 | ``` 65 | 66 | Next we have to update our constructor to initialize the new members. To create an appropriate compile callback manager we use the createLocalCompileCallbackManager function, which takes a TargetMachine and a JITTargetAddress to call if it receives a request to compile an unknown function. In our simple JIT this situation is unlikely to come up, so we’ll cheat and just pass ‘0’ here. In a production quality JIT you could give the address of a function that throws an exception in order to unwind the JIT’d code’s stack. 67 | 68 | Now we can construct our CompileOnDemandLayer. Following the pattern from previous layers we start by passing a reference to the next layer down in our stack – the OptimizeLayer. Next we need to supply a ‘partitioning function’: when a not-yet-compiled function is called, the CompileOnDemandLayer will call this function to ask us what we would like to compile. At a minimum we need to compile the function being called (given by the argument to the partitioning function), but we could also request that the CompileOnDemandLayer compile other functions that are unconditionally called (or highly likely to be called) from the function being called. For KaleidoscopeJIT we’ll keep it simple and just request compilation of the function that was called. Next we pass a reference to our CompileCallbackManager. Finally, we need to supply an “indirect stubs manager builder”: a utility function that constructs IndirectStubManagers, which are in turn used to build the stubs for the functions in each module. The CompileOnDemandLayer will call the indirect stub manager builder once for each call to addModule, and use the resulting indirect stubs manager to create stubs for all functions in all modules in the set. If/when the module set is removed from the JIT the indirect stubs manager will be deleted, freeing any memory allocated to the stubs. We supply this function by using the createLocalIndirectStubsManagerBuilder utility. 69 | 70 | ``` 71 | // ... 72 | if (auto Sym = CODLayer.findSymbol(Name, false)) 73 | // ... 74 | return cantFail(CODLayer.addModule(std::move(Ms), 75 | std::move(Resolver))); 76 | // ... 77 | 78 | // ... 79 | return CODLayer.findSymbol(MangledNameStream.str(), true); 80 | // ... 81 | 82 | // ... 83 | CODLayer.removeModule(H); 84 | // ... 85 | ``` 86 | 87 | Finally, we need to replace the references to OptimizeLayer in our addModule, findSymbol, and removeModule methods. With that, we’re up and running. 88 | 89 | To be done: Chapter conclusion.** 90 | 91 | ### Full Code Listing 92 | Here is the complete code listing for our running example with a CompileOnDemand layer added to enable lazy function-at-a-time compilation. To build this example, use: 93 | 94 | ### Compile 95 | ``` 96 | clang++ -g toy.cpp `llvm-config --cxxflags --ldflags --system-libs --libs core orcjit native` -O3 -o toy 97 | ``` 98 | 99 | ### Run 100 | ``` 101 | ./toy 102 | ``` 103 | 104 | Here is the code: 105 | 106 | ``` 107 | //===- KaleidoscopeJIT.h - A simple JIT for Kaleidoscope --------*- C++ -*-===// 108 | // 109 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 110 | // See https://llvm.org/LICENSE.txt for license information. 111 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 112 | // 113 | //===----------------------------------------------------------------------===// 114 | // 115 | // Contains a simple JIT definition for use in the kaleidoscope tutorials. 116 | // 117 | //===----------------------------------------------------------------------===// 118 | 119 | #ifndef LLVM_EXECUTIONENGINE_ORC_KALEIDOSCOPEJIT_H 120 | #define LLVM_EXECUTIONENGINE_ORC_KALEIDOSCOPEJIT_H 121 | 122 | #include "llvm/ADT/StringRef.h" 123 | #include "llvm/ExecutionEngine/JITSymbol.h" 124 | #include "llvm/ExecutionEngine/Orc/CompileOnDemandLayer.h" 125 | #include "llvm/ExecutionEngine/Orc/CompileUtils.h" 126 | #include "llvm/ExecutionEngine/Orc/Core.h" 127 | #include "llvm/ExecutionEngine/Orc/ExecutionUtils.h" 128 | #include "llvm/ExecutionEngine/Orc/IRCompileLayer.h" 129 | #include "llvm/ExecutionEngine/Orc/IRTransformLayer.h" 130 | #include "llvm/ExecutionEngine/Orc/JITTargetMachineBuilder.h" 131 | #include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h" 132 | #include "llvm/ExecutionEngine/Orc/TPCIndirectionUtils.h" 133 | #include "llvm/ExecutionEngine/Orc/TargetProcessControl.h" 134 | #include "llvm/ExecutionEngine/SectionMemoryManager.h" 135 | #include "llvm/IR/DataLayout.h" 136 | #include "llvm/IR/LLVMContext.h" 137 | #include "llvm/IR/LegacyPassManager.h" 138 | #include "llvm/Transforms/InstCombine/InstCombine.h" 139 | #include "llvm/Transforms/Scalar.h" 140 | #include "llvm/Transforms/Scalar/GVN.h" 141 | #include 142 | 143 | namespace llvm { 144 | namespace orc { 145 | 146 | class KaleidoscopeJIT { 147 | private: 148 | std::unique_ptr TPC; 149 | std::unique_ptr ES; 150 | std::unique_ptr TPCIU; 151 | 152 | DataLayout DL; 153 | MangleAndInterner Mangle; 154 | 155 | RTDyldObjectLinkingLayer ObjectLayer; 156 | IRCompileLayer CompileLayer; 157 | IRTransformLayer OptimizeLayer; 158 | CompileOnDemandLayer CODLayer; 159 | 160 | JITDylib &MainJD; 161 | 162 | static void handleLazyCallThroughError() { 163 | errs() << "LazyCallThrough error: Could not find function body"; 164 | exit(1); 165 | } 166 | 167 | public: 168 | KaleidoscopeJIT(std::unique_ptr TPC, 169 | std::unique_ptr ES, 170 | std::unique_ptr TPCIU, 171 | JITTargetMachineBuilder JTMB, DataLayout DL) 172 | : TPC(std::move(TPC)), ES(std::move(ES)), TPCIU(std::move(TPCIU)), 173 | DL(std::move(DL)), Mangle(*this->ES, this->DL), 174 | ObjectLayer(*this->ES, 175 | []() { return std::make_unique(); }), 176 | CompileLayer(*this->ES, ObjectLayer, 177 | std::make_unique(std::move(JTMB))), 178 | OptimizeLayer(*this->ES, CompileLayer, optimizeModule), 179 | CODLayer(*this->ES, OptimizeLayer, 180 | this->TPCIU->getLazyCallThroughManager(), 181 | [this] { return this->TPCIU->createIndirectStubsManager(); }), 182 | MainJD(this->ES->createBareJITDylib("
")) { 183 | MainJD.addGenerator( 184 | cantFail(DynamicLibrarySearchGenerator::GetForCurrentProcess( 185 | DL.getGlobalPrefix()))); 186 | } 187 | 188 | ~KaleidoscopeJIT() { 189 | if (auto Err = ES->endSession()) 190 | ES->reportError(std::move(Err)); 191 | if (auto Err = TPCIU->cleanup()) 192 | ES->reportError(std::move(Err)); 193 | } 194 | 195 | static Expected> Create() { 196 | auto SSP = std::make_shared(); 197 | auto TPC = SelfTargetProcessControl::Create(SSP); 198 | if (!TPC) 199 | return TPC.takeError(); 200 | 201 | auto ES = std::make_unique(std::move(SSP)); 202 | 203 | auto TPCIU = TPCIndirectionUtils::Create(**TPC); 204 | if (!TPCIU) 205 | return TPCIU.takeError(); 206 | 207 | (*TPCIU)->createLazyCallThroughManager( 208 | *ES, pointerToJITTargetAddress(&handleLazyCallThroughError)); 209 | 210 | if (auto Err = setUpInProcessLCTMReentryViaTPCIU(**TPCIU)) 211 | return std::move(Err); 212 | 213 | JITTargetMachineBuilder JTMB((*TPC)->getTargetTriple()); 214 | 215 | auto DL = JTMB.getDefaultDataLayoutForTarget(); 216 | if (!DL) 217 | return DL.takeError(); 218 | 219 | return std::make_unique(std::move(*TPC), std::move(ES), 220 | std::move(*TPCIU), std::move(JTMB), 221 | std::move(*DL)); 222 | } 223 | 224 | const DataLayout &getDataLayout() const { return DL; } 225 | 226 | JITDylib &getMainJITDylib() { return MainJD; } 227 | 228 | Error addModule(ThreadSafeModule TSM, ResourceTrackerSP RT = nullptr) { 229 | if (!RT) 230 | RT = MainJD.getDefaultResourceTracker(); 231 | 232 | return OptimizeLayer.add(RT, std::move(TSM)); 233 | } 234 | 235 | Expected lookup(StringRef Name) { 236 | return ES->lookup({&MainJD}, Mangle(Name.str())); 237 | } 238 | 239 | private: 240 | static Expected 241 | optimizeModule(ThreadSafeModule TSM, const MaterializationResponsibility &R) { 242 | TSM.withModuleDo([](Module &M) { 243 | // Create a function pass manager. 244 | auto FPM = std::make_unique(&M); 245 | 246 | // Add some optimizations. 247 | FPM->add(createInstructionCombiningPass()); 248 | FPM->add(createReassociatePass()); 249 | FPM->add(createGVNPass()); 250 | FPM->add(createCFGSimplificationPass()); 251 | FPM->doInitialization(); 252 | 253 | // Run the optimizations over all functions in the module being added to 254 | // the JIT. 255 | for (auto &F : M) 256 | FPM->run(F); 257 | }); 258 | 259 | return std::move(TSM); 260 | } 261 | }; 262 | 263 | } // end namespace orc 264 | } // end namespace llvm 265 | 266 | #endif // LLVM_EXECUTIONENGINE_ORC_KALEIDOSCOPEJIT_H 267 | ``` 268 | -------------------------------------------------------------------------------- /KaleidoscopeJP/chap02.md: -------------------------------------------------------------------------------- 1 | # パーサと抽象構文木の構築 2 | 3 | パーサは,再帰的深さ探索とオペレータ優先探索の組み合わせで実装される. 4 | Kaleidscope言語における後者はバイナリ表現を,前者は他のすべての表現をパースする. 5 | まず,抽象構文木について説明する. 6 | 7 | ## 抽象構文木・・・The Abstract Syntax Tree 8 | 9 | ASTは,プログラムが後段でコードを生成するときに翻訳が簡単になるような振る舞いを獲得する. 10 | 我々は,基本的にその言語のそれぞれ構造に対する一つのオブジェクトがほしい. 11 | そして,ASTは,言語を正しくモデリングすべきである. 12 | Kaleidoscopeでは,変数宣言,表現,関数宣言などが必要である. 13 | 14 | ``` 15 | /// ExprAST - Base class for all expression nodes. 16 | class ExprAST { 17 | public: 18 | virtual ~ExprAST() {} 19 | }; 20 | 21 | /// NumberExprAST - Expression class for numeric literals like "1.0". 22 | class NumberExprAST : public ExprAST { 23 | double Val; 24 | 25 | public: 26 | NumberExprAST(double Val) : Val(Val) {} 27 | }; 28 | ``` 29 | 上のクラスが構文木のノードとなるルートクラス. 30 | `NumberExprAST`は,数値リテラルを保存するためのノードになる. 31 | このクラスが,後段でコンパイラがどんな数値が保存されているかを知るための手段になる. 32 | 33 | しかし,現状,ASTを作っただけでは,木の中身にアクセスする方法がないので,便利に使えない. 34 | クラスに仮想関数を加えて,`print`を簡単に実行できるようにしたりする必要がある. 35 | ここで,もうちょっと要素を追加する. 36 | 37 | ``` 38 | /// VariableExprAST - 変数を保存するための要素 39 | class VariableExprAST : public ExprAST { 40 | std::string Name; 41 | 42 | public: 43 | VariableExprAST(const std::string &Name) : Name(Name) {} 44 | }; 45 | 46 | /// BinaryExprAST - +とかの演算子を保存するための要素? 47 | class BinaryExprAST : public ExprAST { 48 | char Op; 49 | std::unique_ptr LHS, RHS; 50 | 51 | public: 52 | BinaryExprAST(char op, std::unique_ptr LHS, 53 | std::unique_ptr RHS) 54 | : Op(op), LHS(std::move(LHS)), RHS(std::move(RHS)) {} 55 | }; 56 | 57 | /// CallExprAST - 関数呼び出しと引数を保存するための木 58 | class CallExprAST : public ExprAST { 59 | std::string Callee; 60 | std::vector> Args; 61 | 62 | public: 63 | CallExprAST(const std::string &Callee, 64 | std::vector> Args) 65 | : Callee(Callee), Args(std::move(Args)) {} 66 | }; 67 | ``` 68 | 69 | とりあえず,この基本的な言語が定義するノードはこれがすべてである. 70 | 実は,条件制御がないため,チューリング完全ではないが,それは後で追加で実装していくことにする. 71 | 72 | 次に必要なのは,関数のインタフェースと関数自身である. 73 | 74 | ``` 75 | /// PrototypeAST - This class represents the "prototype" for a function, 76 | /// which captures its name, and its argument names (thus implicitly the number 77 | /// of arguments the function takes). 78 | class PrototypeAST { 79 | std::string Name; 80 | std::vector Args; 81 | 82 | public: 83 | PrototypeAST(const std::string &name, std::vector Args) 84 | : Name(name), Args(std::move(Args)) {} 85 | 86 | const std::string &getName() const { return Name; } 87 | }; 88 | 89 | /// FunctionAST - This class represents a function definition itself. 90 | class FunctionAST { 91 | std::unique_ptr Proto; 92 | std::unique_ptr Body; 93 | 94 | public: 95 | FunctionAST(std::unique_ptr Proto, 96 | std::unique_ptr Body) 97 | : Proto(std::move(Proto)), Body(std::move(Body)) {} 98 | }; 99 | ``` 100 | 101 | Kaleidoscopeでは,関数は,引数の数しか保存しない. 102 | これは,Kaleidoscopeでは,変数が`double`型しか存在しないため,それらの型を区別する必要がないためである. 103 | 一般的な言語の場合,`ExprAST`は,型を保存するフィールドを持たねばならない. 104 | 105 | ## パーサの基本 106 | 107 | ここまでで,ビルドすべき木を設計できたが,次に,木を作るパーサを設計する必要がある. 108 | "x+y"のような表現があるとき("x+y"は,lexerによって3つのトークンに分割される),それを以下のような表現に変換したい. 109 | 110 | ``` 111 | auto LHS = llvm::make_unique("x"); 112 | auto RHS = llvm::make_unique("y"); 113 | auto Result = std::make_unique('+', std::move(LHS), std::move(RHS)); 114 | ``` 115 | 116 | これを実現するために,色々関数を作っていく. 117 | `CurTok`は,トークンのバッファで,パーサが注目してるトークンそのものである. 118 | `getNextToken`は,つぎのトークンを取ってくると同時に`CurTok`を更新する. 119 | 120 | ``` 121 | /// CurTok/getNextToken - Provide a simple token buffer. CurTok is the current 122 | /// token the parser is looking at. getNextToken reads another token from the 123 | /// lexer and updates CurTok with its results. 124 | static int CurTok; 125 | static int getNextToken() { 126 | return CurTok = gettok(); 127 | } 128 | ``` 129 | 130 | エラーハンドリングは,こんな感じにする. 131 | 本来ならば,もっとユーザフレンドリなものにすべきだが,我々のチュートリアルならば,この程度でよい. 132 | 133 | ``` 134 | /// LogError* - These are little helper functions for error handling. 135 | std::unique_ptr LogError(const char *Str) { 136 | fprintf(stderr, "LogError: %s\n", Str); 137 | return nullptr; 138 | } 139 | std::unique_ptr LogErrorP(const char *Str) { 140 | LogError(Str); 141 | return nullptr; 142 | } 143 | ``` 144 | 145 | こういった関数群を使って,数値リテラルをパースするパーサの初歩的な部分を実装していく. 146 | 147 | ## Basic Expression Parsing 148 | 149 | まずは、もっとも簡単なプロセスなので、数字のリテラルから始める。 150 | このグラマーのそれぞれの生成物に対して、それを生成する関数を定義していく。 151 | 数字のリテラルを処理するコードは、次のようになる。 152 | 153 | ``` 154 | /// numberexpr ::= number 155 | static std::unique_ptr ParseNumberExpr() { 156 | auto Result = llvm::make_unique(NumVal); 157 | getNextToken(); // consume the number 158 | return std::move(Result); 159 | } 160 | ``` 161 | 162 | このメソッドは、チェックしているトークンの種類が`tok_number`であるときに実行される。 163 | 数値のノードを生成し、新しいトークンを取得し、生成したものを戻り値として返す。 164 | `std::move`は、所有権の話。 165 | `Result`は、この後、破棄され、戻り値を受け取った先のスコープに所有権が移される。 166 | 167 | ここにいくつかおもしろい点が見受けられる。 168 | もっとも重要なことは、この関数が、生成物に対応するトークンの全てを処理し、次のトークンを返すということである。 169 | これは、再帰的深さ優先探索では、至極普通な方法である。 170 | いい例を出してみよう。 171 | 次は、かっこを処理するコードである。 172 | 173 | ``` 174 | /// parenexpr ::= '(' expression ')' 175 | static std::unique_ptr ParseParenExpr() { 176 | getNextToken(); // eat (. 177 | auto V = ParseExpression(); 178 | if (!V) 179 | return nullptr; 180 | 181 | if (CurTok != ')') 182 | return LogError("expected ')'"); 183 | getNextToken(); // eat ). 184 | return V; 185 | } 186 | ``` 187 | 188 | この関数は、パーサの面白い点を多分に説明するものである。 189 | 190 | 1. `LogError`関数の使い方。この関数が呼ばれるとき、現在のトークンが`(`であることを期待されている。しかし、それに続く表現をパースした後、`)`がない可能性もある。 191 | 2. `ParseExpression`を再帰的に呼ぶ構造になっている点。`ParseExpression`が`ParseParenExpr`をコールすることになるのは、後で確認する。これは強力な実装だ。なぜなら、文法を再帰的に処理できるようにするし、それぞれの成果物をとてもシンプルにする。かっこ自体は、ASTのノードの構造に組み込まれない。組み込むこともできるが、かっこの大切な役割は、パーサのガイドであり、変数のグルーピングである。一度ASTが構築されれば、かっこはもはや必要ないのである。 192 | 193 | 次にシンプルな、変数と関数呼出のハンドリングのためのコードを示す。 194 | 195 | ``` 196 | /// identifierexpr 197 | /// ::= identifier 198 | /// ::= identifier '(' expression* ')' 199 | static std::unique_ptr ParseIdentifierExpr() { 200 | std::string IdName = IdentifierStr; 201 | 202 | getNextToken(); // eat identifier. 203 | 204 | if (CurTok != '(') // Simple variable ref. 205 | return llvm::make_unique(IdName); 206 | 207 | // Call. 208 | getNextToken(); // eat ( 209 | std::vector> Args; 210 | if (CurTok != ')') { 211 | while (1) { 212 | if (auto Arg = ParseExpression()) 213 | Args.push_back(std::move(Arg)); 214 | else 215 | return nullptr; 216 | 217 | if (CurTok == ')') 218 | break; 219 | 220 | if (CurTok != ',') 221 | return LogError("Expected ')' or ',' in argument list"); 222 | getNextToken(); 223 | } 224 | } 225 | 226 | // Eat the ')'. 227 | getNextToken(); 228 | 229 | return llvm::make_unique(IdName, std::move(Args)); 230 | } 231 | ``` 232 | 233 | この関数も,他の関数と同じようなスタイルになる. 234 | 現在のトークンは`tok_identifier`トークンであるかどうかを問い合わせることを想定している. 235 | 関数は,再帰とエラーハンドリングを使う. 236 | この関数のおもしろいときは,現在の識別子がスタンドアローンで識別できるのか,関数呼び出しなのか,を先読みで決定するというところにある. 237 | 識別子の後のトークンが`(`トークンであるかどうか,`VariableExprAST`や`CallExprAST`ノードを適切に構築しているかを見るためにこれをチェックする. 238 | 239 | 我々のシンプルな表現パーサのロジックを一箇所にまとめ,それを一箇所のエントリーポイントにまとめる. 240 | これをprimary表現と呼ぶ. 241 | そう呼ぶ理由は,後々明らかになる. 242 | 243 | ``` 244 | /// primary 245 | /// ::= identifierexpr 246 | /// ::= numberexpr 247 | /// ::= parenexpr 248 | static std::unique_ptr ParsePrimary() { 249 | switch (CurTok) { 250 | default: 251 | return LogError("unknown token when expecting an expression"); 252 | case tok_identifier: 253 | return ParseIdentifierExpr(); 254 | case tok_number: 255 | return ParseNumberExpr(); 256 | case '(': 257 | return ParseParenExpr(); 258 | } 259 | } 260 | ``` 261 | 262 | この関数の定義を理解しよう。 263 | 我々が個々の関数内部に`CurTok`の状態があることを前提とするのは明らかである。 264 | これは、先読みで表現の優先順位?を決定するために使われ、関数呼び出しをパースする。 265 | 266 | ## Binary Expression Parsing 267 | 二項演算のパースは,かなり難しい. 268 | これは,しばしば曖昧で,一意に決定されないことがあるためである. 269 | たとえば,"x+y*z"が文字列として与えられた場合,パーサは, “(x+y)*z”あるいは“x+(y*z)”を選択しなければならない. 270 | 数学のルールとして,我々は後者を選択することになるが,それは,乗算は,加算よりも優先度が高いからである. 271 | 272 | これを処理する方法はたくさんあるが,もっとも美しく,効率のよい方法は,"Operator-Precedence Parsing"である. 273 | これは,再帰的に二項演算の優先度を処理していく方法である. 274 | この方法を使うには,まず最初に優先度テーブルが必要になる. 275 | 276 | ``` 277 | /// BinopPrecedence - This holds the precedence for each binary operator that is 278 | /// defined. 279 | static std::map BinopPrecedence; 280 | 281 | /// GetTokPrecedence - Get the precedence of the pending binary operator token. 282 | static int GetTokPrecedence() { 283 | if (!isascii(CurTok)) 284 | return -1; 285 | 286 | // Make sure it's a declared binop. 287 | int TokPrec = BinopPrecedence[CurTok]; 288 | if (TokPrec <= 0) return -1; 289 | return TokPrec; 290 | } 291 | 292 | int main() { 293 | // Install standard binary operators. 294 | // 1 is lowest precedence. 295 | BinopPrecedence['<'] = 10; 296 | BinopPrecedence['+'] = 20; 297 | BinopPrecedence['-'] = 20; 298 | BinopPrecedence['*'] = 40; // highest. 299 | ... 300 | } 301 | ``` 302 | 303 | Kaleidoscopeの基本的な形式のため,4つの二項演算子のみをサポートする. 304 | 賢明な読者諸君にとって,これを拡張していくのはよい勉強になるだろう. 305 | `GetTokPrecedence`は,トークンが二項演算子でなければ-1を,二項演算子であれば,その優先度を返す. 306 | マップを作っておけば,新しい演算子を追加したり,特定の演算子の処理方法に依存するようなことがなくなる. 307 | しかしながら,マップをつかわず, `GetTokPrecedence`の内部で演算子の比較処理を実装するのも,そんなに難しい話ではないし,固定配列長でやってもいい. 308 | 309 | 上で定義した`GetTokPrecedence`を使って,我々は,二項演算子のパーサの実装を始められる. 310 | "Operator-Precedence Parsing"のベースとなるアイデアは,潜在的にあいまい性を持つ二項演算子による表現をひとつずつ解消していくことである. 311 | 例えば,“a+b+(c+d)*e*f+g”を例に考えてみる. 312 | "Operator-Precedence Parsing"は,二項演算子で区切られた"primary expression"の流れとして考えることができる. 313 | 具体的には,最初にaという"primary expression"があり,その後,`[+, b]`,`[+, (c+d)]`,`[*, e]`,`[*, f]`および`[+, g]`のペアが続く. 314 | 括弧は,"primary expression"であるため,二項演算子は,(c+d)のような入れ子になった表現に困ることはない. 315 | 316 | [二項演算子,主表現]が続く主表現からパースを始める. 317 | 318 | ``` 319 | /// expression 320 | /// ::= primary binoprhs 321 | /// 322 | static std::unique_ptr ParseExpression() { 323 | auto LHS = ParsePrimary(); 324 | if (!LHS) 325 | return nullptr; 326 | 327 | return ParseBinOpRHS(0, std::move(LHS)); 328 | } 329 | ``` 330 | 331 | `ParseBinOpRHS`は,ペアのシーケンスをパースする関数である. 332 | その関数は,パースされた二項演算子の表現へのポインタと優先度を取る. 333 | `x`は,完全に有効な表現であり,"二項演算子"が空であることが許される. 334 | 上の例では,最初の`a`は,`ParseBinOpRHS`に,現在のトークンである`+`と一緒に渡されることになる. 335 | 336 | `ParseBinOpRHS`に渡される優先度の値は,関数が処理すべき,最小単位のオペレータの優先度を示す. 337 | 例えば,もし現在のペアが`[+, x]`であり,`ParseBinOpRHS`が優先度として40が渡された場合,`+`の優先度が20しかないので,トークンは何も処理されない. 338 | これを気に留めながら,`ParseBinOpRHS`は,以下のコードから始まる 339 | 340 | ``` 341 | /// binoprhs 342 | /// ::= ('+' primary)* 343 | static std::unique_ptr ParseBinOpRHS(int ExprPrec, 344 | std::unique_ptr LHS) { 345 | // If this is a binop, find its precedence. 346 | while (1) { 347 | int TokPrec = GetTokPrecedence(); 348 | 349 | // If this is a binop that binds at least as tightly as the current binop, 350 | // consume it, otherwise we are done. 351 | if (TokPrec < ExprPrec) 352 | return LHS; 353 | ``` 354 | 355 | このコードは,まず現在のトークンの優先度を取得し,それが低すぎないかをチェックする. 356 | 優先度が-1のトークンは,エラーであると定義しているため,このチェックは,暗黙に処理すべき二項演算子のトークンの流れが尽きた時,処理すべきペアのストリームが終わるということにしている. 357 | もし,このチェックにパスすると,トークンが二項演算子であり,以下の表現を含んでいると仮定でき, 358 | 359 | ``` 360 | // Okay, we know this is a binop. 361 | int BinOp = CurTok; 362 | getNextToken(); // eat binop 363 | 364 | // Parse the primary expression after the binary operator. 365 | auto RHS = ParsePrimary(); 366 | if (!RHS) 367 | return nullptr; 368 | ``` 369 | 370 | このコードは,二項演算子を処理し,記憶し,続く主表現をパースしていく. 371 | これは,すべてのペアを作っていき,今回のサンプルでは,`+`と`b`のペアが一番最初に来る. 372 | 373 | 今,左辺とRHSシーケンスのペアのひとつをパースしようとしているとすると,表現を組み合わせ方を決めなければならない. 374 | 特に,`(a+b) <まだパースしてない二項演算子>`あるいは`a+(b <まだパースしてない二項演算子>)`とかが対象のときである. 375 | これを決定するために,その優先度を決定するために,二項演算子を先読みし,現在の二項演算子の優先度と比較する. 376 | 377 | ``` 378 | // If BinOp binds less tightly with RHS than the operator after RHS, let 379 | // the pending operator take RHS as its LHS. 380 | int NextPrec = GetTokPrecedence(); 381 | if (TokPrec < NextPrec) { 382 | ``` 383 | 384 | `RHS`の右辺に対する二項演算子の優先度が,現状の演算子の優先度と等しいか,それより低いい場合,括弧は,"(a+b), 二項演算子"として処理される. 385 | このサンプルの場合,現在の演算子は,`+`で,次の演算子が`+`であり,同じ優先度である. 386 | この場合,まず,`a+b`に対するASTノードが作られるとして,次にパースは続いて, 387 | 388 | ``` 389 | ... if body omitted ... 390 | } 391 | 392 | // Merge LHS/RHS. 393 | LHS = llvm::make_unique(BinOp, std::move(LHS), 394 | std::move(RHS)); 395 | } // loop around to the top of the while loop. 396 | } 397 | ``` 398 | 399 | 上の例では,`a+b+`を`(a+b)`に変え,そして,`+`をカレントトークンとして次のループに入る. 400 | 上のコードは,主表現として,`(c+d)`を処理・パースし,`[+, (c+d)]`を次の処理すべきペアとする. 401 | 次に,`if (TokPrec < NextPrec) {`のif文を評価し,主表現の右辺の二項演算子として`*`を採用する. 402 | この場合,`*`の優先度は,`+`よりも高いので.if文の中に入ることになる. 403 | 404 | ここで残された重要な問題は,"どうやってif文が右辺を最後までパースするか?"ということである. 405 | 特に,構文木を作るために,今回の例だと,`(c+d)*e*f`のすべてを二項演算子の右辺として処理する必要がある. 406 | しかし,これをするためのコードは,驚くほど簡単なのである. 407 | (上のコードをちょっとコピペするだけでよい.つまるところ,再帰的に二項演算子の右辺をパースしつづければよいのである.) 408 | 409 | ``` 410 | // If BinOp binds less tightly with RHS than the operator after RHS, let 411 | // the pending operator take RHS as its LHS. 412 | int NextPrec = GetTokPrecedence(); 413 | if (TokPrec < NextPrec) { 414 | RHS = ParseBinOpRHS(TokPrec+1, std::move(RHS)); 415 | if (!RHS) 416 | return nullptr; 417 | } 418 | // Merge LHS/RHS. 419 | LHS = llvm::make_unique(BinOp, std::move(LHS), 420 | std::move(RHS)); 421 | } // loop around to the top of the while loop. 422 | } 423 | ``` 424 | 425 | 今,現在パースしている二項演算子よりも,"主表現の右辺"に対する二項演算子の優先度が高いと知っているとする. 426 | そういった場合,`+`より高い優先度を持つ演算子とペアになったシーケンスが一緒にパースされ,"主表現の右辺"として返される方がよいとわかる. 427 | これに対して,我々は,最小の優先度を`TokPrec+1`として一つ増やし,再帰的に`ParseBinOpRHS`を呼び出すことになります. 428 | 上の実装では,右辺として,`(c+d)*e*f`が構文木のノードとして返され,それとセットになる演算子は,`+`になります. 429 | 430 | 最後に,次のwhile文で,構文木に`+g`が追加されます. 431 | 14行足らずのコードで,我々は,美しい方法で,すべての一般的な二項演算表現を取り扱うことができました. 432 | 一気にコードを説明しました. 433 | このコードがどうやって動くのかを把握するため,簡単ではない表現を入力し,それを確かめるとよいでしょう. 434 | 435 | ## 残りをパースする 436 | 437 | やり残したことは,関数宣言の取り扱いです. 438 | Kaleidoscopeでは,関数の実装宣言も,関数の宣言にも,`extern`を使います. 439 | 愚直に実装すると,以下のようになるだろう. 440 | 441 | ``` 442 | /// prototype 443 | /// ::= id '(' id* ')' 444 | static std::unique_ptr ParsePrototype() { 445 | if (CurTok != tok_identifier) 446 | return LogErrorP("Expected function name in prototype"); 447 | 448 | std::string FnName = IdentifierStr; 449 | getNextToken(); 450 | 451 | if (CurTok != '(') 452 | return LogErrorP("Expected '(' in prototype"); 453 | 454 | // Read the list of argument names. 455 | std::vector ArgNames; 456 | while (getNextToken() == tok_identifier) 457 | ArgNames.push_back(IdentifierStr); 458 | if (CurTok != ')') 459 | return LogErrorP("Expected ')' in prototype"); 460 | 461 | // success. 462 | getNextToken(); // eat ')'. 463 | 464 | return llvm::make_unique(FnName, std::move(ArgNames)); 465 | } 466 | ``` 467 | 468 | 関数定義は,非常にシンプルなので,実装も難しくはない. 469 | 470 | ``` 471 | /// definition ::= 'def' prototype expression 472 | static std::unique_ptr ParseDefinition() { 473 | getNextToken(); // eat def. 474 | auto Proto = ParsePrototype(); 475 | if (!Proto) return nullptr; 476 | 477 | if (auto E = ParseExpression()) 478 | return llvm::make_unique(std::move(Proto), std::move(E)); 479 | return nullptr; 480 | } 481 | ``` 482 | 483 | 加えて,ユーザが作る関数の宣言に加えて,`sin`や`cos`のような関数を宣言するために`extern`をサポートします. 484 | この`extern`は,本体を持たないプロトタイプ宣言ということになります. 485 | 486 | ``` 487 | /// external ::= 'extern' prototype 488 | static std::unique_ptr ParseExtern() { 489 | getNextToken(); // eat extern. 490 | return ParsePrototype(); 491 | } 492 | ``` 493 | 494 | 最後に,任意のトップレベルの表現を使えるようにし,その場でそれを評価できるようにする. 495 | そういった表現は,引数がない無名関数を定義することで処理する. 496 | 497 | ``` 498 | /// toplevelexpr ::= expression 499 | static std::unique_ptr ParseTopLevelExpr() { 500 | if (auto E = ParseExpression()) { 501 | // Make an anonymous proto. 502 | auto Proto = llvm::make_unique("", std::vector()); 503 | return llvm::make_unique(std::move(Proto), std::move(E)); 504 | } 505 | return nullptr; 506 | } 507 | ``` 508 | 509 | これで,すべてのピースはそろった. 510 | 次に,これらのコードを実際に動かすための`driver`を実装することにする. 511 | 512 | ## Driver 513 | 514 | driverは,トップレベルのループでピースをすべてパースする. 515 | driverは,ここではそれ以上の意味はない. 516 | 517 | ``` 518 | /// top ::= definition | external | expression | ';' 519 | static void MainLoop() { 520 | while (1) { 521 | fprintf(stderr, "ready> "); 522 | switch (CurTok) { 523 | case tok_eof: 524 | return; 525 | case ';': // ignore top-level semicolons. 526 | getNextToken(); 527 | break; 528 | case tok_def: 529 | HandleDefinition(); 530 | break; 531 | case tok_extern: 532 | HandleExtern(); 533 | break; 534 | default: 535 | HandleTopLevelExpression(); 536 | break; 537 | } 538 | } 539 | } 540 | ``` 541 | 542 | このコードでおもしろいところは,トップレベルのセミコロンを無視する点にある. 543 | なぜそうするのだろうか. 544 | 基本的な理由は,cliで`4 + 5`をタイプした場合,パーサはそのタイプに続きがあるのか,ないのか判定できないためである. 545 | 例えば,次の行で,`def foo...`とタイプされた場合,`4+5`は,トップレベルの表現として処理することになる. 546 | 一方で,`*6`と続いた場合は,表現は途切れず続いていくことになる. 547 | トップレベルのセミコロンは,`4+5;`とタイプすることで,区切りを入力できるようにするものである. 548 | 549 | ## まとめ 550 | 551 | 400行くらいのコードで,我々は,十分に,最小の言語のlexer,パーサ,構文木を定義した. 552 | これを実行すれば,Kaleidoscopeのコードが正しいものかを判断するツールとなる.例えば,以下のような実行結果が得られる. 553 | 554 | ``` 555 | $ ./a.out 556 | ready> def foo(x y) x+foo(y, 4.0); 557 | Parsed a function definition. 558 | ready> def foo(x y) x+y y; 559 | Parsed a function definition. 560 | Parsed a top-level expr 561 | ready> def foo(x y) x+y ); 562 | Parsed a function definition. 563 | Error: unknown token when expecting an expression 564 | ready> extern sin(a); 565 | ready> Parsed an extern 566 | ready> ^D 567 | $ 568 | ``` 569 | 570 | このコードの拡張の余地は,たくさんある. 571 | 新しい,構文木のノードを定義したり,たくさんの拡張の方向性がある. 572 | 次章では,LLVM中間表現,LLVM IRを構文木から生成する方法について説明していく. -------------------------------------------------------------------------------- /KaleidoscopeJP/chap04.md: -------------------------------------------------------------------------------- 1 | # JITと最適化 2 | 3 | ## はじめに 4 | 4章へようこそ. 5 | 3章までは,シンプルな言語の実装や,LLVM IRを生成するためのコードなどの解説をしてきた. 6 | 本章では,自分で作った言語の最適化機能とJITコンパイラの二つ新しい技術について紹介する. 7 | これらは,Kaleidoscope言語を,洗練し,高速にする. 8 | 9 | ## 瑣末な定数の折りたたみ 10 | 3章で紹介した内容は,エレガントで拡張が簡単である. 11 | しかしながら,それは不幸にも,すごい,すばらしいコードを生成するかと言うとそうでもない. 12 | 一方で,`IRBuilder`は,シンプルなコードをコンパイルするとき,わかりやすく最適化してくれる. 13 | 14 | ``` 15 | ready> def test(x) 1+2+x; 16 | Read function definition: 17 | define double @test(double %x) { 18 | entry: 19 | %addtmp = fadd double 3.000000e+00, %x 20 | ret double %addtmp 21 | } 22 | ``` 23 | 24 | このコードは,入力をパースして,抽象構文木を直訳したものになっていない. 25 | 直訳したなら,以下のようになっているはずである. 26 | 27 | ``` 28 | ready> def test(x) 1+2+x; 29 | Read function definition: 30 | define double @test(double %x) { 31 | entry: 32 | %addtmp = fadd double 2.000000e+00, 1.000000e+00 33 | %addtmp1 = fadd double %addtmp, %x 34 | ret double %addtmp1 35 | } 36 | ``` 37 | 38 | 上の例のような定数の折りたたみは,特によくあるケースであり,とても重要な最適化のひとつである. 39 | 多くの言語が,抽象構文木の表現の中で定数の折りたたみを実装している. 40 | 41 | LLVMを使う場合,抽象構文木側(のパーサ?)で,これをサポートする必要はない. 42 | すべての命令は,LLVM IRビルダーを通して,LLVM IRを構築するため,ビルダをコールしたときに,ビルダ自身が,定数折りたたみを実行するチャンスがあるかをチェックすることになる. 43 | もし,折りたたみを実行する余地があるなら,ビルダは,定数折りたたみを実行し,計算のための命令を生成する代わりに,定数を返すことになる. 44 | 45 | 一方,`IRBuilder`は,インラインで分析することしかできないため,以下のようなちょっとややこしいコードの場合,うまく処理することができない. 46 | 47 | ``` 48 | ready> def test(x) (1+2+x)*(x+(1+2)); 49 | ready> Read function definition: 50 | define double @test(double %x) { 51 | entry: 52 | %addtmp = fadd double 3.000000e+00, %x 53 | %addtmp1 = fadd double %x, 3.000000e+00 54 | %multmp = fmul double %addtmp, %addtmp1 55 | ret double %multmp 56 | } 57 | ``` 58 | 59 | このケースでは,乗算の左の項と,右の項は,まったく同じ値である. 60 | この場合,我々が期待するのは,`x+3`を2回計算する代わりに,`temp = x + 3; result = temp * temp;`といったコードが生成されることである. 61 | 62 | 不幸にも,ローカルな分析をどれだけ積み上げても,このコードを最適化することはできない. 63 | このコードは、二つのコードの変形を必要とする。 64 | それは、冗長な加算命令を除去するための表現の結合と、共通部分式除去(CSE,評価する式に共通する部分を除去すること)である。 65 | 幸運にもllvmは、`passes`という形で誰もが使える幅広い最適化機能を提供してくれる。 66 | 67 | ## LLVMの最適化Pass 68 | 69 | LLVMは、様々な問題をこなし、それぞれに異なるトレードオフを持つ多くの最適化経路を提供する。 70 | 他のシステムと異なり、llvmは、ある最適化の考え方が、すべての言語、すべてのコンテキストにおいて正しいとするような間違った考え方に固執しない。 71 | LLVMは、コンパイラを実装する人に、どのオプティマイザを、どんな順番で、どんな状況で、を完全に指定させる。 72 | 73 | 確固たる例として、llvmは、モジュール全体を最適化するパスもまたサポートする。このときの全体最適化とは、リンク時にファイル全体を、あるいはプログラムの一部を、パスが最適できる限りコード全体を通して、最適化するものである。 74 | LLVMは、他の関数は全く見ずに、同時に一つの関数ただ操作するだけの関数単位でのパスもサポートする。 75 | パスの詳しい解説やどうやって実行するかについては、[How to Write a Pass](https://llvm.org/docs/WritingAnLLVMPass.html)や、[List of LLVM Passes](https://llvm.org/docs/Passes.html)を参考にされたい。 76 | 77 | Kaleidosopeのために、我々は、今、関数を即座に、すぐに、あるいはユーザが入力すると同時に生成している。 78 | この設定で、最高の最適化経験が得られることを我々は、目指しておらず、できる限り、簡単かつすぐ作れるコードを作ることを目的としている。 79 | そうであるとは言い難いが、我々は、ユーザが関数を入力したときに、関数ごとに動作する最適化を走らせるサンプルを選ぶことにする。 80 | もし、“静的なKaleidoscopeコンパイラ”を作りたいのなら、ファイル全体をパースしてしまう直前まで、オプティマイザを実行するのを延期することを除いて、今あるコードを使えばいいだろう。 81 | 82 | 関数ごとの最適化を実行するために、我々が実行したいllvmの最適化を構成する`FunctionPassManager`をセットアップする必要がある。 83 | 一度、これを実行すると、我々は、実行したい最適化のセットをmanagerに追加できる。 84 | 最適化したい、それぞれのモジュールごとに新しい`FunctionPassManager`が必要になる。 85 | そのため、モジュールとpass managerの両方を作成し、初期化する関数を書く必要がある。 86 | 87 | ``` 88 | void InitializeModuleAndPassManager(void) { 89 | // Open a new module. 90 | TheModule = llvm::make_unique("my cool jit", TheContext); 91 | 92 | // Create a new pass manager attached to it. 93 | TheFPM = llvm::make_unique(TheModule.get()); 94 | 95 | // Do simple "peephole" optimizations and bit-twiddling optzns. 96 | TheFPM->add(createInstructionCombiningPass()); 97 | // Reassociate expressions. 98 | TheFPM->add(createReassociatePass()); 99 | // Eliminate Common SubExpressions. 100 | TheFPM->add(createGVNPass()); 101 | // Simplify the control flow graph (deleting unreachable blocks, etc). 102 | TheFPM->add(createCFGSimplificationPass()); 103 | 104 | TheFPM->doInitialization(); 105 | } 106 | ``` 107 | 108 | このコードは、全体を管理するモジュール`TheModule`を初期化し、その`TheModule`にアタッチされる`TheFPM`(関数のパスマネージャ)を初期化する。 109 | 一度、パスマネージャがセットアップされると、我々は、llvmパスの塊を追加するために、直列につないだ`add`を使える。 110 | 111 | この例では、4つの最適化passを追加している。 112 | ここで選んだpassは、かなり標準的な, 113 | 様々なコードに使いやすい最適化のセットとなっている。 114 | ここでは、それが何をするのか詳しく説明しないが、信じてほしい、これらのpassは、まず最初に使うのによい例なのだ。 115 | 116 | 一度、`PassManager`がセットアップされると、それを使う準備をする必要がある。 117 | 新しく作った関数が構成された(`FunctionAST::codegen()`の内部で実行される)後に、`PassManager`を実行することでそれができる。 118 | ただし、その関数がクライアント側に返される前に準備を実行しなければならない。 119 | 120 | ``` 121 | if (Value *RetVal = Body->codegen()) { 122 | // Finish off the function. 123 | Builder.CreateRet(RetVal); 124 | 125 | // Validate the generated code, checking for consistency. 126 | verifyFunction(*TheFunction); 127 | 128 | // Optimize the function. 129 | TheFPM->run(*TheFunction); 130 | 131 | return TheFunction; 132 | } 133 | ``` 134 | 135 | 見えればわかるように、このコードはかなり愚直な実装になっている。 136 | `FunctionPasssManager`は、所定のLLVMの`Function*`の実装を改善しながら、最適化し、更新する。 137 | これを用いて、上述のテストを再度実行できる、すなわち、下記のような結果を得られる。 138 | 139 | ``` 140 | ready> def test(x) (1+2+x)*(x+(1+2)); 141 | ready> Read function definition: 142 | define double @test(double %x) { 143 | entry: 144 | %addtmp = fadd double %x, 3.000000e+00 145 | %multmp = fmul double %addtmp, %addtmp 146 | ret double %multmp 147 | } 148 | ``` 149 | 150 | 期待踊りに、この関数のすべての実行から余計な浮動小数点の命令が省かれ、いい感じに最適化されたコードが得られた。 151 | 152 | LLVMは、特定の環境で使われうる多種多様な最適化手法を提供する。 153 | 様々なpassのドキュメントが公開されているが、それらだけでは残念ながら不十分である。 154 | 他の情報源は、`clang`が実行するpassを見ることだ。 155 | `opt`ツールは、コマンドラインから、passを使う体験を提供してくれるので、それらが何をしているかを理解できるだろう。 156 | 157 | さて、今は、我々は、フロントエンドになる合理的なコードを得ることができた。 158 | さぁ、次はそれを実行してみよう。 159 | 160 | ## JITコンパイラを追加する 161 | 162 | LLVM IRで使えるコードは、それに適応できる様々なツールを提供する。 163 | 例えば、上でやったようにさいてきかできるし、テキストやバイナリ形式でダンプできるし、いくつかのターゲット向けにアセンブラにコンパイルできるし、それをJITコンパイルできる。 164 | LLVM IR表現のいいところは、コンパイラの異なるモジュール、パーツ間で`共通に使える通貨`のように振舞ってくれることにある。 165 | 166 | このセクションでは、我々が作ってきた翻訳機にJITコンパイル機能を追加する。 167 | Kaleidoscopeに我々が求めるものは、打ち込んだ関数をそのまま保持?コンパイル?することだが、即座にtop-level表現として、打ち込んだ瞬間から評価もしてほしいのである。 168 | 例えば、もし、`1+2`と打ち込んだ時は、それらを即座に評価し、`3`と出力してほしいのである。もし、入力したものが関数であった場合、コンパイラは、コマンドラインからただちに呼べるようにしてほしいのである。 169 | 170 | これを実行するために、まず最初に、現在のネイティブターゲット向けのコードを生成するための環境を用意し、JITを宣言かつ初期化する。 171 | これは、`InitializeNativeTarget`を呼べば良い。そして、グローバル変数として`TheJIT`を追加し、`main`でそれらを初期化する。 172 | 173 | ``` 174 | static std::unique_ptr TheJIT; 175 | ... 176 | int main() { 177 | InitializeNativeTarget(); 178 | InitializeNativeTargetAsmPrinter(); 179 | InitializeNativeTargetAsmParser(); 180 | 181 | // Install standard binary operators. 182 | // 1 is lowest precedence. 183 | BinopPrecedence['<'] = 10; 184 | BinopPrecedence['+'] = 20; 185 | BinopPrecedence['-'] = 20; 186 | BinopPrecedence['*'] = 40; // highest. 187 | 188 | // Prime the first token. 189 | fprintf(stderr, "ready> "); 190 | getNextToken(); 191 | 192 | TheJIT = llvm::make_unique(); 193 | 194 | // Run the main "interpreter loop" now. 195 | MainLoop(); 196 | 197 | return 0; 198 | } 199 | ``` 200 | 201 | さらに、JITのためのデータレイアウトもセットアップする必要がある。 202 | 203 | ``` 204 | void InitializeModuleAndPassManager(void) { 205 | // Open a new module. 206 | TheModule = llvm::make_unique("my cool jit", TheContext); 207 | TheModule->setDataLayout(TheJIT->getTargetMachine().createDataLayout()); 208 | 209 | // Create a new pass manager attached to it. 210 | TheFPM = llvm::make_unique(TheModule.get()); 211 | ... 212 | ``` 213 | 214 | `KaleidoscopeJIT`クラスは、チュートリアルのために作った特殊な、シンプルなJITである。 215 | このクラスのソースコードは、LLVMのソースの中の`llvm-src/examples/Kaleidoscope/include/KaleidoscopeJIT.h`にある。 216 | 後々、このクラスがどう動くのか、新しい機能でどう拡張されていくのかについて説明するが、今のところ、天下り的に使うことにする。 217 | そのAPIは、非常にシンプルで、`addModule`は,LLVM IRのモジュールをJITに追加し,実行できるようにする.また,`removeModule`は,モジュールが内包するコードに結びつけれられたメモリを開放し,`findSymbol`を使うと,コンパイルされたコードへのポインタを見つけられる. 218 | 219 | 我々は,このシンプルなAPIを使い,以下のようにtop-level表現をパースするコードを変更する. 220 | 221 | ``` 222 | static void HandleTopLevelExpression() { 223 | // Evaluate a top-level expression into an anonymous function. 224 | if (auto FnAST = ParseTopLevelExpr()) { 225 | if (FnAST->codegen()) { 226 | 227 | // JIT the module containing the anonymous expression, keeping a handle so 228 | // we can free it later. 229 | auto H = TheJIT->addModule(std::move(TheModule)); 230 | InitializeModuleAndPassManager(); 231 | 232 | // Search the JIT for the __anon_expr symbol. 233 | auto ExprSymbol = TheJIT->findSymbol("__anon_expr"); 234 | assert(ExprSymbol && "Function not found"); 235 | 236 | // Get the symbol's address and cast it to the right type (takes no 237 | // arguments, returns a double) so we can call it as a native function. 238 | double (*FP)() = (double (*)())(intptr_t)ExprSymbol.getAddress(); 239 | fprintf(stderr, "Evaluated to %f\n", FP()); 240 | 241 | // Delete the anonymous expression module from the JIT. 242 | TheJIT->removeModule(H); 243 | } 244 | ``` 245 | 246 | パースと`codegen`が成功すると,次にJITにtop-level表現を保持するモジュールを追加します. 247 | 我々は,`addModule`を呼び出すことでこれを実行できる. 248 | `addModule`は,モジュール内のすべての関数コード生成のトリガとなり,返されるハンドルは,JITからモジュールを取り除く時に用いる. 249 | 一度,モジュールがJITに追加されると,モジュールはもはや変更できない. 250 | このため,我々は,`InitializeModuleAndPassManager`を呼び出して,引き続くコードを保持する新しいモジュールを開く. 251 | 252 | 一度,モジュールをJITへ追加すると,最後に生成されたコードへのポインタを取得する必要がある. 253 | 我々は,JITの`findSymbol`メソッドを呼び出し,`__anon_expr`というtop-level表現の名前を引き渡し,これを実行する. 254 | この関数をJITに追加するので,`findSymbol`は結果を返すかどうか`assert`でチェックする. 255 | 256 | 次に,シンボル上の`__anon_expr`関数のアドレスを`getAddress()`を使って取得する. 257 | 我々は,引数を取らない,`double`型を返すLLVM関数にtop-level表現をコンパイルすることを思い出して欲しい. 258 | LLVM JITコンパイラは,ネイティブのABIにマッチするため,これは,プログラマは,結果のポインタをその関数のポインタにキャストでき,直接,呼び出すことができる. 259 | つまり,アプリケーションに静的にリンクするようなネイティブのマシンコードと,JITコンパイラが出力するコードには,違いはないのである. 260 | 261 | 最終的に,我々は,top-level表現の再評価をサポートしないため,割り当てられたメモリを開放する時は,JITからモジュールを取り除くことになる. 262 | しかし,思い出して欲しいのは,早い段階で`InitializeModuleAndPassManager`を使って,2,3行のラインを作ったモジュールは,今だに開かれており,新しいコードが追加されるのを待っている状態であることだ. 263 | ** 264 | これらの二つの変更を加えると,Kaleidoscopeがどう動くのかがわかるはずです. 265 | 266 | ``` 267 | ready> 4+5; 268 | Read top-level expression: 269 | define double @0() { 270 | entry: 271 | ret double 9.000000e+00 272 | } 273 | 274 | Evaluated to 9.000000 275 | ``` 276 | 277 | 基本的には,このコードは動きそうです. 278 | 関数のダンプは,入力されたtop-level表現を構成する“no argument function that always returns double”を表示する. 279 | これは,とても基本的な機能のデモだが,それ以上に何かできるだろうか. 280 | 281 | ``` 282 | ready> def testfunc(x y) x + y*2; 283 | Read function definition: 284 | define double @testfunc(double %x, double %y) { 285 | entry: 286 | %multmp = fmul double %y, 2.000000e+00 287 | %addtmp = fadd double %multmp, %x 288 | ret double %addtmp 289 | } 290 | 291 | ready> testfunc(4, 10); 292 | Read top-level expression: 293 | define double @1() { 294 | entry: 295 | %calltmp = call double @testfunc(double 4.000000e+00, double 1.000000e+01) 296 | ret double %calltmp 297 | } 298 | 299 | Evaluated to 24.000000 300 | 301 | ready> testfunc(5, 10); 302 | ready> LLVM ERROR: Program used external function 'testfunc' which could not be resolved! 303 | ``` 304 | 305 | 関数定義と呼び出しは動作するが,最後の行で何か悪いことが起こることになる. 306 | 呼び出しは有効に見えるが,何が起こったんだろうか. 307 | `Module`は,JITのためにメモリを確保したユニット(オブジェクト?)であると,APIから予想されるだろうか? 308 | そして,`testfunc`は,無名な表現を保持する同じモジュールの一部である. 309 | その無名表現を保持するメモリを解放するためにそのモジュールを削除するとき,そのモジュールに含まれる`testfunc`の定義も削除される. 310 | そして,2回目に`testfunc`を読み出そうしたとき,JITは,もはやそれを見つけることはできないのだ. 311 | 312 | これを修正する手っ取り早い方法は,無名表現を関数定義から,別のモジュールに切り出すことだ. 313 | 呼び出されるそれぞれの関数がプロトタイプを持ち,それらが呼び出される前にJITに追加される限り,JITは,幸運にも,モジュールの境界を超えて,関数呼び出しを解決する. 314 | 無名表現を異なるモジュールに動かすことによって,関数定義の残りの部分に手を加えずに,無名表現を削除できる. 315 | 316 | 事実,ここから,すべての関数をそれ自身のモジュールに突っ込むことにする.そうすることで,この環境をよりREPLっぽくする`KaleidoscopeJIT`の便利な性質をうまく活用できるようになる,つまり,(固有の定義を持つ)関数は,JITに(モジュールではなく)何度でも追加することができるのである. 317 | 318 | ``` 319 | ready> def foo(x) x + 1; 320 | Read function definition: 321 | define double @foo(double %x) { 322 | entry: 323 | %addtmp = fadd double %x, 1.000000e+00 324 | ret double %addtmp 325 | } 326 | 327 | ready> foo(2); 328 | Evaluated to 3.000000 329 | 330 | ready> def foo(x) x + 2; 331 | define double @foo(double %x) { 332 | entry: 333 | %addtmp = fadd double %x, 2.000000e+00 334 | ret double %addtmp 335 | } 336 | 337 | ready> foo(2); 338 | Evaluated to 4.000000 339 | ``` 340 | 341 | それぞれの関数をそのモジュール内で有効にさせるため,我々は,それぞれのモジュール内の一つ前の関数宣言を再生成する方法が必要になる. 342 | 343 | ``` 344 | static std::unique_ptr TheJIT; 345 | 346 | ... 347 | 348 | Function *getFunction(std::string Name) { 349 | // First, see if the function has already been added to the current module. 350 | if (auto *F = TheModule->getFunction(Name)) 351 | return F; 352 | 353 | // If not, check whether we can codegen the declaration from some existing 354 | // prototype. 355 | auto FI = FunctionProtos.find(Name); 356 | if (FI != FunctionProtos.end()) 357 | return FI->second->codegen(); 358 | 359 | // If no existing prototype exists, return null. 360 | return nullptr; 361 | } 362 | 363 | ... 364 | 365 | Value *CallExprAST::codegen() { 366 | // Look up the name in the global module table. 367 | Function *CalleeF = getFunction(Callee); 368 | 369 | ... 370 | 371 | Function *FunctionAST::codegen() { 372 | // Transfer ownership of the prototype to the FunctionProtos map, but keep a 373 | // reference to it for use below. 374 | auto &P = *Proto; 375 | FunctionProtos[Proto->getName()] = std::move(Proto); 376 | Function *TheFunction = getFunction(P.getName()); 377 | if (!TheFunction) 378 | return nullptr; 379 | ``` 380 | 381 | このコードは,それぞれの関数の,もっとも最近のプロトタイプ宣言を保持するグローバル変数,`FunctionProtos`を加えることから始まる. 382 | さらに,便利がいいように,`TheModule->getFunction()`の代わりに,`getFunction()`を追加する. 383 | この便利な関数は,既存の関数宣言を探すために,`TheModule`の中を検索し,既存の宣言が見つからない場合,`FunctionProtos`から新しい宣言を生成する. 384 | `In CallExprAST::codegen()`の中にある`getFunction()`を`TheModule->getFunction()`に差し替える.そして,`FunctionAST::codegen()`では,ハッシュである`FunctionProtos`のマッピングを最初にアップデートし,その後に`getFunction()`を呼び出す. 385 | これが完了すると,事前に宣言した関数を,現在モジュール内の関数宣言として得られるようになる. 386 | 387 | さらに,`HandleDefinition`と`HandleExtern`を更新する, 388 | 389 | ``` 390 | static void HandleDefinition() { 391 | if (auto FnAST = ParseDefinition()) { 392 | if (auto *FnIR = FnAST->codegen()) { 393 | fprintf(stderr, "Read function definition:"); 394 | FnIR->print(errs()); 395 | fprintf(stderr, "\n"); 396 | TheJIT->addModule(std::move(TheModule)); 397 | InitializeModuleAndPassManager(); 398 | } 399 | } else { 400 | // Skip token for error recovery. 401 | getNextToken(); 402 | } 403 | } 404 | 405 | static void HandleExtern() { 406 | if (auto ProtoAST = ParseExtern()) { 407 | if (auto *FnIR = ProtoAST->codegen()) { 408 | fprintf(stderr, "Read extern: "); 409 | FnIR->print(errs()); 410 | fprintf(stderr, "\n"); 411 | FunctionProtos[ProtoAST->getName()] = std::move(ProtoAST); 412 | } 413 | } else { 414 | // Skip token for error recovery. 415 | getNextToken(); 416 | } 417 | } 418 | ``` 419 | 420 | ‌`HandleDefinition`内部で,新しく定義された関数をJITへ転送するための2行を追加し,新しいモジュールを開く. 421 | `HandleExtern`内部で,`FunctionProtos`へプロトタイプ宣言を追加する1行を追加する. 422 | 423 | これらの変更の後,REPLに再度挑戦してみよう(無名関数のダンプを削除し,ここまでにいいアイデアを考えてみて欲しい). 424 | 425 | ``` 426 | ready> def foo(x) x + 1; 427 | ready> foo(2); 428 | Evaluated to 3.000000 429 | 430 | ready> def foo(x) x + 2; 431 | ready> foo(2); 432 | Evaluated to 4.000000 433 | ``` 434 | 435 | 動いた! 436 | 437 | このシンプルなコードでさえ,すごい能力を発揮する・・・下の結果を見て欲しい. 438 | 439 | ``` 440 | ready> extern sin(x); 441 | Read extern: 442 | declare double @sin(double) 443 | 444 | ready> extern cos(x); 445 | Read extern: 446 | declare double @cos(double) 447 | 448 | ready> sin(1.0); 449 | Read top-level expression: 450 | define double @2() { 451 | entry: 452 | ret double 0x3FEAED548F090CEE 453 | } 454 | 455 | Evaluated to 0.841471 456 | 457 | ready> def foo(x) sin(x)*sin(x) + cos(x)*cos(x); 458 | Read function definition: 459 | define double @foo(double %x) { 460 | entry: 461 | %calltmp = call double @sin(double %x) 462 | %multmp = fmul double %calltmp, %calltmp 463 | %calltmp2 = call double @cos(double %x) 464 | %multmp4 = fmul double %calltmp2, %calltmp2 465 | %addtmp = fadd double %multmp, %multmp4 466 | ret double %addtmp 467 | } 468 | 469 | ready> foo(4.0); 470 | Read top-level expression: 471 | define double @3() { 472 | entry: 473 | %calltmp = call double @foo(double 4.000000e+00) 474 | ret double %calltmp 475 | } 476 | 477 | Evaluated to 1.000000 478 | ``` 479 | 480 | ちょっと待って欲しい,JITは,`sin`と`cos`をどうやって知ったのだろうか.その答えは,驚くほどシンプルだ. 481 | `KaleidoscopeJIT`は,指定されたモジュール内で有効でないシンボルを見つけるために使う指定された愚直なシンボルの名前解決のルールを持つ. 482 | `KaleidoscopeJIT`は,もっとも新しい定義を見つけるため,JITにすでに追加されたすべてのモジュールの中を〜最後に追加されたものから順にもっとも古く追加されたものまで〜探索する. 483 | もし,JITの中で関数定義が見つけられなかった場合,`Kaleidoscope`プロセス自身の上で`dlsym("sin")`を呼び出し,それをfallbackする. 484 | JITのアドレス空間の中で`sin`が定義されたため,`KaleidoscopeJIT`は,直接,`sin`の`limb`バージョンを呼び出すように,モジュール内の呼び出しを修正する. 485 | しかし,いくつかのケースで,これは`sin`と`cos`は,一般的な数学の関数の名前なので,`constant folder`は,上で実装した`sin(1.0)`の中のように,定数を使って呼ばれるとき,正しい結果へ直接関数呼び出しを評価することになる. 486 | 487 | 将来,このシンボル解決のルールの微調整することで,セキュリティ(JITのコードで使えるシンボルを制限する)から,シンボル名をベースにした動的なコード生成,遅延評価(lazy complication)などの,様々な便利機能を提供する方法を理解できるようになるだろう. 488 | 489 | 即物的なシンボル解決ルールの利点は,operation(操作?なんの?)を実装する任意のC++のコードを書くことで,言語を拡張することができる. 490 | 例えば, 491 | 492 | ``` 493 | #ifdef _WIN32 494 | #define DLLEXPORT __declspec(dllexport) 495 | #else 496 | #define DLLEXPORT 497 | #endif 498 | 499 | /// putchard - putchar that takes a double and returns 0. 500 | extern "C" DLLEXPORT double putchard(double X) { 501 | fputc((char)X, stderr); 502 | return 0; 503 | } 504 | ``` 505 | 506 | 上のコードで,エラーコンソールに`double`の値を出力する関数を追加できる. 507 | Windowsの場合,動的シンボルローダが`GetProcAddress`を使ってシンボルを見つけるため,実際に関数をエクスポートする必要があることに注意されたい. 508 | 509 | 今, 510 | 511 | ``` 512 | extern putchard(x); putchard(120); 513 | ``` 514 | 515 | のように書けば,コンソールに文字を文字コードを指定して出力できるようになった(つまり,120は'x'のアスキーコードなので,このコードの場合,コンソールには小文字のxが出力される). 516 | 同じようなコードで,ファイルI/Oも実現できるし,多くの機能を`Kaleidoscope`で実装できるのである. 517 | 518 | これで,JITと最適化の章を終わりにします. 519 | この時点で,チューリング完全ではないプログラミング言語をコンパイルできるようになり,ユーザのやりたいように最適化し,JITコンパイルできるようになった. 520 | 次は,言語に制御フローを持たせ,その方法にそって,LLVM IRのおもしろい課題にトライしていくことにしよう. 521 | 522 | ## コードリスト 523 | 524 | ``` 525 | # Compile 526 | clang++ -g toy.cpp `llvm-config --cxxflags --ldflags --system-libs --libs core mcjit native` -O3 -o toy 527 | # Run 528 | ./toy 529 | ``` 530 | 531 | もし,Linuxでコンパイルする場合は,`-rdynamic`オプションを加える必要がある.これは,ランタイム時に正しく外部関数を解決するためである. 532 | 533 | ### ビルド方法・・・訳者追記 534 | ビルドには,llvmのソースに含まれる`Kaleidoscope`の[ソースコード](hhttps://github.com/llvm-mirror/llvm/blob/release_70/examples/Kaleidoscope/)の中のヘッダが必要である. 535 | ヘッダは,[ここ](https://github.com/llvm-mirror/llvm/blob/release_70/examples/Kaleidoscope/include/)にある. 536 | この最新版(現状,brewでインストールされるllvmはv7.0なので,`release_70`のブランチ)のヘッダをダウンロードする. 537 | しかし,このままでは,ビルドできない環境もあるようだ. 538 | そのときは,`llvm-config`のオプションに`orcjit`を加えるとよいようだ[^https://twitter.com/giginet/status/1080744056722878464]. 539 | ゆえに,コードリストは,以下のコマンドでビルドするとよい. 540 | 541 | ``` 542 | clang++ -g tutorial04.cpp `llvm-config --cxxflags --ldflags --system-libs --libs core orcjit mcjit native` -O3 -o tutorial04 543 | ``` 544 | 545 | ### コードリスト 546 | [ソース](https://github.com/sonsongithub/llvm-tutorial/blob/master/tutorial04.cpp) --------------------------------------------------------------------------------