├── LICENSE.md ├── include └── codegen │ ├── literals.hpp │ ├── module.hpp │ ├── utils.hpp │ ├── compiler.hpp │ ├── variable.hpp │ ├── builtin.hpp │ ├── relational_ops.hpp │ ├── statements.hpp │ ├── arithmetic_ops.hpp │ └── module_builder.hpp ├── src ├── os.hpp ├── statements.cpp ├── module_builder.cpp └── compiler.cpp ├── tests ├── CMakeLists.txt ├── variable.cpp ├── builtin.cpp ├── module_builder.cpp ├── relational_ops.cpp ├── statements.cpp ├── arithmetic_ops.cpp └── examples.cpp ├── CMakeLists.txt ├── .travis.yml ├── .clang-format └── README.md /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /include/codegen/literals.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "module_builder.hpp" 4 | 5 | namespace codegen::literals { 6 | 7 | value operator""_i8(unsigned long long v) { 8 | return constant(v); 9 | } 10 | value operator""_i16(unsigned long long v) { 11 | return constant(v); 12 | } 13 | value operator""_i32(unsigned long long v) { 14 | return constant(v); 15 | } 16 | value operator""_i64(unsigned long long v) { 17 | return constant(v); 18 | } 19 | 20 | value operator""_u8(unsigned long long v) { 21 | return constant(v); 22 | } 23 | value operator""_u16(unsigned long long v) { 24 | return constant(v); 25 | } 26 | value operator""_u32(unsigned long long v) { 27 | return constant(v); 28 | } 29 | value operator""_u64(unsigned long long v) { 30 | return constant(v); 31 | } 32 | 33 | value operator""_f32(long double v) { 34 | return constant(v); 35 | } 36 | value operator""_f64(long double v) { 37 | return constant(v); 38 | } 39 | 40 | } // namespace codegen::literals -------------------------------------------------------------------------------- /src/os.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2019 Paweł Dziepak 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | #include 24 | #include 25 | 26 | #include 27 | 28 | namespace codegen { 29 | 30 | namespace { 31 | 32 | #if __linux__ 33 | inline std::string get_process_name() { 34 | auto ifs = std::ifstream("/proc/self/comm"); 35 | auto str = std::string{}; 36 | std::getline(ifs, str); 37 | return str; 38 | } 39 | #elif __APPLE__ 40 | inline std::string get_process_name() { 41 | return std::string(getprogname()); 42 | } 43 | #else 44 | #error Unsupported operating system. 45 | #endif 46 | 47 | } // namespace 48 | 49 | } // namespace codegen 50 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright © 2019 Paweł Dziepak 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | # SOFTWARE. 21 | 22 | find_package(GTest REQUIRED) 23 | 24 | function(codegen_add_test TESTNAME SOURCE) 25 | add_executable(${TESTNAME} ${SOURCE} ${ARGN}) 26 | target_link_libraries(${TESTNAME} codegen GTest::GTest GTest::Main ${CODEGEN_CXX_FLAGS}) 27 | target_compile_options(${TESTNAME} PRIVATE ${CODEGEN_CXX_FLAGS}) 28 | add_test(${TESTNAME} ${TESTNAME}) 29 | endfunction(codegen_add_test) 30 | 31 | codegen_add_test(builtin builtin.cpp) 32 | codegen_add_test(arithmetic_ops arithmetic_ops.cpp) 33 | codegen_add_test(examples examples.cpp) 34 | codegen_add_test(module_builder module_builder.cpp) 35 | codegen_add_test(relational_ops relational_ops.cpp) 36 | codegen_add_test(statements statements.cpp) 37 | codegen_add_test(variable variable.cpp) 38 | -------------------------------------------------------------------------------- /include/codegen/module.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2019 Paweł Dziepak 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | #pragma once 24 | 25 | namespace codegen { 26 | 27 | template class function_ref; 28 | 29 | class module { 30 | llvm::orc::ExecutionSession* session_; 31 | 32 | llvm::orc::MangleAndInterner mangle_; 33 | 34 | private: 35 | module(llvm::orc::ExecutionSession&, llvm::DataLayout const&); 36 | 37 | void* get_address(std::string const&); 38 | 39 | friend class module_builder; 40 | 41 | public: 42 | module(module const&) = delete; 43 | module(module&&) = delete; 44 | 45 | template 46 | auto get_address(function_ref const& fn) { 47 | return reinterpret_cast(get_address(fn.name())); 48 | } 49 | }; 50 | 51 | } // namespace codegen 52 | -------------------------------------------------------------------------------- /src/statements.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2019 Paweł Dziepak 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | #include "codegen/statements.hpp" 24 | 25 | namespace codegen { 26 | 27 | void break_() { 28 | auto& mb = *detail::current_builder; 29 | assert(mb.current_loop_.break_block_); 30 | 31 | mb.exited_block_ = true; 32 | 33 | auto line_no = mb.source_code_.add_line("break;"); 34 | mb.ir_builder_.SetCurrentDebugLocation(llvm::DebugLoc::get(line_no, 1, mb.dbg_scope_)); 35 | 36 | mb.ir_builder_.CreateBr(mb.current_loop_.break_block_); 37 | } 38 | 39 | void continue_() { 40 | auto& mb = *detail::current_builder; 41 | assert(mb.current_loop_.continue_block_); 42 | 43 | mb.exited_block_ = true; 44 | 45 | auto line_no = mb.source_code_.add_line("continue;"); 46 | mb.ir_builder_.SetCurrentDebugLocation(llvm::DebugLoc::get(line_no, 1, mb.dbg_scope_)); 47 | 48 | mb.ir_builder_.CreateBr(mb.current_loop_.continue_block_); 49 | } 50 | 51 | } // namespace codegen 52 | -------------------------------------------------------------------------------- /include/codegen/utils.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2019 Paweł Dziepak 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | #pragma once 24 | 25 | #include 26 | 27 | #include 28 | #include 29 | 30 | namespace codegen { 31 | 32 | namespace detail { 33 | 34 | static inline std::atomic id_counter{}; 35 | 36 | } 37 | 38 | class llvm_error : public std::runtime_error { 39 | public: 40 | explicit llvm_error(llvm::Error err) 41 | : std::runtime_error([&] { 42 | auto str = std::string{"LLVM Error: "}; 43 | auto os = llvm::raw_string_ostream(str); 44 | os << err; 45 | os.str(); 46 | return str; 47 | }()) {} 48 | }; 49 | 50 | inline void throw_on_error(llvm::Error err) { 51 | if (err) { throw llvm_error(std::move(err)); } 52 | } 53 | 54 | template T unwrap(llvm::Expected value) { 55 | if (!value) { throw llvm_error(value.takeError()); } 56 | return std::move(*value); 57 | } 58 | 59 | } // namespace codegen 60 | -------------------------------------------------------------------------------- /tests/variable.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2019 Paweł Dziepak 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | #include "codegen/variable.hpp" 24 | 25 | #include 26 | 27 | #include "codegen/arithmetic_ops.hpp" 28 | #include "codegen/compiler.hpp" 29 | #include "codegen/module.hpp" 30 | #include "codegen/module_builder.hpp" 31 | 32 | TEST(variable, set_get) { 33 | auto comp = codegen::compiler{}; 34 | auto builder = codegen::module_builder(comp, "set_get"); 35 | 36 | auto set_get = builder.create_function("set_get_fn", [](codegen::value x) { 37 | auto y = codegen::variable{"y"}; 38 | y.set(x); 39 | auto z = codegen::variable{"z"}; 40 | auto expr = y.get() + codegen::constant(1); 41 | y.set(codegen::constant(4)); 42 | z.set(expr); 43 | codegen::return_(y.get() + z.get()); 44 | }); 45 | 46 | auto module = std::move(builder).build(); 47 | 48 | auto set_get_ptr = module.get_address(set_get); 49 | EXPECT_EQ(set_get_ptr(8), 13); 50 | } 51 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright © 2019 Paweł Dziepak 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | # SOFTWARE. 21 | 22 | cmake_minimum_required(VERSION 3.12) 23 | 24 | project(codegen 25 | LANGUAGES CXX 26 | ) 27 | 28 | include(CTest) 29 | 30 | option(CODEGEN_SANITIZERS "Build with AddressSanitizer and UndefinedBehaviorSanitizer." ON) 31 | 32 | list(APPEND CODEGEN_CXX_FLAGS -Wall -Wextra -Werror -Wno-unused-parameter) 33 | if (CODEGEN_SANITIZERS) 34 | list(APPEND CODEGEN_CXX_DEBUG_FLAGS -fsanitize=address -fsanitize=undefined) 35 | endif() 36 | 37 | if(CMAKE_BUILD_TYPE STREQUAL Debug) 38 | list(APPEND CODEGEN_CXX_FLAGS ${CODEGEN_CXX_DEBUG_FLAGS}) 39 | else() 40 | list(APPEND CODEGEN_CXX_FLAGS ${CODEGEN_CXX_RELEASE_FLAGS}) 41 | endif() 42 | 43 | set(CODEGEN_CXX_FILESYSTEM stdc++fs) 44 | if(APPLE) 45 | set(CODEGEN_CXX_FILESYSTEM c++fs) 46 | endif() 47 | 48 | find_package(LLVM 8 REQUIRED) 49 | find_package(fmt REQUIRED) 50 | 51 | add_library(codegen 52 | src/compiler.cpp 53 | src/module_builder.cpp 54 | src/statements.cpp 55 | ) 56 | target_include_directories(codegen PUBLIC 57 | $ 58 | ${LLVM_INCLUDE_DIR} 59 | ) 60 | target_compile_features(codegen PUBLIC cxx_std_17) 61 | target_link_libraries(codegen PRIVATE ${CODEGEN_CXX_FLAGS}) 62 | target_compile_options(codegen PRIVATE ${CODEGEN_CXX_FLAGS}) 63 | target_link_libraries(codegen PUBLIC LLVM fmt::fmt ${CODEGEN_CXX_FILESYSTEM}) 64 | 65 | if(BUILD_TESTING) 66 | add_subdirectory(tests) 67 | endif() 68 | -------------------------------------------------------------------------------- /include/codegen/compiler.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2019 Paweł Dziepak 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | #pragma once 24 | 25 | #include 26 | #include 27 | 28 | #include 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include "utils.hpp" 38 | 39 | namespace codegen { 40 | 41 | class compiler { 42 | llvm::orc::ExecutionSession session_; 43 | 44 | llvm::DataLayout data_layout_; 45 | std::unique_ptr target_machine_; 46 | 47 | llvm::orc::MangleAndInterner mangle_; 48 | 49 | llvm::orc::RTDyldObjectLinkingLayer object_layer_; 50 | llvm::orc::IRCompileLayer compile_layer_; 51 | llvm::orc::IRTransformLayer optimize_layer_; 52 | 53 | llvm::JITEventListener* gdb_listener_; 54 | 55 | std::filesystem::path source_directory_; 56 | 57 | std::vector loaded_modules_; 58 | 59 | std::unordered_map external_symbols_; 60 | llvm::orc::DynamicLibrarySearchGenerator dynlib_generator_; 61 | 62 | friend class module_builder; 63 | 64 | private: 65 | explicit compiler(llvm::orc::JITTargetMachineBuilder); 66 | 67 | public: 68 | compiler(); 69 | ~compiler(); 70 | 71 | compiler(compiler const&) = delete; 72 | compiler(compiler&&) = delete; 73 | 74 | void add_symbol(std::string const& name, void* address); 75 | 76 | private: 77 | llvm::Expected optimize_module(llvm::orc::ThreadSafeModule, 78 | llvm::orc::MaterializationResponsibility const&); 79 | }; 80 | 81 | } // namespace codegen 82 | -------------------------------------------------------------------------------- /tests/builtin.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2019 Paweł Dziepak 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | #include "codegen/builtin.hpp" 24 | 25 | #include 26 | 27 | #include "codegen/compiler.hpp" 28 | #include "codegen/module.hpp" 29 | #include "codegen/module_builder.hpp" 30 | #include "codegen/statements.hpp" 31 | 32 | TEST(builtin, memcpy) { 33 | auto comp = codegen::compiler{}; 34 | auto builder = codegen::module_builder(comp, "memcpy"); 35 | 36 | auto memcpy_i32 = builder.create_function( 37 | "memcpy_i32", [](codegen::value src, codegen::value dst) { 38 | codegen::builtin::memcpy(dst, src, codegen::constant(4)); 39 | codegen::return_(); 40 | }); 41 | 42 | auto memcpy_any = builder.create_function( 43 | "memcpy_any", [](codegen::value src, codegen::value dst, codegen::value(n)) { 44 | codegen::builtin::memcpy(dst, src, n); 45 | codegen::return_(); 46 | }); 47 | 48 | int32_t i32_src = 0x1234abcd; 49 | int32_t i32_dst = 0x77776666; 50 | 51 | auto module = std::move(builder).build(); 52 | 53 | auto memcpy_i32_ptr = module.get_address(memcpy_i32); 54 | memcpy_i32_ptr(&i32_src, &i32_dst); 55 | EXPECT_EQ(i32_src, i32_dst); 56 | EXPECT_EQ(i32_dst, 0x1234abcd); 57 | 58 | i32_src = 0x34569876; 59 | auto memcpy_any_ptr = module.get_address(memcpy_any); 60 | memcpy_any_ptr(&i32_src, &i32_dst, 4); 61 | EXPECT_EQ(i32_src, i32_dst); 62 | EXPECT_EQ(i32_dst, 0x34569876); 63 | } 64 | 65 | TEST(builtin, bswap) { 66 | auto comp = codegen::compiler{}; 67 | auto builder = codegen::module_builder(comp, "bswap"); 68 | 69 | auto bswap_i32 = builder.create_function( 70 | "bswap_i32", [](codegen::value v) { codegen::return_(codegen::builtin::bswap(v)); }); 71 | auto module = std::move(builder).build(); 72 | 73 | auto bswap_i32_ptr = module.get_address(bswap_i32); 74 | EXPECT_EQ(bswap_i32_ptr(0x12345678), 0x78563412); 75 | } 76 | -------------------------------------------------------------------------------- /include/codegen/variable.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2019 Paweł Dziepak 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | #pragma once 24 | 25 | #include "codegen/module_builder.hpp" 26 | 27 | namespace codegen { 28 | 29 | template class variable { 30 | llvm::AllocaInst* variable_; 31 | std::string name_; 32 | 33 | public: 34 | static_assert(!std::is_const_v); 35 | static_assert(!std::is_volatile_v); 36 | 37 | explicit variable(std::string const& n) : name_(n) { 38 | auto& mb = *detail::current_builder; 39 | 40 | auto alloca_builder = llvm::IRBuilder<>(&mb.function_->getEntryBlock(), mb.function_->getEntryBlock().begin()); 41 | variable_ = alloca_builder.CreateAlloca(detail::type::llvm(), nullptr, name_); 42 | 43 | auto line_no = mb.source_code_.add_line(fmt::format("{} {};", detail::type::name(), name_)); 44 | auto dbg_variable = 45 | mb.dbg_builder_.createAutoVariable(mb.dbg_scope_, name_, mb.dbg_file_, line_no, detail::type::dbg()); 46 | mb.dbg_builder_.insertDeclare(variable_, dbg_variable, mb.dbg_builder_.createExpression(), 47 | llvm::DebugLoc::get(line_no, 1, mb.dbg_scope_), mb.ir_builder_.GetInsertBlock()); 48 | } 49 | 50 | template explicit variable(std::string const& n, Value const& v) : variable(n) { set(v); } 51 | 52 | variable(variable const&) = delete; 53 | variable(variable&&) = delete; 54 | 55 | value get() const { 56 | auto v = detail::current_builder->ir_builder_.CreateAlignedLoad(variable_, detail::type::alignment); 57 | return value{v, name_}; 58 | } 59 | 60 | template void set(Value const& v) { 61 | static_assert(std::is_same_v); 62 | auto& mb = *detail::current_builder; 63 | auto line_no = mb.source_code_.add_line(fmt::format("{} = {};", name_, v)); 64 | mb.ir_builder_.SetCurrentDebugLocation(llvm::DebugLoc::get(line_no, 1, mb.dbg_scope_)); 65 | mb.ir_builder_.CreateAlignedStore(v.eval(), variable_, detail::type::alignment); 66 | } 67 | }; 68 | 69 | } // namespace codegen 70 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | dist: xenial 3 | 4 | matrix: 5 | include: 6 | - os: linux 7 | compiler: clang 8 | addons: &clang 9 | apt: 10 | sources: 11 | - ubuntu-toolchain-r-test 12 | - llvm-toolchain-xenial-8 13 | packages: 14 | - g++-8 15 | - llvm-8-dev 16 | - clang-8 17 | - clang++-8 18 | - lcov 19 | env: 20 | - MATRIX_EVAL="CC=clang-8 && CXX=clang++-8" 21 | - BUILD_TYPE=Release 22 | 23 | - os: linux 24 | compiler: clang 25 | addons: *clang 26 | env: 27 | - MATRIX_EVAL="CC=clang-8 && CXX=clang++-8" 28 | - BUILD_TYPE=Debug 29 | 30 | - os: linux 31 | compiler: clang 32 | addons: *clang 33 | env: 34 | - COVERAGE=1 35 | - MATRIX_EVAL="CC=clang-8 && CXX=clang++-8" 36 | - BUILD_TYPE=Debug 37 | - CXX_FLAGS=--coverage 38 | 39 | - os: linux 40 | compiler: gcc 41 | addons: &gcc8 42 | apt: 43 | sources: 44 | - ubuntu-toolchain-r-test 45 | - llvm-toolchain-xenial-8 46 | packages: 47 | - g++-8 48 | - llvm-8-dev 49 | env: 50 | - MATRIX_EVAL="CC=gcc-8 && CXX=g++-8" 51 | - BUILD_TYPE=Release 52 | 53 | - os: linux 54 | compiler: gcc 55 | addons: *gcc8 56 | env: 57 | - MATRIX_EVAL="CC=gcc-8 && CXX=g++-8" 58 | - BUILD_TYPE=Debug 59 | 60 | - os: osx 61 | compiler: clang 62 | osx_image: xcode10.2 63 | env: 64 | - BUILD_TYPE=Release 65 | 66 | - os: osx 67 | compiler: clang 68 | osx_image: xcode10.2 69 | env: 70 | - BUILD_TYPE=Debug 71 | 72 | install: 73 | - eval "${MATRIX_EVAL}" 74 | - if [ $TRAVIS_OS_NAME = 'osx' ]; then 75 | brew install llvm@8; 76 | export PATH="/usr/local/opt/llvm/bin:$PATH"; 77 | export LDFLAGS="-L/usr/local/opt/llvm/lib -Wl,-rpath,/usr/local/opt/llvm/lib"; 78 | fi 79 | - mkdir build 80 | - cd build 81 | - (curl https://codeload.github.com/google/googletest/tar.gz/release-1.8.1 | tar -xzf - && cd googletest-release-1.8.1 && mkdir build && cd build && cmake .. -DCMAKE_BUILD_TYPE=Release && make && sudo make install); 82 | - (curl https://codeload.github.com/fmtlib/fmt/tar.gz/5.3.0 | tar -xzf - && cd fmt-5.3.0 && mkdir build && cd build && cmake .. -DCMAKE_BUILD_TYPE=Release -DFMT_TEST=OFF -DFMT_DOC=OFF && make && sudo make install); 83 | 84 | script: 85 | - cmake .. -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DCODEGEN_SANITIZERS=OFF -DCMAKE_CXX_FLAGS=${CXX_FLAGS} 86 | - make VERBOSE=1 87 | - make test 88 | 89 | after_script: 90 | - if [[ "${COVERAGE}" == 1 ]]; then 91 | cd ${TRAVIS_BUILD_DIR}; 92 | echo -e '#!/bin/bash\nexec llvm-cov-8 gcov "$@"' > ${TRAVIS_BUILD_DIR}/llvm-gcov.sh; 93 | chmod +x ${TRAVIS_BUILD_DIR}/llvm-gcov.sh; 94 | lcov --directory . --capture --output-file coverage.info --gcov-tool ${TRAVIS_BUILD_DIR}/llvm-gcov.sh; 95 | lcov --remove coverage.info '/usr/*' --output-file coverage.info; 96 | lcov --list coverage.info; 97 | bash <(curl -s https://codecov.io/bash) -x ${TRAVIS_BUILD_DIR}/llvm-gcov.sh || echo "Codecov did not collect coverage reports"; 98 | fi -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | # BasedOnStyle: LLVM 4 | AccessModifierOffset: -2 5 | AlignAfterOpenBracket: Align 6 | AlignConsecutiveAssignments: false 7 | AlignConsecutiveDeclarations: false 8 | AlignEscapedNewlines: Right 9 | AlignOperands: true 10 | AlignTrailingComments: true 11 | AllowAllParametersOfDeclarationOnNextLine: true 12 | AllowShortBlocksOnASingleLine: true 13 | AllowShortCaseLabelsOnASingleLine: true 14 | AllowShortFunctionsOnASingleLine: InlineOnly 15 | AllowShortIfStatementsOnASingleLine: true 16 | AllowShortLoopsOnASingleLine: true 17 | AlwaysBreakAfterDefinitionReturnType: None 18 | AlwaysBreakAfterReturnType: None 19 | AlwaysBreakBeforeMultilineStrings: false 20 | AlwaysBreakTemplateDeclarations: false 21 | BinPackArguments: true 22 | BinPackParameters: true 23 | BraceWrapping: 24 | AfterClass: false 25 | AfterControlStatement: false 26 | AfterEnum: false 27 | AfterFunction: false 28 | AfterNamespace: false 29 | AfterObjCDeclaration: false 30 | AfterStruct: false 31 | AfterUnion: false 32 | AfterExternBlock: false 33 | BeforeCatch: false 34 | BeforeElse: false 35 | IndentBraces: false 36 | SplitEmptyFunction: true 37 | SplitEmptyRecord: true 38 | SplitEmptyNamespace: true 39 | BreakBeforeBinaryOperators: None 40 | BreakBeforeBraces: Attach 41 | BreakBeforeInheritanceComma: false 42 | BreakBeforeTernaryOperators: true 43 | BreakConstructorInitializersBeforeComma: false 44 | BreakConstructorInitializers: BeforeColon 45 | BreakAfterJavaFieldAnnotations: false 46 | BreakStringLiterals: true 47 | ColumnLimit: 120 48 | CommentPragmas: '^ IWYU pragma:' 49 | CompactNamespaces: false 50 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 51 | ConstructorInitializerIndentWidth: 4 52 | ContinuationIndentWidth: 4 53 | Cpp11BracedListStyle: true 54 | DerivePointerAlignment: false 55 | DisableFormat: false 56 | ExperimentalAutoDetectBinPacking: false 57 | FixNamespaceComments: true 58 | ForEachMacros: 59 | - foreach 60 | - Q_FOREACH 61 | - BOOST_FOREACH 62 | IncludeBlocks: Preserve 63 | IncludeCategories: 64 | - Regex: '^"(llvm|llvm-c|clang|clang-c)/' 65 | Priority: 2 66 | - Regex: '^(<|"(gtest|gmock|isl|json)/)' 67 | Priority: 3 68 | - Regex: '.*' 69 | Priority: 1 70 | IncludeIsMainRegex: '(Test)?$' 71 | IndentCaseLabels: false 72 | IndentPPDirectives: None 73 | IndentWidth: 2 74 | IndentWrappedFunctionNames: false 75 | JavaScriptQuotes: Leave 76 | JavaScriptWrapImports: true 77 | KeepEmptyLinesAtTheStartOfBlocks: true 78 | MacroBlockBegin: '' 79 | MacroBlockEnd: '' 80 | MaxEmptyLinesToKeep: 1 81 | NamespaceIndentation: None 82 | ObjCBlockIndentWidth: 2 83 | ObjCSpaceAfterProperty: false 84 | ObjCSpaceBeforeProtocolList: true 85 | PenaltyBreakAssignment: 2 86 | PenaltyBreakBeforeFirstCallParameter: 19 87 | PenaltyBreakComment: 300 88 | PenaltyBreakFirstLessLess: 120 89 | PenaltyBreakString: 1000 90 | PenaltyExcessCharacter: 1000000 91 | PenaltyReturnTypeOnItsOwnLine: 60 92 | PointerAlignment: Left 93 | ReflowComments: true 94 | SortIncludes: true 95 | SortUsingDeclarations: true 96 | SpaceAfterCStyleCast: false 97 | SpaceAfterTemplateKeyword: false 98 | SpaceBeforeAssignmentOperators: true 99 | SpaceBeforeParens: ControlStatements 100 | SpaceInEmptyParentheses: false 101 | SpacesBeforeTrailingComments: 1 102 | SpacesInAngles: false 103 | SpacesInContainerLiterals: true 104 | SpacesInCStyleCastParentheses: false 105 | SpacesInParentheses: false 106 | SpacesInSquareBrackets: false 107 | Standard: Cpp11 108 | TabWidth: 8 109 | UseTab: Never 110 | ... 111 | 112 | -------------------------------------------------------------------------------- /src/module_builder.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2019 Paweł Dziepak 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | #include "codegen/module_builder.hpp" 24 | 25 | #include 26 | 27 | #include 28 | 29 | #include "codegen/compiler.hpp" 30 | #include "codegen/module.hpp" 31 | 32 | namespace codegen { 33 | 34 | module_builder::module_builder(compiler& c, std::string const& name) 35 | : compiler_(&c), context_(std::make_unique()), 36 | module_(std::make_unique(name, *context_)), ir_builder_(*context_), 37 | source_file_(c.source_directory_ / (name + ".txt")), dbg_builder_(*module_), 38 | dbg_file_(dbg_builder_.createFile(source_file_.string(), source_file_.parent_path().string())), 39 | dbg_scope_(dbg_file_) { 40 | dbg_builder_.createCompileUnit(llvm::dwarf::DW_LANG_C_plus_plus, dbg_file_, "codegen", true, "", 0); 41 | } 42 | 43 | module module_builder::build() && { 44 | { 45 | auto ofs = std::ofstream(source_file_, std::ios::trunc); 46 | ofs << source_code_.get(); 47 | } 48 | 49 | dbg_builder_.finalize(); 50 | 51 | auto target_triple = compiler_->target_machine_->getTargetTriple(); 52 | module_->setDataLayout(compiler_->data_layout_); 53 | module_->setTargetTriple(target_triple.str()); 54 | 55 | throw_on_error(compiler_->optimize_layer_.add(compiler_->session_.getMainJITDylib(), 56 | llvm::orc::ThreadSafeModule(std::move(module_), std::move(context_)))); 57 | return module{compiler_->session_, compiler_->data_layout_}; 58 | } 59 | 60 | void module_builder::set_function_attributes(llvm::Function* fn) { 61 | fn->addFnAttr("target-cpu", llvm::sys::getHostCPUName()); 62 | } 63 | 64 | unsigned module_builder::source_code_generator::add_line(std::string const& line) { 65 | source_code_ << std::string(indent_, ' ') << line << "\n"; 66 | return line_no_++; 67 | } 68 | 69 | std::string module_builder::source_code_generator::get() const { 70 | return source_code_.str(); 71 | } 72 | 73 | void module_builder::declare_external_symbol(std::string const& name, void* address) { 74 | compiler_->add_symbol(name, address); 75 | } 76 | 77 | std::ostream& operator<<(std::ostream& os, module_builder const& mb) { 78 | auto llvm_os = llvm::raw_os_ostream(os); 79 | mb.module_->print(llvm_os, nullptr); 80 | return os; 81 | } 82 | 83 | value true_() { 84 | return constant(true); 85 | } 86 | value false_() { 87 | return constant(false); 88 | } 89 | 90 | void return_() { 91 | auto& mb = *detail::current_builder; 92 | auto line_no = mb.source_code_.add_line("return;"); 93 | mb.exited_block_ = true; 94 | mb.ir_builder_.SetCurrentDebugLocation(llvm::DebugLoc::get(line_no, 1, mb.dbg_scope_)); 95 | mb.ir_builder_.CreateRetVoid(); 96 | } 97 | 98 | } // namespace codegen 99 | -------------------------------------------------------------------------------- /include/codegen/builtin.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2019 Paweł Dziepak 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | #pragma once 24 | 25 | #include "codegen/module_builder.hpp" 26 | 27 | namespace codegen::builtin { 28 | 29 | template void memcpy(Destination dst, Source src, Size n) { 30 | static_assert(std::is_pointer_v); 31 | static_assert(std::is_pointer_v); 32 | static_assert(std::is_same_v || 33 | std::is_same_v); 34 | 35 | using namespace detail; 36 | auto& mb = *detail::current_builder; 37 | 38 | auto line_no = mb.source_code_.add_line(fmt::format("memcpy({}, {}, {});", dst, src, n)); 39 | mb.ir_builder_.SetCurrentDebugLocation(llvm::DebugLoc::get(line_no, 1, mb.dbg_scope_)); 40 | mb.ir_builder_.CreateMemCpy(dst.eval(), detail::type::alignment, src.eval(), 41 | detail::type::alignment, n.eval()); 42 | } 43 | 44 | template value memcmp(Source1 src1, Source2 src2, Size n) { 45 | static_assert(std::is_pointer_v); 46 | static_assert(std::is_pointer_v); 47 | 48 | using namespace detail; 49 | auto& mb = *detail::current_builder; 50 | 51 | auto fn_type = llvm::FunctionType::get(type::llvm(), 52 | {type::llvm(), type::llvm(), type::llvm()}, false); 53 | auto fn = 54 | llvm::Function::Create(fn_type, llvm::GlobalValue::LinkageTypes::ExternalLinkage, "memcmp", mb.module_.get()); 55 | 56 | auto line_no = mb.source_code_.add_line(fmt::format("memcmp_ret = memcmp({}, {}, {});", src1, src2, n)); 57 | mb.ir_builder_.SetCurrentDebugLocation(llvm::DebugLoc::get(line_no, 1, mb.dbg_scope_)); 58 | return value{mb.ir_builder_.CreateCall(fn, {src1.eval(), src2.eval(), n.eval()}), "memcmp_ret"}; 59 | } 60 | 61 | namespace detail { 62 | 63 | template class bswap_impl { 64 | Value value_; 65 | 66 | public: 67 | using value_type = typename Value::value_type; 68 | static_assert(std::is_integral_v); 69 | 70 | explicit bswap_impl(Value v) : value_(v) {} 71 | 72 | llvm::Value* eval() { 73 | return codegen::detail::current_builder->ir_builder_.CreateUnaryIntrinsic(llvm::Intrinsic::bswap, value_.eval()); 74 | } 75 | 76 | friend std::ostream& operator<<(std::ostream& os, bswap_impl bi) { return os << "bswap(" << bi.value_ << ")"; } 77 | }; 78 | 79 | } // namespace detail 80 | 81 | template auto bswap(Value v) { 82 | return detail::bswap_impl(v); 83 | } 84 | 85 | } // namespace codegen::builtin 86 | -------------------------------------------------------------------------------- /tests/module_builder.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2019 Paweł Dziepak 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | #include 24 | 25 | #include "codegen/compiler.hpp" 26 | #include "codegen/literals.hpp" 27 | #include "codegen/module.hpp" 28 | #include "codegen/module_builder.hpp" 29 | #include "codegen/statements.hpp" 30 | 31 | using namespace codegen::literals; 32 | 33 | TEST(module_builder, empty) { 34 | auto comp = codegen::compiler{}; 35 | auto builder = codegen::module_builder(comp, "empty"); 36 | auto module = std::move(builder).build(); 37 | (void)module; 38 | } 39 | 40 | TEST(module_builder, return_void) { 41 | auto comp = codegen::compiler{}; 42 | auto builder = codegen::module_builder(comp, "return_void"); 43 | auto fn = builder.create_function("return_void_fn", [] { codegen::return_(); }); 44 | auto module = std::move(builder).build(); 45 | auto fn_ptr = module.get_address(fn); 46 | fn_ptr(); 47 | } 48 | 49 | TEST(module_builder, return_i32) { 50 | auto comp = codegen::compiler{}; 51 | auto builder = codegen::module_builder(comp, "return_i32"); 52 | 53 | auto return_constant = builder.create_function("return_constant", [] { codegen::return_(4_i32); }); 54 | 55 | auto return_argument = builder.create_function( 56 | "return_argument", [](codegen::value arg) { codegen::return_(arg); }); 57 | 58 | auto module = std::move(builder).build(); 59 | 60 | auto return_constant_ptr = module.get_address(return_constant); 61 | EXPECT_EQ(return_constant_ptr(), 4); 62 | 63 | auto return_argument_ptr = module.get_address(return_argument); 64 | EXPECT_EQ(return_argument_ptr(1), 1); 65 | EXPECT_EQ(return_argument_ptr(8), 8); 66 | EXPECT_EQ(return_argument_ptr(-7), -7); 67 | } 68 | 69 | TEST(module_builder, external_functions) { 70 | auto comp = codegen::compiler{}; 71 | auto builder = codegen::module_builder(comp, "external_functions"); 72 | 73 | bool called = false; 74 | auto callee = builder.declare_external_function("set_true", [](bool* flag) { *flag = true; }); 75 | 76 | auto caller = builder.create_function("caller", [&](codegen::value f) { 77 | codegen::call(callee, f); 78 | codegen::return_(); 79 | }); 80 | 81 | auto module = std::move(builder).build(); 82 | auto caller_ptr = module.get_address(caller); 83 | caller_ptr(&called); 84 | EXPECT_TRUE(called); 85 | 86 | called = false; 87 | auto callee_ptr = module.get_address(callee); 88 | callee_ptr(&called); 89 | EXPECT_TRUE(called); 90 | } 91 | 92 | TEST(module_builder, bit_cast) { 93 | auto comp = codegen::compiler{}; 94 | auto builder = codegen::module_builder(comp, "bit_cast"); 95 | 96 | auto intptr_to_voidptr = builder.create_function( 97 | "intptr_to_voidptr", [&](codegen::value ptr) { codegen::return_(codegen::bit_cast(ptr)); }); 98 | 99 | auto module = std::move(builder).build(); 100 | auto intptr_to_voidptr_ptr = module.get_address(intptr_to_voidptr); 101 | int32_t value; 102 | EXPECT_EQ(reinterpret_cast(intptr_to_voidptr_ptr(&value)), reinterpret_cast(&value)); 103 | } 104 | 105 | TEST(module_builder, cast) { 106 | auto comp = codegen::compiler{}; 107 | auto builder = codegen::module_builder(comp, "cast"); 108 | 109 | auto f32_to_i16 = builder.create_function( 110 | "f32_to_i16", [&](codegen::value x) { codegen::return_(codegen::cast(x)); }); 111 | 112 | auto i32_to_f64 = builder.create_function( 113 | "i32_to_f64", [&](codegen::value x) { codegen::return_(codegen::cast(x)); }); 114 | 115 | auto i16_to_i64 = builder.create_function( 116 | "i16_to_i64", [&](codegen::value x) { codegen::return_(codegen::cast(x)); }); 117 | 118 | auto u16_to_u64 = builder.create_function( 119 | "u16_to_u64", [&](codegen::value x) { codegen::return_(codegen::cast(x)); }); 120 | 121 | auto module = std::move(builder).build(); 122 | 123 | auto f32_to_i16_ptr = module.get_address(f32_to_i16); 124 | EXPECT_EQ(f32_to_i16_ptr(3.5f), 3); 125 | 126 | auto i32_to_f64_ptr = module.get_address(i32_to_f64); 127 | EXPECT_EQ(i32_to_f64_ptr(4), 4.); 128 | 129 | auto i16_to_i64_ptr = module.get_address(i16_to_i64); 130 | EXPECT_EQ(i16_to_i64_ptr(-1), -1); 131 | 132 | auto u16_to_u64_ptr = module.get_address(u16_to_u64); 133 | EXPECT_EQ(u16_to_u64_ptr(-1), 0xffff); 134 | } 135 | -------------------------------------------------------------------------------- /src/compiler.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2019 Paweł Dziepak 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | #include "codegen/compiler.hpp" 24 | 25 | #include "os.hpp" 26 | 27 | #include 28 | 29 | #include 30 | #include 31 | 32 | #include 33 | 34 | #include 35 | #include 36 | 37 | #include 38 | 39 | #include 40 | #include 41 | 42 | #include "codegen/module.hpp" 43 | 44 | namespace codegen { 45 | 46 | compiler::compiler(llvm::orc::JITTargetMachineBuilder tmb) 47 | : data_layout_(unwrap(tmb.getDefaultDataLayoutForTarget())), target_machine_(unwrap(tmb.createTargetMachine())), 48 | mangle_(session_, data_layout_), object_layer_( 49 | session_, [] { return std::make_unique(); }, 50 | [this](llvm::orc::VModuleKey vk, llvm::object::ObjectFile const& object, 51 | llvm::RuntimeDyld::LoadedObjectInfo const& info) { 52 | if (gdb_listener_) { gdb_listener_->notifyObjectLoaded(vk, object, info); } 53 | loaded_modules_.emplace_back(vk); 54 | }), 55 | compile_layer_(session_, object_layer_, llvm::orc::ConcurrentIRCompiler(std::move(tmb))), 56 | optimize_layer_(session_, compile_layer_, 57 | [this](llvm::orc::ThreadSafeModule tsm, llvm::orc::MaterializationResponsibility const& mr) { 58 | return optimize_module(std::move(tsm), mr); 59 | }), 60 | gdb_listener_(llvm::JITEventListener::createGDBRegistrationListener()), source_directory_([&] { 61 | auto eng = std::default_random_engine{std::random_device{}()}; 62 | auto dist = std::uniform_int_distribution{}; 63 | return std::filesystem::temp_directory_path() / (get_process_name() + "-" + std::to_string(dist(eng))); 64 | }()), 65 | dynlib_generator_(unwrap(llvm::orc::DynamicLibrarySearchGenerator::GetForCurrentProcess(data_layout_))) { 66 | session_.getMainJITDylib().setGenerator([this](llvm::orc::JITDylib& jd, 67 | llvm::orc::SymbolNameSet const& Names) -> llvm::orc::SymbolNameSet { 68 | auto added = llvm::orc::SymbolNameSet{}; 69 | auto remaining = llvm::orc::SymbolNameSet{}; 70 | auto new_symbols = llvm::orc::SymbolMap{}; 71 | 72 | for (auto& name : Names) { 73 | auto it = external_symbols_.find(std::string(*name)); 74 | if (it == external_symbols_.end()) { 75 | remaining.insert(name); 76 | continue; 77 | } 78 | added.insert(name); 79 | new_symbols[name] = llvm::JITEvaluatedSymbol(llvm::JITTargetAddress{it->second}, llvm::JITSymbolFlags::Exported); 80 | } 81 | throw_on_error(jd.define(llvm::orc::absoluteSymbols(std::move(new_symbols)))); 82 | if (!remaining.empty()) { 83 | auto dynlib_added = dynlib_generator_(jd, remaining); 84 | added.insert(dynlib_added.begin(), dynlib_added.end()); 85 | } 86 | return added; 87 | }); 88 | 89 | std::filesystem::create_directories(source_directory_); 90 | } // namespace codegen 91 | 92 | compiler::compiler() 93 | : compiler([] { 94 | llvm::InitializeNativeTarget(); 95 | llvm::InitializeNativeTargetAsmPrinter(); 96 | 97 | auto tmb = unwrap(llvm::orc::JITTargetMachineBuilder::detectHost()); 98 | tmb.setCodeGenOptLevel(llvm::CodeGenOpt::Aggressive); 99 | tmb.setCPU(llvm::sys::getHostCPUName()); 100 | return tmb; 101 | }()) { 102 | } 103 | 104 | compiler::~compiler() { 105 | for (auto vk : loaded_modules_) { gdb_listener_->notifyFreeingObject(vk); } 106 | std::filesystem::remove_all(source_directory_); 107 | } 108 | 109 | llvm::Expected compiler::optimize_module(llvm::orc::ThreadSafeModule tsm, 110 | llvm::orc::MaterializationResponsibility const&) { 111 | auto module = tsm.getModule(); 112 | 113 | auto target_triple = target_machine_->getTargetTriple(); 114 | 115 | auto library_info = std::make_unique(target_triple); 116 | 117 | auto function_passes = llvm::legacy::FunctionPassManager(module); 118 | auto module_passes = llvm::legacy::PassManager(); 119 | 120 | auto builder = llvm::PassManagerBuilder{}; 121 | builder.OptLevel = 3; 122 | builder.SizeLevel = 0; 123 | builder.Inliner = llvm::createFunctionInliningPass(); 124 | builder.MergeFunctions = true; 125 | builder.LoopVectorize = true; 126 | builder.SLPVectorize = true; 127 | builder.DisableUnrollLoops = false; 128 | builder.RerollLoops = true; 129 | builder.LibraryInfo = new llvm::TargetLibraryInfoImpl(target_triple); 130 | 131 | function_passes.add(llvm::createTargetTransformInfoWrapperPass(target_machine_->getTargetIRAnalysis())); 132 | module_passes.add(llvm::createTargetTransformInfoWrapperPass(target_machine_->getTargetIRAnalysis())); 133 | 134 | target_machine_->adjustPassManager(builder); 135 | 136 | builder.populateFunctionPassManager(function_passes); 137 | builder.populateModulePassManager(module_passes); 138 | 139 | function_passes.doInitialization(); 140 | for (auto& func : *module) { function_passes.run(func); } 141 | function_passes.doFinalization(); 142 | 143 | module_passes.run(*module); 144 | 145 | return tsm; 146 | } 147 | 148 | void compiler::add_symbol(std::string const& name, void* address) { 149 | external_symbols_[*mangle_(name)] = reinterpret_cast(address); 150 | } 151 | 152 | module::module(llvm::orc::ExecutionSession& session, llvm::DataLayout const& dl) 153 | : session_(&session), mangle_(session, dl) { 154 | } 155 | 156 | void* module::get_address(std::string const& name) { 157 | auto address = unwrap(session_->lookup({&session_->getMainJITDylib()}, mangle_(name))).getAddress(); 158 | return reinterpret_cast(address); 159 | } 160 | 161 | } // namespace codegen 162 | -------------------------------------------------------------------------------- /include/codegen/relational_ops.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2019 Paweł Dziepak 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | #pragma once 24 | 25 | #include "codegen/module_builder.hpp" 26 | 27 | namespace codegen { 28 | 29 | namespace detail { 30 | 31 | enum class relational_operation_type { 32 | eq, 33 | ne, 34 | ge, 35 | gt, 36 | le, 37 | lt, 38 | }; 39 | 40 | template class relational_operation { 41 | LHS lhs_; 42 | RHS rhs_; 43 | 44 | using operand_type = typename LHS::value_type; 45 | static_assert(std::is_same_v); 46 | 47 | public: 48 | using value_type = bool; 49 | 50 | relational_operation(LHS lhs, RHS rhs) : lhs_(std::move(lhs)), rhs_(std::move(rhs)) {} 51 | 52 | llvm::Value* eval() const { 53 | if constexpr (std::is_integral_v) { 54 | switch (Op) { 55 | case relational_operation_type::eq: return current_builder->ir_builder_.CreateICmpEQ(lhs_.eval(), rhs_.eval()); 56 | case relational_operation_type::ne: return current_builder->ir_builder_.CreateICmpNE(lhs_.eval(), rhs_.eval()); 57 | case relational_operation_type::ge: 58 | if constexpr (std::is_signed_v) { 59 | return current_builder->ir_builder_.CreateICmpSGE(lhs_.eval(), rhs_.eval()); 60 | } else { 61 | return current_builder->ir_builder_.CreateICmpUGE(lhs_.eval(), rhs_.eval()); 62 | } 63 | case relational_operation_type::gt: 64 | if constexpr (std::is_signed_v) { 65 | return current_builder->ir_builder_.CreateICmpSGT(lhs_.eval(), rhs_.eval()); 66 | } else { 67 | return current_builder->ir_builder_.CreateICmpUGT(lhs_.eval(), rhs_.eval()); 68 | } 69 | case relational_operation_type::le: 70 | if constexpr (std::is_signed_v) { 71 | return current_builder->ir_builder_.CreateICmpSLE(lhs_.eval(), rhs_.eval()); 72 | } else { 73 | return current_builder->ir_builder_.CreateICmpULE(lhs_.eval(), rhs_.eval()); 74 | } 75 | case relational_operation_type::lt: 76 | if constexpr (std::is_signed_v) { 77 | return current_builder->ir_builder_.CreateICmpSLT(lhs_.eval(), rhs_.eval()); 78 | } else { 79 | return current_builder->ir_builder_.CreateICmpULT(lhs_.eval(), rhs_.eval()); 80 | } 81 | } 82 | } else { 83 | switch (Op) { 84 | case relational_operation_type::eq: return current_builder->ir_builder_.CreateFCmpOEQ(lhs_.eval(), rhs_.eval()); 85 | case relational_operation_type::ne: return current_builder->ir_builder_.CreateFCmpONE(lhs_.eval(), rhs_.eval()); 86 | case relational_operation_type::ge: return current_builder->ir_builder_.CreateFCmpOGE(lhs_.eval(), rhs_.eval()); 87 | case relational_operation_type::gt: return current_builder->ir_builder_.CreateFCmpOGT(lhs_.eval(), rhs_.eval()); 88 | case relational_operation_type::le: return current_builder->ir_builder_.CreateFCmpOLE(lhs_.eval(), rhs_.eval()); 89 | case relational_operation_type::lt: return current_builder->ir_builder_.CreateFCmpOLT(lhs_.eval(), rhs_.eval()); 90 | } 91 | } 92 | } 93 | 94 | friend std::ostream& operator<<(std::ostream& os, relational_operation const& ro) { 95 | auto symbol = [] { 96 | switch (Op) { 97 | case relational_operation_type::eq: return "=="; 98 | case relational_operation_type::ne: return "!="; 99 | case relational_operation_type::ge: return ">="; 100 | case relational_operation_type::gt: return ">"; 101 | case relational_operation_type::le: return "<="; 102 | case relational_operation_type::lt: return "<"; 103 | } 104 | }(); 105 | return os << '(' << ro.lhs_ << ' ' << symbol << ' ' << ro.rhs_ << ')'; 106 | } 107 | }; 108 | 109 | } // namespace detail 110 | 111 | template && 113 | std::is_same_v>> 114 | auto operator==(LHS lhs, RHS rhs) { 115 | return detail::relational_operation(std::move(lhs), std::move(rhs)); 116 | } 117 | 118 | template && 120 | std::is_same_v>> 121 | auto operator!=(LHS lhs, RHS rhs) { 122 | return detail::relational_operation(std::move(lhs), std::move(rhs)); 123 | } 124 | 125 | template && 127 | std::is_same_v>> 128 | auto operator>=(LHS lhs, RHS rhs) { 129 | return detail::relational_operation(std::move(lhs), std::move(rhs)); 130 | } 131 | 132 | template && 134 | std::is_same_v>> 135 | auto operator>(LHS lhs, RHS rhs) { 136 | return detail::relational_operation(std::move(lhs), std::move(rhs)); 137 | } 138 | 139 | template && 141 | std::is_same_v>> 142 | auto operator<=(LHS lhs, RHS rhs) { 143 | return detail::relational_operation(std::move(lhs), std::move(rhs)); 144 | } 145 | 146 | template && 148 | std::is_same_v>> 149 | auto operator<(LHS lhs, RHS rhs) { 150 | return detail::relational_operation(std::move(lhs), std::move(rhs)); 151 | } 152 | 153 | } // namespace codegen 154 | -------------------------------------------------------------------------------- /tests/relational_ops.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2019 Paweł Dziepak 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | #include "codegen/relational_ops.hpp" 24 | 25 | #include 26 | 27 | #include "codegen/compiler.hpp" 28 | #include "codegen/module.hpp" 29 | #include "codegen/module_builder.hpp" 30 | 31 | TEST(relational_ops, signed_integer) { 32 | auto comp = codegen::compiler{}; 33 | auto builder = codegen::module_builder(comp, "signed_integer"); 34 | 35 | auto eq2 = builder.create_function( 36 | "eq2", [](codegen::value x, codegen::value y) { codegen::return_(x == y); }); 37 | 38 | auto ne2 = builder.create_function( 39 | "ne2", [](codegen::value x, codegen::value y) { codegen::return_(x != y); }); 40 | 41 | auto ge2 = builder.create_function( 42 | "ge2", [](codegen::value x, codegen::value y) { codegen::return_(x >= y); }); 43 | 44 | auto gt2 = builder.create_function( 45 | "gt2", [](codegen::value x, codegen::value y) { codegen::return_(x > y); }); 46 | 47 | auto le2 = builder.create_function( 48 | "le2", [](codegen::value x, codegen::value y) { codegen::return_(x <= y); }); 49 | 50 | auto lt2 = builder.create_function( 51 | "lt2", [](codegen::value x, codegen::value y) { codegen::return_(x < y); }); 52 | 53 | auto module = std::move(builder).build(); 54 | 55 | auto eq2_ptr = module.get_address(eq2); 56 | EXPECT_EQ(eq2_ptr(2, 2), true); 57 | EXPECT_EQ(eq2_ptr(1, 3), false); 58 | 59 | auto ne2_ptr = module.get_address(ne2); 60 | EXPECT_EQ(ne2_ptr(2, 2), false); 61 | EXPECT_EQ(ne2_ptr(1, 3), true); 62 | 63 | auto ge2_ptr = module.get_address(ge2); 64 | EXPECT_EQ(ge2_ptr(2, 2), true); 65 | EXPECT_EQ(ge2_ptr(1, 3), false); 66 | EXPECT_EQ(ge2_ptr(5, 4), true); 67 | EXPECT_EQ(ge2_ptr(-1, 1), false); 68 | EXPECT_EQ(ge2_ptr(-1, -3), true); 69 | EXPECT_EQ(ge2_ptr(-5, -4), false); 70 | 71 | auto gt2_ptr = module.get_address(gt2); 72 | EXPECT_EQ(gt2_ptr(2, 2), false); 73 | EXPECT_EQ(gt2_ptr(1, 3), false); 74 | EXPECT_EQ(gt2_ptr(5, 4), true); 75 | EXPECT_EQ(gt2_ptr(-1, 1), false); 76 | EXPECT_EQ(gt2_ptr(-1, -3), true); 77 | EXPECT_EQ(gt2_ptr(-5, -4), false); 78 | 79 | auto le2_ptr = module.get_address(le2); 80 | EXPECT_EQ(le2_ptr(2, 2), true); 81 | EXPECT_EQ(le2_ptr(1, 3), true); 82 | EXPECT_EQ(le2_ptr(5, 4), false); 83 | EXPECT_EQ(le2_ptr(-1, 1), true); 84 | EXPECT_EQ(le2_ptr(-1, -3), false); 85 | EXPECT_EQ(le2_ptr(-5, -4), true); 86 | 87 | auto lt2_ptr = module.get_address(lt2); 88 | EXPECT_EQ(lt2_ptr(2, 2), false); 89 | EXPECT_EQ(lt2_ptr(1, 3), true); 90 | EXPECT_EQ(lt2_ptr(5, 4), false); 91 | EXPECT_EQ(lt2_ptr(-1, 1), true); 92 | EXPECT_EQ(lt2_ptr(-1, -3), false); 93 | EXPECT_EQ(lt2_ptr(-5, -4), true); 94 | } 95 | 96 | TEST(relational_ops, unsigned_integer) { 97 | auto comp = codegen::compiler{}; 98 | auto builder = codegen::module_builder(comp, "unsigned_integer"); 99 | 100 | auto eq2 = builder.create_function( 101 | "eq2", [](codegen::value x, codegen::value y) { codegen::return_(x == y); }); 102 | 103 | auto ne2 = builder.create_function( 104 | "ne2", [](codegen::value x, codegen::value y) { codegen::return_(x != y); }); 105 | 106 | auto ge2 = builder.create_function( 107 | "ge2", [](codegen::value x, codegen::value y) { codegen::return_(x >= y); }); 108 | 109 | auto gt2 = builder.create_function( 110 | "gt2", [](codegen::value x, codegen::value y) { codegen::return_(x > y); }); 111 | 112 | auto le2 = builder.create_function( 113 | "le2", [](codegen::value x, codegen::value y) { codegen::return_(x <= y); }); 114 | 115 | auto lt2 = builder.create_function( 116 | "lt2", [](codegen::value x, codegen::value y) { codegen::return_(x < y); }); 117 | 118 | auto module = std::move(builder).build(); 119 | 120 | auto eq2_ptr = module.get_address(eq2); 121 | EXPECT_EQ(eq2_ptr(2, 2), true); 122 | EXPECT_EQ(eq2_ptr(1, 3), false); 123 | 124 | auto ne2_ptr = module.get_address(ne2); 125 | EXPECT_EQ(ne2_ptr(2, 2), false); 126 | EXPECT_EQ(ne2_ptr(1, 3), true); 127 | 128 | auto ge2_ptr = module.get_address(ge2); 129 | EXPECT_EQ(ge2_ptr(2, 2), true); 130 | EXPECT_EQ(ge2_ptr(1, 3), false); 131 | EXPECT_EQ(ge2_ptr(5, 4), true); 132 | EXPECT_EQ(ge2_ptr(-1, 1), true); 133 | EXPECT_EQ(ge2_ptr(-1, -3), true); 134 | EXPECT_EQ(ge2_ptr(-5, -4), false); 135 | 136 | auto gt2_ptr = module.get_address(gt2); 137 | EXPECT_EQ(gt2_ptr(2, 2), false); 138 | EXPECT_EQ(gt2_ptr(1, 3), false); 139 | EXPECT_EQ(gt2_ptr(5, 4), true); 140 | EXPECT_EQ(gt2_ptr(-1, 1), true); 141 | EXPECT_EQ(gt2_ptr(-1, -3), true); 142 | EXPECT_EQ(gt2_ptr(-5, -4), false); 143 | 144 | auto le2_ptr = module.get_address(le2); 145 | EXPECT_EQ(le2_ptr(2, 2), true); 146 | EXPECT_EQ(le2_ptr(1, 3), true); 147 | EXPECT_EQ(le2_ptr(5, 4), false); 148 | EXPECT_EQ(le2_ptr(-1, 1), false); 149 | EXPECT_EQ(le2_ptr(-1, -3), false); 150 | EXPECT_EQ(le2_ptr(-5, -4), true); 151 | 152 | auto lt2_ptr = module.get_address(lt2); 153 | EXPECT_EQ(lt2_ptr(2, 2), false); 154 | EXPECT_EQ(lt2_ptr(1, 3), true); 155 | EXPECT_EQ(lt2_ptr(5, 4), false); 156 | EXPECT_EQ(lt2_ptr(-1, 1), false); 157 | EXPECT_EQ(lt2_ptr(-1, -3), false); 158 | EXPECT_EQ(lt2_ptr(-5, -4), true); 159 | } 160 | 161 | 162 | TEST(relational_ops, floating_point) { 163 | auto comp = codegen::compiler{}; 164 | auto builder = codegen::module_builder(comp, "float"); 165 | 166 | auto eq2 = builder.create_function( 167 | "eq2", [](codegen::value x, codegen::value y) { codegen::return_(x == y); }); 168 | 169 | auto ne2 = builder.create_function( 170 | "ne2", [](codegen::value x, codegen::value y) { codegen::return_(x != y); }); 171 | 172 | auto ge2 = builder.create_function( 173 | "ge2", [](codegen::value x, codegen::value y) { codegen::return_(x >= y); }); 174 | 175 | auto gt2 = builder.create_function( 176 | "gt2", [](codegen::value x, codegen::value y) { codegen::return_(x > y); }); 177 | 178 | auto le2 = builder.create_function( 179 | "le2", [](codegen::value x, codegen::value y) { codegen::return_(x <= y); }); 180 | 181 | auto lt2 = builder.create_function( 182 | "lt2", [](codegen::value x, codegen::value y) { codegen::return_(x < y); }); 183 | 184 | auto module = std::move(builder).build(); 185 | 186 | auto eq2_ptr = module.get_address(eq2); 187 | EXPECT_EQ(eq2_ptr(2, 2), true); 188 | EXPECT_EQ(eq2_ptr(1, 3), false); 189 | 190 | auto ne2_ptr = module.get_address(ne2); 191 | EXPECT_EQ(ne2_ptr(2, 2), false); 192 | EXPECT_EQ(ne2_ptr(1, 3), true); 193 | 194 | auto ge2_ptr = module.get_address(ge2); 195 | EXPECT_EQ(ge2_ptr(2, 2), true); 196 | EXPECT_EQ(ge2_ptr(1, 3), false); 197 | EXPECT_EQ(ge2_ptr(5, 4), true); 198 | EXPECT_EQ(ge2_ptr(-1, 1), false); 199 | EXPECT_EQ(ge2_ptr(-1, -3), true); 200 | EXPECT_EQ(ge2_ptr(-5, -4), false); 201 | 202 | auto gt2_ptr = module.get_address(gt2); 203 | EXPECT_EQ(gt2_ptr(2, 2), false); 204 | EXPECT_EQ(gt2_ptr(1, 3), false); 205 | EXPECT_EQ(gt2_ptr(5, 4), true); 206 | EXPECT_EQ(gt2_ptr(-1, 1), false); 207 | EXPECT_EQ(gt2_ptr(-1, -3), true); 208 | EXPECT_EQ(gt2_ptr(-5, -4), false); 209 | 210 | auto le2_ptr = module.get_address(le2); 211 | EXPECT_EQ(le2_ptr(2, 2), true); 212 | EXPECT_EQ(le2_ptr(1, 3), true); 213 | EXPECT_EQ(le2_ptr(5, 4), false); 214 | EXPECT_EQ(le2_ptr(-1, 1), true); 215 | EXPECT_EQ(le2_ptr(-1, -3), false); 216 | EXPECT_EQ(le2_ptr(-5, -4), true); 217 | 218 | auto lt2_ptr = module.get_address(lt2); 219 | EXPECT_EQ(lt2_ptr(2, 2), false); 220 | EXPECT_EQ(lt2_ptr(1, 3), true); 221 | EXPECT_EQ(lt2_ptr(5, 4), false); 222 | EXPECT_EQ(lt2_ptr(-1, 1), true); 223 | EXPECT_EQ(lt2_ptr(-1, -3), false); 224 | EXPECT_EQ(lt2_ptr(-5, -4), true); 225 | } 226 | -------------------------------------------------------------------------------- /tests/statements.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2019 Paweł Dziepak 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | #include "codegen/statements.hpp" 24 | 25 | #include 26 | 27 | #include "codegen/arithmetic_ops.hpp" 28 | #include "codegen/compiler.hpp" 29 | #include "codegen/module.hpp" 30 | #include "codegen/module_builder.hpp" 31 | #include "codegen/relational_ops.hpp" 32 | #include "codegen/variable.hpp" 33 | 34 | TEST(statements, if_condition) { 35 | auto comp = codegen::compiler{}; 36 | auto builder = codegen::module_builder(comp, "if_cond"); 37 | 38 | auto if_cond = builder.create_function("if_cond_fn", [](codegen::value x) { 39 | auto y = codegen::variable{"ret"}; 40 | codegen::if_( 41 | x > codegen::constant(4), [&] { y.set(x + x); }, [&] { y.set(x * x); }); 42 | codegen::return_(y.get() + codegen::constant(1)); 43 | }); 44 | 45 | auto module = std::move(builder).build(); 46 | 47 | auto if_cond_ptr = module.get_address(if_cond); 48 | EXPECT_EQ(if_cond_ptr(8), 17); 49 | EXPECT_EQ(if_cond_ptr(2), 5); 50 | } 51 | 52 | TEST(statements, if_condition_true_only) { 53 | auto comp = codegen::compiler{}; 54 | auto builder = codegen::module_builder(comp, "if_cond_true_only"); 55 | 56 | auto if_cond = builder.create_function("if_cond_fn", [](codegen::value x) { 57 | auto y = codegen::variable{"ret"}; 58 | y.set(x); 59 | codegen::if_(x > codegen::constant(4), [&] { y.set(x + x); }); 60 | codegen::return_(y.get() + codegen::constant(1)); 61 | }); 62 | 63 | auto module = std::move(builder).build(); 64 | 65 | auto if_cond_ptr = module.get_address(if_cond); 66 | EXPECT_EQ(if_cond_ptr(8), 17); 67 | EXPECT_EQ(if_cond_ptr(2), 3); 68 | } 69 | 70 | TEST(statements, if_condition_nested) { 71 | auto comp = codegen::compiler{}; 72 | auto builder = codegen::module_builder(comp, "if_cond_nested"); 73 | 74 | auto if_cond_nested = builder.create_function( 75 | "if_cond_nested_fn", [](codegen::value x, codegen::value y) { 76 | auto z = codegen::variable{"ret"}; 77 | codegen::if_( 78 | x > codegen::constant(4), 79 | [&] { 80 | codegen::if_( 81 | y < x, [&] { z.set(x + y); }, [&] { z.set(x * y); }); 82 | }, 83 | [&] { 84 | codegen::if_( 85 | y > codegen::constant(0), [&] { z.set(x * x); }, [&] { z.set(x - y); }); 86 | }); 87 | codegen::return_(z.get() + codegen::constant(1)); 88 | }); 89 | 90 | auto module = std::move(builder).build(); 91 | 92 | auto if_cond_nested_ptr = module.get_address(if_cond_nested); 93 | EXPECT_EQ(if_cond_nested_ptr(8, 2), 11); 94 | EXPECT_EQ(if_cond_nested_ptr(8, 12), 97); 95 | EXPECT_EQ(if_cond_nested_ptr(2, 7), 5); 96 | EXPECT_EQ(if_cond_nested_ptr(2, -7), 10); 97 | } 98 | 99 | TEST(statements, function_call) { 100 | auto comp = codegen::compiler{}; 101 | auto builder = codegen::module_builder(comp, "function_call"); 102 | 103 | auto add2 = builder.create_function( 104 | "add2", [](codegen::value x, codegen::value y) { codegen::return_(x + y); }); 105 | 106 | auto caller = builder.create_function( 107 | "caller", [&](codegen::value x, codegen::value y) { 108 | codegen::return_(codegen::call(add2, x * x, y * y)); 109 | }); 110 | 111 | auto module = std::move(builder).build(); 112 | 113 | auto caller_ptr = module.get_address(caller); 114 | EXPECT_EQ(caller_ptr(8, 2), 68); 115 | } 116 | 117 | TEST(statements, load) { 118 | auto comp = codegen::compiler{}; 119 | auto builder = codegen::module_builder(comp, "load"); 120 | 121 | int32_t value = 8; 122 | int32_t* pointer = &value; 123 | 124 | auto load = builder.create_function( 125 | "load_fn", [&](codegen::value ptr) { codegen::return_(codegen::load(codegen::load(ptr))); }); 126 | 127 | auto module = std::move(builder).build(); 128 | 129 | auto load_ptr = module.get_address(load); 130 | EXPECT_EQ(load_ptr(&pointer), 8); 131 | value = 6; 132 | EXPECT_EQ(load_ptr(&pointer), 6); 133 | } 134 | 135 | TEST(statements, store) { 136 | auto comp = codegen::compiler{}; 137 | auto builder = codegen::module_builder(comp, "store"); 138 | 139 | int32_t value = 8; 140 | int32_t* pointer = &value; 141 | 142 | auto store = builder.create_function( 143 | "store_fn", [&](codegen::value v, codegen::value ptr) { 144 | codegen::store(v + codegen::constant(4), (codegen::load(ptr))); 145 | codegen::return_(); 146 | }); 147 | 148 | auto module = std::move(builder).build(); 149 | 150 | auto store_ptr = module.get_address(store); 151 | store_ptr(9, &pointer); 152 | EXPECT_EQ(value, 13); 153 | store_ptr(-8, &pointer); 154 | EXPECT_EQ(value, -4); 155 | } 156 | 157 | TEST(statements, while_loop) { 158 | auto comp = codegen::compiler{}; 159 | auto builder = codegen::module_builder(comp, "while_loop"); 160 | 161 | auto fact = builder.create_function("fact", [&](codegen::value x) { 162 | auto value = codegen::variable("value"); 163 | auto next = codegen::variable("next"); 164 | 165 | value.set(codegen::constant(1)); 166 | next.set(x); 167 | 168 | codegen::while_([&] { return next.get() != codegen::constant(1); }, 169 | [&] { 170 | value.set(value.get() * next.get()); 171 | next.set(next.get() - codegen::constant(1)); 172 | }); 173 | codegen::return_(value.get()); 174 | }); 175 | 176 | auto module = std::move(builder).build(); 177 | 178 | auto fact_ptr = module.get_address(fact); 179 | EXPECT_EQ(fact_ptr(4), 24); 180 | EXPECT_EQ(fact_ptr(6), 720); 181 | } 182 | 183 | TEST(statements, while_loop_continue) { 184 | auto comp = codegen::compiler{}; 185 | auto builder = codegen::module_builder(comp, "while_loop_continue"); 186 | 187 | auto while_loop_continue = 188 | builder.create_function("while_loop_continue_fn", [&](codegen::value x) { 189 | auto value = codegen::variable("value"); 190 | auto idx = codegen::variable("idx"); 191 | value.set(codegen::constant(0)); 192 | idx.set(x); 193 | codegen::while_([&] { return idx.get() != codegen::constant(0); }, 194 | [&] { 195 | idx.set(idx.get() - codegen::constant(1)); 196 | codegen::if_( 197 | idx.get() % codegen::constant(2) == codegen::constant(0), 198 | [&] { codegen::continue_(); }, [&] {}); 199 | value.set(value.get() + idx.get()); 200 | }); 201 | codegen::return_(value.get()); 202 | }); 203 | 204 | auto module = std::move(builder).build(); 205 | 206 | auto while_loop_continue_ptr = module.get_address(while_loop_continue); 207 | EXPECT_EQ(while_loop_continue_ptr(6), 9); 208 | } 209 | 210 | TEST(statements, while_loop_break) { 211 | auto comp = codegen::compiler{}; 212 | auto builder = codegen::module_builder(comp, "while_loop_break"); 213 | 214 | auto while_loop_break = 215 | builder.create_function("while_loop_break_fn", [&](codegen::value x) { 216 | auto value = codegen::variable("value"); 217 | auto idx = codegen::variable("idx"); 218 | value.set(codegen::constant(0)); 219 | idx.set(x); 220 | codegen::while_([&] { return idx.get() != codegen::constant(0); }, 221 | [&] { 222 | idx.set(idx.get() - codegen::constant(1)); 223 | codegen::if_( 224 | idx.get() == codegen::constant(2), [&] { codegen::break_(); }, [&] {}); 225 | value.set(value.get() + idx.get()); 226 | }); 227 | codegen::return_(value.get()); 228 | }); 229 | 230 | auto module = std::move(builder).build(); 231 | 232 | auto while_loop_break_ptr = module.get_address(while_loop_break); 233 | EXPECT_EQ(while_loop_break_ptr(6), 12); 234 | } 235 | -------------------------------------------------------------------------------- /tests/arithmetic_ops.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2019 Paweł Dziepak 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | #include "codegen/arithmetic_ops.hpp" 24 | 25 | #include 26 | 27 | #include "codegen/compiler.hpp" 28 | #include "codegen/module.hpp" 29 | #include "codegen/module_builder.hpp" 30 | #include "codegen/statements.hpp" 31 | 32 | TEST(arithmetic_ops, signed_integer_arithmetic) { 33 | auto comp = codegen::compiler{}; 34 | auto builder = codegen::module_builder(comp, "signed_integer_arithmetic"); 35 | 36 | auto add2 = builder.create_function( 37 | "add2", [](codegen::value x, codegen::value y) { codegen::return_(x + y); }); 38 | 39 | auto add4 = builder.create_function( 40 | "add4", [](codegen::value x, codegen::value y, codegen::value z, 41 | codegen::value w) { codegen::return_(x + y + z + w); }); 42 | 43 | auto sub_add4 = builder.create_function( 44 | "sub_add4", [](codegen::value x, codegen::value y, codegen::value z, 45 | codegen::value w) { codegen::return_(x - y + z - w); }); 46 | 47 | auto mul_div_mod2 = builder.create_function( 48 | "mul_div_mod2", 49 | [](codegen::value x, codegen::value y) { codegen::return_((x / y) * y + x % y); }); 50 | 51 | auto module = std::move(builder).build(); 52 | 53 | auto add2_ptr = module.get_address(add2); 54 | EXPECT_EQ(add2_ptr(1, 2), 3); 55 | 56 | auto add4_ptr = module.get_address(add4); 57 | EXPECT_EQ(add4_ptr(1, 2, 3, 4), 10); 58 | 59 | auto sub_add4_ptr = module.get_address(sub_add4); 60 | EXPECT_EQ(sub_add4_ptr(1, 2, 3, 4), -2); 61 | 62 | auto mul_div_mod2_ptr = module.get_address(mul_div_mod2); 63 | EXPECT_EQ(mul_div_mod2_ptr(7, 2), 7); 64 | EXPECT_EQ(mul_div_mod2_ptr(11, 3), 11); 65 | EXPECT_EQ(mul_div_mod2_ptr(4, -3), 4); 66 | EXPECT_EQ(mul_div_mod2_ptr(1, -7), 1); 67 | } 68 | 69 | TEST(arithmetic_ops, unsigned_integer_arithmetic) { 70 | auto comp = codegen::compiler{}; 71 | auto builder = codegen::module_builder(comp, "unsigned_integer_arithmetic"); 72 | 73 | auto add2 = builder.create_function( 74 | "add2", [](codegen::value x, codegen::value y) { codegen::return_(x + y); }); 75 | 76 | auto add4 = builder.create_function( 77 | "add4", [](codegen::value x, codegen::value y, codegen::value z, 78 | codegen::value w) { codegen::return_(x + y + z + w); }); 79 | 80 | auto sub_add4 = builder.create_function( 81 | "sub_add4", [](codegen::value x, codegen::value y, codegen::value z, 82 | codegen::value w) { codegen::return_(x - y + z - w); }); 83 | 84 | auto mul_div_mod2 = builder.create_function( 85 | "mul_div_mod2", 86 | [](codegen::value x, codegen::value y) { codegen::return_((x / y) * y + x % y); }); 87 | 88 | auto module = std::move(builder).build(); 89 | 90 | auto add2_ptr = module.get_address(add2); 91 | EXPECT_EQ(add2_ptr(1, 2), 3); 92 | 93 | auto add4_ptr = module.get_address(add4); 94 | EXPECT_EQ(add4_ptr(1, 2, 3, 4), 10); 95 | 96 | auto sub_add4_ptr = module.get_address(sub_add4); 97 | EXPECT_EQ(sub_add4_ptr(1, 2, 3, 4), uint32_t(-2)); 98 | 99 | auto mul_div_mod2_ptr = module.get_address(mul_div_mod2); 100 | EXPECT_EQ(mul_div_mod2_ptr(7, 2), 7); 101 | EXPECT_EQ(mul_div_mod2_ptr(11, 3), 11); 102 | EXPECT_EQ(mul_div_mod2_ptr(4, uint32_t(-3)), 4); 103 | EXPECT_EQ(mul_div_mod2_ptr(1, uint32_t(-7)), 1); 104 | } 105 | 106 | TEST(arithmetic_ops, float_arithmetic) { 107 | auto comp = codegen::compiler{}; 108 | auto builder = codegen::module_builder(comp, "float_arithmetic"); 109 | 110 | auto add2 = 111 | builder.create_function("add2", [](codegen::value x, codegen::value y) { 112 | codegen::return_(x + y + codegen::constant(0.5f)); 113 | }); 114 | 115 | auto add4 = builder.create_function( 116 | "add4", [](codegen::value x, codegen::value y, codegen::value z, codegen::value w) { 117 | codegen::return_(x + y + z + w); 118 | }); 119 | 120 | auto sub_add4 = builder.create_function( 121 | "sub_add4", [](codegen::value x, codegen::value y, codegen::value z, 122 | codegen::value w) { codegen::return_(x - y + z - w); }); 123 | 124 | auto mul_div_mod2 = builder.create_function( 125 | "mul_div_mod2", [](codegen::value x, codegen::value y) { codegen::return_((x / y) * y + x % y); }); 126 | 127 | auto module = std::move(builder).build(); 128 | 129 | auto add2_ptr = module.get_address(add2); 130 | EXPECT_EQ(add2_ptr(1, 2), 3.5f); 131 | 132 | auto add4_ptr = module.get_address(add4); 133 | EXPECT_EQ(add4_ptr(1, 2, 3, 4), 10); 134 | 135 | auto sub_add4_ptr = module.get_address(sub_add4); 136 | EXPECT_EQ(sub_add4_ptr(1, 2, 3, 4), -2); 137 | 138 | auto mul_div_mod2_ptr = module.get_address(mul_div_mod2); 139 | EXPECT_EQ(mul_div_mod2_ptr(7, 2), 8); 140 | EXPECT_EQ(mul_div_mod2_ptr(11, 3), 13); 141 | EXPECT_EQ(mul_div_mod2_ptr(4, -3), 5); 142 | EXPECT_EQ(mul_div_mod2_ptr(1, -7), 2); 143 | } 144 | 145 | TEST(arithmetic_ops, signed_integer_bitwise) { 146 | auto comp = codegen::compiler{}; 147 | auto builder = codegen::module_builder(comp, "signed_integer_bitwise"); 148 | 149 | auto and2 = builder.create_function( 150 | "and2", [](codegen::value x, codegen::value y) { codegen::return_(x & y); }); 151 | 152 | auto and4 = builder.create_function( 153 | "and4", [](codegen::value x, codegen::value y, codegen::value z, 154 | codegen::value w) { codegen::return_(x & y & z & w); }); 155 | 156 | auto and_or4 = builder.create_function( 157 | "and_or4", [](codegen::value x, codegen::value y, codegen::value z, 158 | codegen::value w) { codegen::return_((x & y) | (z & w)); }); 159 | 160 | auto and_or_xor4 = builder.create_function( 161 | "and_or_xor4", [](codegen::value x, codegen::value y, codegen::value z, 162 | codegen::value w) { codegen::return_((x | y) ^ (z & w)); }); 163 | 164 | auto module = std::move(builder).build(); 165 | 166 | auto and2_ptr = module.get_address(and2); 167 | EXPECT_EQ(and2_ptr(1, 2), 0); 168 | EXPECT_EQ(and2_ptr(1, 3), 1); 169 | 170 | auto and4_ptr = module.get_address(and4); 171 | EXPECT_EQ(and4_ptr(3, 3, 7, 2), 2); 172 | 173 | auto and_or4_ptr = module.get_address(and_or4); 174 | EXPECT_EQ(and_or4_ptr(0x10, 0x30, 3, 6), 0x12); 175 | 176 | auto and_or_xor4_ptr = module.get_address(and_or_xor4); 177 | EXPECT_EQ(and_or_xor4_ptr(3, 6, 11, 14), 13); 178 | } 179 | 180 | TEST(arithmetic_ops, pointer_arithmetic) { 181 | 182 | auto comp = codegen::compiler{}; 183 | auto builder = codegen::module_builder(comp, "pointer_arithmetic"); 184 | 185 | auto add = builder.create_function( 186 | "add", [](codegen::value x, codegen::value y) { codegen::return_(codegen::load(x + y)); }); 187 | 188 | auto add2 = builder.create_function( 189 | "add2", [](codegen::value x, codegen::value y, codegen::value z) { 190 | codegen::return_(codegen::load(x + y + z)); 191 | }); 192 | 193 | auto sub = builder.create_function( 194 | "sub", [](codegen::value x, codegen::value y) { codegen::return_(codegen::load(x - y)); }); 195 | 196 | int32_t values[] = { 197 | 0, 1, 2, 3, 4, 198 | }; 199 | 200 | auto module = std::move(builder).build(); 201 | 202 | auto add_ptr = module.get_address(add); 203 | EXPECT_EQ(add_ptr(&values[1], 2), 3); 204 | EXPECT_EQ(add_ptr(&values[1], -1), 0); 205 | 206 | auto add2_ptr = module.get_address(add2); 207 | EXPECT_EQ(add2_ptr(&values[1], 3, -2), 2); 208 | 209 | auto sub_ptr = module.get_address(sub); 210 | EXPECT_EQ(sub_ptr(&values[3], 2), 1); 211 | } 212 | -------------------------------------------------------------------------------- /tests/examples.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2019 Paweł Dziepak 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | #include 24 | 25 | #include 26 | 27 | #include "codegen/arithmetic_ops.hpp" 28 | #include "codegen/builtin.hpp" 29 | #include "codegen/compiler.hpp" 30 | #include "codegen/literals.hpp" 31 | #include "codegen/module.hpp" 32 | #include "codegen/module_builder.hpp" 33 | #include "codegen/relational_ops.hpp" 34 | #include "codegen/statements.hpp" 35 | #include "codegen/variable.hpp" 36 | 37 | namespace cg = codegen; 38 | using namespace cg::literals; 39 | 40 | template 41 | size_t less_cmp(cg::value a_ptr, cg::value b_ptr, size_t off) { 42 | auto a_val = cg::load(cg::bit_cast(a_ptr + cg::constant(off))); 43 | auto b_val = cg::load(cg::bit_cast(b_ptr + cg::constant(off))); 44 | cg::if_(a_val < b_val, [&] { cg::return_(cg::true_()); }); 45 | cg::if_(a_val > b_val, [&] { cg::return_(cg::false_()); }); 46 | return sizeof(T) + off; 47 | } 48 | 49 | TEST(examples, tuple_i32f32u16_less) { 50 | auto comp = codegen::compiler{}; 51 | auto builder = codegen::module_builder(comp, "tuple_i32f32u16_less"); 52 | auto less = builder.create_function( 53 | "less", [&](cg::value a_ptr, cg::value b_ptr) { 54 | size_t offset = 0; 55 | offset = less_cmp(a_ptr, b_ptr, offset); 56 | offset = less_cmp(a_ptr, b_ptr, offset); 57 | offset = less_cmp(a_ptr, b_ptr, offset); 58 | (void)offset; 59 | cg::return_(cg::false_()); 60 | }); 61 | auto module = std::move(builder).build(); 62 | auto less_ptr = module.get_address(less); 63 | 64 | auto make_tuple = [](int32_t a, float b, uint16_t c) { 65 | auto data = std::make_unique(sizeof(a) + sizeof(b) + sizeof(c)); 66 | auto dst = data.get(); 67 | dst = std::copy_n(reinterpret_cast(&a), sizeof(a), dst); 68 | dst = std::copy_n(reinterpret_cast(&b), sizeof(b), dst); 69 | dst = std::copy_n(reinterpret_cast(&c), sizeof(c), dst); 70 | (void)dst; 71 | return data; 72 | }; 73 | 74 | EXPECT_TRUE(less_ptr(make_tuple(0, 2.5f, 1).get(), make_tuple(1, 2.5f, 2).get())); 75 | EXPECT_TRUE(less_ptr(make_tuple(1, 2, 1).get(), make_tuple(1, 2.5f, 2).get())); 76 | EXPECT_TRUE(less_ptr(make_tuple(1, 2.5f, 1).get(), make_tuple(1, 2.5f, 2).get())); 77 | EXPECT_FALSE(less_ptr(make_tuple(1, 2.5f, 2).get(), make_tuple(1, 2.5f, 2).get())); 78 | EXPECT_FALSE(less_ptr(make_tuple(1, 2.5f, 2).get(), make_tuple(-1, 2.5f, 2).get())); 79 | EXPECT_FALSE(less_ptr(make_tuple(1, 2.5f, 2).get(), make_tuple(1, -2.5f, 2).get())); 80 | EXPECT_FALSE(less_ptr(make_tuple(1, 2.5f, 2).get(), make_tuple(1, 2.5f, 0).get())); 81 | } 82 | 83 | TEST(examples, tuple_i32str_less) { 84 | auto comp = codegen::compiler{}; 85 | auto builder = codegen::module_builder(comp, "tuple_i32str_less"); 86 | 87 | auto min = 88 | builder.create_function("min", [&](cg::value a, cg::value b) { 89 | cg::if_(a < b, [&] { cg::return_(a); }); 90 | cg::return_(b); 91 | }); 92 | 93 | auto less = builder.create_function( 94 | "less", [&](cg::value a_ptr, cg::value b_ptr) { 95 | size_t offset = 0; 96 | offset = less_cmp(a_ptr, b_ptr, offset); 97 | 98 | auto a_len = cg::load(cg::bit_cast(a_ptr + cg::constant(offset))); 99 | auto b_len = cg::load(cg::bit_cast(b_ptr + cg::constant(offset))); 100 | auto len = cg::call(min, a_len, b_len); 101 | auto ret = cg::builtin::memcmp(a_ptr + cg::constant(offset) + 4_u64, 102 | b_ptr + cg::constant(offset) + 4_u64, len); 103 | cg::if_(ret < 0_i32, [&] { cg::return_(cg::true_()); }); 104 | cg::if_(ret > 0_i32, [&] { cg::return_(cg::false_()); }); 105 | cg::return_(a_len < b_len); 106 | }); 107 | 108 | auto module = std::move(builder).build(); 109 | auto less_ptr = module.get_address(less); 110 | 111 | auto make_tuple = [](int32_t a, std::string_view b) { 112 | auto data = std::make_unique(sizeof(a) + sizeof(uint32_t) + b.size()); 113 | uint32_t b_len = b.size(); 114 | auto dst = data.get(); 115 | dst = std::copy_n(reinterpret_cast(&a), sizeof(a), dst); 116 | dst = std::copy_n(reinterpret_cast(&b_len), sizeof(b_len), dst); 117 | dst = std::copy_n(reinterpret_cast(b.data()), b.size(), dst); 118 | (void)dst; 119 | return data; 120 | }; 121 | 122 | EXPECT_TRUE(less_ptr(make_tuple(0, "bbb").get(), make_tuple(1, "bbb").get())); 123 | EXPECT_TRUE(less_ptr(make_tuple(1, "aaa").get(), make_tuple(1, "bbb").get())); 124 | EXPECT_TRUE(less_ptr(make_tuple(1, "aa").get(), make_tuple(1, "aaa").get())); 125 | EXPECT_TRUE(less_ptr(make_tuple(1, "aaa").get(), make_tuple(1, "z").get())); 126 | EXPECT_FALSE(less_ptr(make_tuple(1, "bbb").get(), make_tuple(1, "bbb").get())); 127 | EXPECT_FALSE(less_ptr(make_tuple(1, "bbb").get(), make_tuple(-1, "bbb").get())); 128 | EXPECT_FALSE(less_ptr(make_tuple(1, "bbb").get(), make_tuple(1, "abc").get())); 129 | EXPECT_FALSE(less_ptr(make_tuple(1, "bbb").get(), make_tuple(1, "bb").get())); 130 | EXPECT_FALSE(less_ptr(make_tuple(1, "z").get(), make_tuple(1, "bbb").get())); 131 | } 132 | 133 | TEST(examples, soa_compute) { 134 | auto comp = codegen::compiler{}; 135 | auto builder = codegen::module_builder(comp, "soa_compute"); 136 | auto compute = builder.create_function( 137 | "compute", [&](cg::value a, cg::value b_ptr, cg::value c_ptr, 138 | cg::value d_ptr, cg::value n) { 139 | auto idx = cg::variable("idx", 0_u64); 140 | cg::while_([&] { return idx.get() < n; }, 141 | [&] { 142 | auto i = idx.get(); 143 | cg::store(a * cg::load(b_ptr + i) + cg::load(c_ptr + i), d_ptr + i); 144 | idx.set(i + 1_u64); 145 | }); 146 | cg::return_(); 147 | }); 148 | 149 | auto module = std::move(builder).build(); 150 | auto compute_ptr = module.get_address(compute); 151 | compute_ptr(0, nullptr, nullptr, nullptr, 0); 152 | 153 | auto test = [&](int32_t a, std::vector const& b, std::vector const& c) { 154 | EXPECT_EQ(b.size(), c.size()); 155 | auto d = std::make_unique(b.size()); 156 | compute_ptr(a, b.data(), c.data(), d.get(), b.size()); 157 | for (auto i = 0u; i < b.size(); i++) { EXPECT_EQ(d[i], a * b[i] + c[i]); } 158 | }; 159 | 160 | test(2, {1, 2, 3, 4, 5, 6}, {11, 12, 13, 14, 15, 16}); 161 | test(5, {-8, 5, -4, 3, -10, 11}, {0, 8, 3, -9, 4, 2}); 162 | 163 | auto gen = std::default_random_engine{std::random_device{}()}; 164 | auto dist = std::uniform_int_distribution(-10000, 10000); 165 | 166 | auto b = std::vector(); 167 | auto c = std::vector(); 168 | std::generate_n(std::back_inserter(b), 1000000, [&] { return dist(gen); }); 169 | std::generate_n(std::back_inserter(c), 1000000, [&] { return dist(gen); }); 170 | test(dist(gen), std::move(b), std::move(c)); 171 | } 172 | 173 | TEST(examples, trivial_if) { 174 | auto comp = codegen::compiler{}; 175 | auto builder = codegen::module_builder(comp, "trivial_if"); 176 | auto silly_function = builder.create_function("silly_function", [](cg::value is_true) { 177 | cg::if_( 178 | is_true, [] { cg::return_(cg::true_()); }, [] { cg::return_(cg::false_()); }); 179 | }); 180 | auto module = std::move(builder).build(); 181 | auto fn = module.get_address(silly_function); 182 | EXPECT_TRUE(fn(true)); 183 | EXPECT_FALSE(fn(false)); 184 | } 185 | 186 | TEST(examples, trivial_while) { 187 | auto comp = codegen::compiler{}; 188 | auto builder = codegen::module_builder(comp, "trivial_while"); 189 | auto silly_function2 = builder.create_function("silly_function2", 190 | [](cg::value target) { 191 | auto var = cg::variable("var", cg::constant(0)); 192 | cg::while_([&] { return var.get() < target; }, 193 | [&] { 194 | var.set(var.get() + cg::constant(1)); 195 | }); 196 | cg::return_(var.get()); 197 | }); 198 | auto module = std::move(builder).build(); 199 | auto fn = module.get_address(silly_function2); 200 | EXPECT_EQ(fn(0), 0); 201 | EXPECT_EQ(fn(1), 1); 202 | EXPECT_EQ(fn(7), 7); 203 | EXPECT_EQ(fn(100), 100); 204 | EXPECT_EQ(fn(123), 123); 205 | } 206 | -------------------------------------------------------------------------------- /include/codegen/statements.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2019 Paweł Dziepak 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | #pragma once 24 | 25 | #include "codegen/module_builder.hpp" 26 | #include "codegen/utils.hpp" 27 | 28 | namespace codegen { 29 | 30 | template::value_type, bool>>> 32 | void if_(Condition&& cnd, TrueBlock&& tb, FalseBlock&& fb) { 33 | auto& mb = *detail::current_builder; 34 | 35 | auto line_no = mb.source_code_.add_line(fmt::format("if ({}) {{", cnd)); 36 | mb.ir_builder_.SetCurrentDebugLocation(llvm::DebugLoc::get(line_no, 1, mb.dbg_scope_)); 37 | 38 | auto true_block = llvm::BasicBlock::Create(*mb.context_, "true_block", mb.function_); 39 | auto false_block = llvm::BasicBlock::Create(*mb.context_, "false_block"); 40 | auto merge_block = llvm::BasicBlock::Create(*mb.context_, "merge_block"); 41 | 42 | mb.ir_builder_.CreateCondBr(cnd.eval(), true_block, false_block); 43 | 44 | mb.ir_builder_.SetInsertPoint(true_block); 45 | mb.source_code_.enter_scope(); 46 | 47 | auto scope = mb.dbg_builder_.createLexicalBlock(mb.dbg_scope_, mb.dbg_file_, mb.source_code_.current_line(), 1); 48 | auto parent_scope = std::exchange(mb.dbg_scope_, scope); 49 | 50 | assert(!mb.exited_block_); 51 | tb(); 52 | mb.source_code_.leave_scope(); 53 | 54 | line_no = mb.source_code_.add_line("} else {"); 55 | 56 | if (!mb.exited_block_) { 57 | mb.ir_builder_.SetCurrentDebugLocation(llvm::DebugLoc::get(line_no, 1, parent_scope)); 58 | mb.ir_builder_.CreateBr(merge_block); 59 | } 60 | mb.exited_block_ = false; 61 | 62 | mb.function_->getBasicBlockList().push_back(false_block); 63 | mb.ir_builder_.SetInsertPoint(false_block); 64 | mb.source_code_.enter_scope(); 65 | 66 | mb.dbg_scope_ = mb.dbg_builder_.createLexicalBlock(parent_scope, mb.dbg_file_, mb.source_code_.current_line(), 1); 67 | 68 | fb(); 69 | mb.source_code_.leave_scope(); 70 | 71 | mb.dbg_scope_ = parent_scope; 72 | 73 | line_no = mb.source_code_.add_line("}"); 74 | 75 | if (!mb.exited_block_) { 76 | mb.ir_builder_.SetCurrentDebugLocation(llvm::DebugLoc::get(line_no, 1, mb.dbg_scope_)); 77 | mb.ir_builder_.CreateBr(merge_block); 78 | } 79 | mb.exited_block_ = false; 80 | 81 | mb.function_->getBasicBlockList().push_back(merge_block); 82 | mb.ir_builder_.SetInsertPoint(merge_block); 83 | } 84 | 85 | template::value_type, bool>>> 87 | void if_(Condition&& cnd, TrueBlock&& tb) { 88 | auto& mb = *detail::current_builder; 89 | 90 | auto line_no = mb.source_code_.add_line(fmt::format("if ({}) {{", cnd)); 91 | mb.ir_builder_.SetCurrentDebugLocation(llvm::DebugLoc::get(line_no, 1, mb.dbg_scope_)); 92 | 93 | auto true_block = llvm::BasicBlock::Create(*mb.context_, "true_block", mb.function_); 94 | auto merge_block = llvm::BasicBlock::Create(*mb.context_, "merge_block"); 95 | 96 | mb.ir_builder_.CreateCondBr(cnd.eval(), true_block, merge_block); 97 | 98 | mb.ir_builder_.SetInsertPoint(true_block); 99 | mb.source_code_.enter_scope(); 100 | 101 | auto scope = mb.dbg_builder_.createLexicalBlock(mb.dbg_scope_, mb.dbg_file_, mb.source_code_.current_line(), 1); 102 | auto parent_scope = std::exchange(mb.dbg_scope_, scope); 103 | 104 | assert(!mb.exited_block_); 105 | tb(); 106 | mb.source_code_.leave_scope(); 107 | 108 | mb.dbg_scope_ = parent_scope; 109 | 110 | line_no = mb.source_code_.add_line("}"); 111 | 112 | if (!mb.exited_block_) { 113 | mb.ir_builder_.SetCurrentDebugLocation(llvm::DebugLoc::get(line_no, 1, mb.dbg_scope_)); 114 | mb.ir_builder_.CreateBr(merge_block); 115 | } 116 | mb.exited_block_ = false; 117 | 118 | mb.function_->getBasicBlockList().push_back(merge_block); 119 | mb.ir_builder_.SetInsertPoint(merge_block); 120 | } 121 | 122 | template 123 | value call(function_ref const& fn, Values&&... args) { 124 | static_assert((std::is_same_v::value_type> && ...)); 125 | 126 | auto& mb = *detail::current_builder; 127 | 128 | auto str = std::stringstream{}; 129 | str << fn.name() << "_ret = " << fn.name() << "("; 130 | (void)(str << ... << fmt::format("{}, ", args)); 131 | str << ");"; 132 | auto line_no = mb.source_code_.add_line(str.str()); 133 | 134 | mb.ir_builder_.SetCurrentDebugLocation(llvm::DebugLoc::get(line_no, 1, mb.dbg_scope_)); 135 | 136 | auto values = std::vector{}; 137 | [[maybe_unused]] auto _ = {0, ((values.emplace_back(args.eval())), 0)...}; 138 | 139 | auto ret = mb.ir_builder_.CreateCall(fn, values); 140 | return value{ret, fmt::format("{}_ret", fn.name())}; 141 | } 142 | 143 | template::value_type>>> 144 | auto load(Pointer ptr) { 145 | using value_type = std::remove_cv_t::value_type>>; 146 | auto& mb = *detail::current_builder; 147 | 148 | auto id = fmt::format("val{}", detail::id_counter++); 149 | 150 | auto line_no = mb.source_code_.add_line(fmt::format("{} = *{}", id, ptr)); 151 | mb.ir_builder_.SetCurrentDebugLocation(llvm::DebugLoc::get(line_no, 1, mb.dbg_scope_)); 152 | auto v = mb.ir_builder_.CreateAlignedLoad(ptr.eval(), detail::type::alignment); 153 | 154 | auto dbg_value = 155 | mb.dbg_builder_.createAutoVariable(mb.dbg_scope_, id, mb.dbg_file_, line_no, detail::type::dbg()); 156 | mb.dbg_builder_.insertDbgValueIntrinsic(v, dbg_value, mb.dbg_builder_.createExpression(), 157 | llvm::DebugLoc::get(line_no, 1, mb.dbg_scope_), 158 | mb.ir_builder_.GetInsertBlock()); 159 | 160 | return value{v, id}; 161 | } 162 | 163 | template< 164 | typename Value, typename Pointer, 165 | typename = std::enable_if_t::value_type> && 166 | !std::is_const_v::value_type>> && 167 | std::is_same_v::value_type, 168 | std::remove_pointer_t::value_type>>>> 169 | void store(Value v, Pointer ptr) { 170 | using value_type = std::remove_pointer_t::value_type>; 171 | auto& mb = *detail::current_builder; 172 | 173 | auto line_no = mb.source_code_.add_line(fmt::format("*{} = {}", ptr, v)); 174 | mb.ir_builder_.SetCurrentDebugLocation(llvm::DebugLoc::get(line_no, 1, mb.dbg_scope_)); 175 | mb.ir_builder_.CreateAlignedStore(v.eval(), ptr.eval(), detail::type::alignment); 176 | } 177 | 178 | template::value_type, bool>>> 180 | void while_(ConditionFn cnd_fn, Body bdy) { 181 | auto& mb = *detail::current_builder; 182 | 183 | auto line_no = mb.source_code_.current_line() + 1; 184 | mb.ir_builder_.SetCurrentDebugLocation(llvm::DebugLoc::get(line_no, 1, mb.dbg_scope_)); 185 | auto cnd = cnd_fn(); 186 | mb.source_code_.add_line(fmt::format("while ({}) {{", cnd)); 187 | 188 | auto while_continue = llvm::BasicBlock::Create(*mb.context_, "while_continue", mb.function_); 189 | auto while_iteration = llvm::BasicBlock::Create(*mb.context_, "while_iteration"); 190 | auto while_break = llvm::BasicBlock::Create(*mb.context_, "while_break"); 191 | 192 | auto parent_loop = std::exchange(mb.current_loop_, module_builder::loop{while_continue, while_break}); 193 | 194 | mb.ir_builder_.CreateBr(while_continue); 195 | mb.ir_builder_.SetInsertPoint(while_continue); 196 | 197 | mb.ir_builder_.CreateCondBr(cnd_fn().eval(), while_iteration, while_break); 198 | 199 | mb.source_code_.enter_scope(); 200 | 201 | auto scope = mb.dbg_builder_.createLexicalBlock(mb.dbg_scope_, mb.dbg_file_, mb.source_code_.current_line(), 1); 202 | auto parent_scope = std::exchange(mb.dbg_scope_, scope); 203 | 204 | mb.function_->getBasicBlockList().push_back(while_iteration); 205 | mb.ir_builder_.SetInsertPoint(while_iteration); 206 | 207 | assert(!mb.exited_block_); 208 | bdy(); 209 | 210 | mb.dbg_scope_ = parent_scope; 211 | 212 | mb.source_code_.leave_scope(); 213 | line_no = mb.source_code_.add_line("}"); 214 | 215 | if (!mb.exited_block_) { 216 | mb.ir_builder_.SetCurrentDebugLocation(llvm::DebugLoc::get(line_no, 1, mb.dbg_scope_)); 217 | mb.ir_builder_.CreateBr(while_continue); 218 | } 219 | mb.exited_block_ = false; 220 | 221 | mb.function_->getBasicBlockList().push_back(while_break); 222 | mb.ir_builder_.SetInsertPoint(while_break); 223 | 224 | mb.current_loop_ = parent_loop; 225 | } 226 | 227 | void break_(); 228 | void continue_(); 229 | 230 | } // namespace codegen 231 | -------------------------------------------------------------------------------- /include/codegen/arithmetic_ops.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2019 Paweł Dziepak 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | #pragma once 24 | 25 | #include "codegen/module_builder.hpp" 26 | 27 | namespace codegen { 28 | 29 | namespace detail { 30 | 31 | enum class arithmetic_operation_type { 32 | add, 33 | sub, 34 | mul, 35 | div, 36 | mod, 37 | and_, 38 | or_, 39 | xor_, 40 | }; 41 | 42 | template class arithmetic_operation { 43 | LHS lhs_; 44 | RHS rhs_; 45 | 46 | static_assert(std::is_same_v); 47 | 48 | public: 49 | using value_type = typename LHS::value_type; 50 | 51 | arithmetic_operation(LHS lhs, RHS rhs) : lhs_(std::move(lhs)), rhs_(std::move(rhs)) {} 52 | 53 | llvm::Value* eval() const { 54 | if constexpr (std::is_integral_v) { 55 | switch (Op) { 56 | case arithmetic_operation_type::add: return current_builder->ir_builder_.CreateAdd(lhs_.eval(), rhs_.eval()); 57 | case arithmetic_operation_type::sub: return current_builder->ir_builder_.CreateSub(lhs_.eval(), rhs_.eval()); 58 | case arithmetic_operation_type::mul: return current_builder->ir_builder_.CreateMul(lhs_.eval(), rhs_.eval()); 59 | case arithmetic_operation_type::div: 60 | if constexpr (std::is_signed_v) { 61 | return current_builder->ir_builder_.CreateSDiv(lhs_.eval(), rhs_.eval()); 62 | } else { 63 | return current_builder->ir_builder_.CreateUDiv(lhs_.eval(), rhs_.eval()); 64 | } 65 | case arithmetic_operation_type::mod: 66 | if constexpr (std::is_signed_v) { 67 | return current_builder->ir_builder_.CreateSRem(lhs_.eval(), rhs_.eval()); 68 | } else { 69 | return current_builder->ir_builder_.CreateURem(lhs_.eval(), rhs_.eval()); 70 | } 71 | case arithmetic_operation_type::and_: return current_builder->ir_builder_.CreateAnd(lhs_.eval(), rhs_.eval()); 72 | case arithmetic_operation_type::or_: return current_builder->ir_builder_.CreateOr(lhs_.eval(), rhs_.eval()); 73 | case arithmetic_operation_type::xor_: return current_builder->ir_builder_.CreateXor(lhs_.eval(), rhs_.eval()); 74 | } 75 | } else { 76 | switch (Op) { 77 | case arithmetic_operation_type::add: return current_builder->ir_builder_.CreateFAdd(lhs_.eval(), rhs_.eval()); 78 | case arithmetic_operation_type::sub: return current_builder->ir_builder_.CreateFSub(lhs_.eval(), rhs_.eval()); 79 | case arithmetic_operation_type::mul: return current_builder->ir_builder_.CreateFMul(lhs_.eval(), rhs_.eval()); 80 | case arithmetic_operation_type::div: return current_builder->ir_builder_.CreateFDiv(lhs_.eval(), rhs_.eval()); 81 | case arithmetic_operation_type::mod: return current_builder->ir_builder_.CreateFRem(lhs_.eval(), rhs_.eval()); 82 | case arithmetic_operation_type::and_: [[fallthrough]]; 83 | case arithmetic_operation_type::or_: [[fallthrough]]; 84 | case arithmetic_operation_type::xor_: abort(); 85 | } 86 | } 87 | } 88 | 89 | friend std::ostream& operator<<(std::ostream& os, arithmetic_operation const& ao) { 90 | auto symbol = [] { 91 | switch (Op) { 92 | case arithmetic_operation_type::add: return '+'; 93 | case arithmetic_operation_type::sub: return '-'; 94 | case arithmetic_operation_type::mul: return '*'; 95 | case arithmetic_operation_type::div: return '/'; 96 | case arithmetic_operation_type::mod: return '%'; 97 | case arithmetic_operation_type::and_: return '&'; 98 | case arithmetic_operation_type::or_: return '|'; 99 | case arithmetic_operation_type::xor_: return '^'; 100 | } 101 | }(); 102 | return os << '(' << ao.lhs_ << ' ' << symbol << ' ' << ao.rhs_ << ')'; 103 | } 104 | }; 105 | 106 | enum class pointer_arithmetic_operation_type { 107 | add, 108 | sub, 109 | }; 110 | 111 | template class pointer_arithmetic_operation { 112 | LHS lhs_; 113 | RHS rhs_; 114 | 115 | static_assert(std::is_pointer_v); 116 | static_assert(std::is_integral_v); 117 | 118 | using rhs_value_type = typename RHS::value_type; 119 | 120 | public: 121 | using value_type = typename LHS::value_type; 122 | 123 | pointer_arithmetic_operation(LHS lhs, RHS rhs) : lhs_(std::move(lhs)), rhs_(std::move(rhs)) {} 124 | 125 | llvm::Value* eval() const { 126 | auto& mb = *current_builder; 127 | auto rhs = rhs_.eval(); 128 | if constexpr (sizeof(rhs_value_type) < sizeof(uint64_t)) { 129 | if constexpr (std::is_unsigned_v) { 130 | rhs = mb.ir_builder_.CreateZExt(rhs, type::llvm()); 131 | } else { 132 | rhs = mb.ir_builder_.CreateSExt(rhs, type::llvm()); 133 | } 134 | } 135 | switch (Op) { 136 | case pointer_arithmetic_operation_type::add: return mb.ir_builder_.CreateInBoundsGEP(lhs_.eval(), rhs); 137 | case pointer_arithmetic_operation_type::sub: 138 | return mb.ir_builder_.CreateInBoundsGEP(lhs_.eval(), mb.ir_builder_.CreateSub(constant(0), rhs)); 139 | } 140 | abort(); 141 | } 142 | 143 | friend std::ostream& operator<<(std::ostream& os, pointer_arithmetic_operation const& ao) { 144 | auto symbol = [] { 145 | switch (Op) { 146 | case pointer_arithmetic_operation_type::add: return '+'; 147 | case pointer_arithmetic_operation_type::sub: return '-'; 148 | } 149 | }(); 150 | return os << '(' << ao.lhs_ << ' ' << symbol << ' ' << ao.rhs_ << ')'; 151 | } 152 | }; 153 | 154 | } // namespace detail 155 | 156 | template && 158 | std::is_same_v>> 159 | auto operator+(LHS lhs, RHS rhs) { 160 | return detail::arithmetic_operation(std::move(lhs), std::move(rhs)); 161 | } 162 | 163 | template && 165 | std::is_same_v>> 166 | auto operator-(LHS lhs, RHS rhs) { 167 | return detail::arithmetic_operation(std::move(lhs), std::move(rhs)); 168 | } 169 | 170 | template && 172 | std::is_same_v>> 173 | auto operator*(LHS lhs, RHS rhs) { 174 | return detail::arithmetic_operation(std::move(lhs), std::move(rhs)); 175 | } 176 | 177 | template && 179 | std::is_same_v>> 180 | auto operator/(LHS lhs, RHS rhs) { 181 | return detail::arithmetic_operation(std::move(lhs), std::move(rhs)); 182 | } 183 | 184 | template && 186 | std::is_same_v>> 187 | auto operator%(LHS lhs, RHS rhs) { 188 | return detail::arithmetic_operation(std::move(lhs), std::move(rhs)); 189 | } 190 | 191 | template && 193 | std::is_same_v>> 194 | auto operator&(LHS lhs, RHS rhs) { 195 | return detail::arithmetic_operation(std::move(lhs), 196 | std::move(rhs)); 197 | } 198 | 199 | template && 201 | std::is_same_v>> 202 | auto operator|(LHS lhs, RHS rhs) { 203 | return detail::arithmetic_operation(std::move(lhs), std::move(rhs)); 204 | } 205 | 206 | template && 208 | std::is_same_v>> 209 | auto operator^(LHS lhs, RHS rhs) { 210 | return detail::arithmetic_operation(std::move(lhs), 211 | std::move(rhs)); 212 | } 213 | 214 | template && 216 | std::is_pointer_v>, 217 | typename = void> 218 | auto operator+(LHS lhs, RHS rhs) { 219 | return detail::pointer_arithmetic_operation(std::move(lhs), 220 | std::move(rhs)); 221 | } 222 | 223 | template && 225 | std::is_pointer_v>, 226 | typename = void> 227 | auto operator-(LHS lhs, RHS rhs) { 228 | return detail::pointer_arithmetic_operation(std::move(lhs), 229 | std::move(rhs)); 230 | } 231 | 232 | } // namespace codegen 233 | -------------------------------------------------------------------------------- /include/codegen/module_builder.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright © 2019 Paweł Dziepak 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | * SOFTWARE. 21 | */ 22 | 23 | #pragma once 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include 35 | #include 36 | 37 | namespace codegen { 38 | 39 | class compiler; 40 | class module; 41 | 42 | template class function_ref { 43 | std::string name_; 44 | llvm::Function* function_; 45 | 46 | public: 47 | explicit function_ref(std::string const& name, llvm::Function* fn) : name_(name), function_(fn) {} 48 | 49 | operator llvm::Function*() const { return function_; } 50 | 51 | std::string const& name() const { return name_; } 52 | }; 53 | 54 | class module_builder { 55 | compiler* compiler_; 56 | 57 | public: // FIXME: proper encapsulation 58 | std::unique_ptr context_; 59 | std::unique_ptr module_; 60 | 61 | llvm::IRBuilder<> ir_builder_; 62 | 63 | llvm::Function* function_{}; 64 | 65 | class source_code_generator { 66 | std::stringstream source_code_; 67 | unsigned line_no_ = 1; 68 | unsigned indent_ = 0; 69 | 70 | public: 71 | unsigned add_line(std::string const&); 72 | void enter_scope() { indent_ += 4; } 73 | void leave_scope() { indent_ -= 4; } 74 | unsigned current_line() const { return line_no_; } 75 | std::string get() const; 76 | }; 77 | source_code_generator source_code_; 78 | std::filesystem::path source_file_; 79 | 80 | struct loop { 81 | llvm::BasicBlock* continue_block_ = nullptr; 82 | llvm::BasicBlock* break_block_ = nullptr; 83 | }; 84 | loop current_loop_; 85 | bool exited_block_ = false; 86 | 87 | llvm::DIBuilder dbg_builder_; 88 | 89 | llvm::DIFile* dbg_file_; 90 | llvm::DIScope* dbg_scope_; 91 | 92 | public: 93 | module_builder(compiler&, std::string const& name); 94 | 95 | module_builder(module_builder const&) = delete; 96 | module_builder(module_builder&&) = delete; 97 | 98 | template 99 | auto create_function(std::string const& name, FunctionBuilder&& fb); 100 | 101 | template auto declare_external_function(std::string const& name, FunctionType* fn); 102 | 103 | [[nodiscard]] module build() &&; 104 | 105 | friend std::ostream& operator<<(std::ostream&, module_builder const&); 106 | 107 | private: 108 | void set_function_attributes(llvm::Function*); 109 | 110 | void declare_external_symbol(std::string const&, void*); 111 | }; 112 | 113 | namespace detail { 114 | 115 | inline thread_local module_builder* current_builder; 116 | 117 | template struct type { 118 | static_assert(std::is_integral_v); 119 | static constexpr size_t alignment = alignof(Type); 120 | static llvm::DIType* dbg() { 121 | return current_builder->dbg_builder_.createBasicType( 122 | name(), sizeof(Type) * 8, std::is_signed_v ? llvm::dwarf::DW_ATE_signed : llvm::dwarf::DW_ATE_unsigned); 123 | } 124 | static llvm::Type* llvm() { return llvm::Type::getIntNTy(*current_builder->context_, sizeof(Type) * 8); } 125 | static std::string name() { return fmt::format("{}{}", std::is_signed_v ? 'i' : 'u', sizeof(Type) * 8); } 126 | }; 127 | template<> struct type { 128 | static constexpr size_t alignment = 0; 129 | static llvm::DIType* dbg() { return nullptr; } 130 | static llvm::Type* llvm() { return llvm::Type::getVoidTy(*current_builder->context_); } 131 | static std::string name() { return "void"; } 132 | }; 133 | template<> struct type { 134 | static constexpr size_t alignment = alignof(bool); 135 | static llvm::DIType* dbg() { 136 | return current_builder->dbg_builder_.createBasicType(name(), 8, llvm::dwarf::DW_ATE_boolean); 137 | } 138 | static llvm::Type* llvm() { return llvm::Type::getInt1Ty(*current_builder->context_); } 139 | static std::string name() { return "bool"; } 140 | }; 141 | template<> struct type { 142 | static constexpr size_t alignment = 1; 143 | static llvm::DIType* dbg() { 144 | return current_builder->dbg_builder_.createBasicType(name(), 8, llvm::dwarf::DW_ATE_unsigned); 145 | } 146 | static llvm::Type* llvm() { return llvm::Type::getInt8Ty(*current_builder->context_); } 147 | static std::string name() { return "byte"; } 148 | }; 149 | template<> struct type { 150 | static constexpr size_t alignment = alignof(float); 151 | static llvm::DIType* dbg() { 152 | return current_builder->dbg_builder_.createBasicType(name(), 32, llvm::dwarf::DW_ATE_float); 153 | } 154 | static llvm::Type* llvm() { return llvm::Type::getFloatTy(*current_builder->context_); } 155 | static std::string name() { return "f32"; } 156 | }; 157 | template<> struct type { 158 | static constexpr size_t alignment = alignof(double); 159 | static llvm::DIType* dbg() { 160 | return current_builder->dbg_builder_.createBasicType(name(), 64, llvm::dwarf::DW_ATE_float); 161 | } 162 | static llvm::Type* llvm() { return llvm::Type::getDoubleTy(*current_builder->context_); } 163 | static std::string name() { return "f64"; } 164 | }; 165 | template struct type { 166 | static constexpr size_t alignment = alignof(Type*); 167 | static llvm::DIType* dbg() { 168 | return current_builder->dbg_builder_.createPointerType(type>::dbg(), sizeof(Type*) * 8); 169 | } 170 | static llvm::Type* llvm() { return type>::llvm()->getPointerTo(); } 171 | static std::string name() { return type>::name() + '*'; } 172 | }; 173 | 174 | template std::enable_if_t, llvm::Value*> get_constant(Type v) { 175 | if constexpr (std::is_integral_v) { 176 | return llvm::ConstantInt::get(*current_builder->context_, llvm::APInt(sizeof(Type) * 8, v, std::is_signed_v)); 177 | } else if constexpr (std::is_floating_point_v) { 178 | return llvm::ConstantFP::get(*current_builder->context_, llvm::APFloat(v)); 179 | } 180 | } 181 | 182 | template<> inline llvm::Value* get_constant(bool v) { 183 | return llvm::ConstantInt::get(*current_builder->context_, llvm::APInt(1, v, true)); 184 | } 185 | 186 | } // namespace detail 187 | 188 | template class value { 189 | llvm::Value* value_; 190 | std::string name_; 191 | 192 | public: 193 | static_assert(!std::is_const_v); 194 | static_assert(!std::is_volatile_v); 195 | 196 | explicit value(llvm::Value* v, std::string const& n) : value_(v), name_(n) {} 197 | 198 | value(value const&) = default; 199 | value(value&&) = default; 200 | void operator=(value const&) = delete; 201 | void operator=(value&&) = delete; 202 | 203 | using value_type = Type; 204 | 205 | operator llvm::Value*() const noexcept { return value_; } 206 | 207 | llvm::Value* eval() const { return value_; } 208 | 209 | friend std::ostream& operator<<(std::ostream& os, value v) { return os << v.name_; } 210 | }; 211 | 212 | template value constant(Type v) { 213 | return value{detail::get_constant(v), [&] { 214 | if constexpr (std::is_same_v) { 215 | return v ? "true" : "false"; 216 | } else { 217 | return std::to_string(v); 218 | } 219 | }()}; 220 | } 221 | 222 | value true_(); 223 | value false_(); 224 | 225 | namespace detail { 226 | 227 | template class bit_cast_impl { 228 | FromValue from_value_; 229 | 230 | using from_type = typename FromValue::value_type; 231 | 232 | public: 233 | static_assert(sizeof(from_type) == sizeof(ToType)); 234 | static_assert(std::is_pointer_v == std::is_pointer_v); 235 | 236 | using value_type = ToType; 237 | 238 | bit_cast_impl(FromValue fv) : from_value_(fv) {} 239 | 240 | llvm::Value* eval() { 241 | return detail::current_builder->ir_builder_.CreateBitCast(from_value_.eval(), type::llvm()); 242 | } 243 | 244 | friend std::ostream& operator<<(std::ostream& os, bit_cast_impl bci) { 245 | return os << "bit_cast<" << type::name() << ">(" << bci.from_value_ << ")"; 246 | } 247 | }; 248 | 249 | template class cast_impl { 250 | FromValue from_value_; 251 | 252 | using from_type = typename FromValue::value_type; 253 | using to_type = ToType; 254 | 255 | public: 256 | static_assert(!std::is_pointer_v && !std::is_pointer_v); 257 | 258 | using value_type = ToType; 259 | 260 | cast_impl(FromValue fv) : from_value_(fv) {} 261 | 262 | llvm::Value* eval() { 263 | auto& mb = *current_builder; 264 | if constexpr (std::is_floating_point_v && std::is_floating_point_v) { 265 | return mb.ir_builder_.CreateFPCast(from_value_.eval(), type::llvm()); 266 | } else if constexpr (std::is_floating_point_v && std::is_integral_v) { 267 | if constexpr (std::is_signed_v) { 268 | return mb.ir_builder_.CreateFPToSI(from_value_.eval(), type::llvm()); 269 | } else { 270 | return mb.ir_builder_.CreateFPToUI(from_value_.eval(), type::llvm()); 271 | } 272 | } else if constexpr (std::is_integral_v && std::is_floating_point_v) { 273 | if constexpr (std::is_signed_v) { 274 | return mb.ir_builder_.CreateSIToFP(from_value_.eval(), type::llvm()); 275 | } else { 276 | return mb.ir_builder_.CreateUIToFP(from_value_.eval(), type::llvm()); 277 | } 278 | } else if constexpr (std::is_integral_v && std::is_integral_v) { 279 | if constexpr (std::is_signed_v) { 280 | return mb.ir_builder_.CreateSExtOrTrunc(from_value_.eval(), type::llvm()); 281 | } else { 282 | return mb.ir_builder_.CreateZExtOrTrunc(from_value_.eval(), type::llvm()); 283 | } 284 | } 285 | } 286 | 287 | friend std::ostream& operator<<(std::ostream& os, cast_impl ci) { 288 | return os << "cast<" << type::name() << ">(" << ci.from_value_ << ")"; 289 | } 290 | }; 291 | 292 | } // namespace detail 293 | 294 | template auto bit_cast(FromValue v) { 295 | return detail::bit_cast_impl(v); 296 | } 297 | 298 | template auto cast(FromValue v) { 299 | return detail::cast_impl(v); 300 | } 301 | 302 | void return_(); 303 | 304 | template void return_(Value v) { 305 | auto& mb = *detail::current_builder; 306 | mb.exited_block_ = true; 307 | auto line_no = mb.source_code_.add_line(fmt::format("return {};", v)); 308 | mb.ir_builder_.SetCurrentDebugLocation(llvm::DebugLoc::get(line_no, 1, mb.dbg_scope_)); 309 | mb.ir_builder_.CreateRet(v.eval()); 310 | } 311 | 312 | namespace detail { 313 | 314 | template class function_builder; 315 | 316 | template class function_builder { 317 | template void prepare_argument(llvm::Function::arg_iterator args, size_t idx) { 318 | auto& mb = *current_builder; 319 | 320 | auto it = args + idx; 321 | auto name = "arg" + std::to_string(idx); 322 | it->setName(name); 323 | 324 | auto dbg_arg = mb.dbg_builder_.createParameterVariable(mb.dbg_scope_, name, idx + 1, mb.dbg_file_, 325 | mb.source_code_.current_line(), type::dbg()); 326 | mb.dbg_builder_.insertDbgValueIntrinsic(&*(args + idx), dbg_arg, mb.dbg_builder_.createExpression(), 327 | llvm::DebugLoc::get(mb.source_code_.current_line(), 1, mb.dbg_scope_), 328 | mb.ir_builder_.GetInsertBlock()); 329 | } 330 | 331 | template 332 | void call_builder(std::index_sequence, std::string const& name, FunctionBuilder&& fb, 333 | llvm::Function::arg_iterator args) { 334 | auto& mb = *current_builder; 335 | 336 | auto str = std::stringstream{}; 337 | str << type::name() << " " << name << "("; 338 | (void)(str << ... 339 | << (type::name() + " arg" + std::to_string(Idx) + (Idx + 1 == sizeof...(Idx) ? "" : ", "))); 340 | str << ") {"; 341 | mb.source_code_.add_line(str.str()); 342 | mb.source_code_.enter_scope(); 343 | 344 | [[maybe_unused]] auto _ = {0, (prepare_argument(args, Idx), 0)...}; 345 | fb(value(&*(args + Idx), "arg" + std::to_string(Idx))...); 346 | 347 | mb.source_code_.leave_scope(); 348 | mb.source_code_.add_line("}"); 349 | } 350 | 351 | public: 352 | template 353 | function_ref operator()(std::string const& name, FunctionBuilder&& fb) { 354 | auto& mb = *current_builder; 355 | auto fn_type = llvm::FunctionType::get(type::llvm(), {type::llvm()...}, false); 356 | auto fn = llvm::Function::Create(fn_type, llvm::GlobalValue::LinkageTypes::ExternalLinkage, name, mb.module_.get()); 357 | 358 | std::vector dbg_types = {detail::type::dbg(), detail::type::dbg()...}; 359 | auto dbg_fn_type = mb.dbg_builder_.createSubroutineType(mb.dbg_builder_.getOrCreateTypeArray(dbg_types)); 360 | auto dbg_fn_scope = mb.dbg_builder_.createFunction( 361 | mb.dbg_scope_, name, name, mb.dbg_file_, mb.source_code_.current_line(), dbg_fn_type, 362 | mb.source_code_.current_line(), llvm::DINode::FlagPrototyped, 363 | llvm::DISubprogram::DISPFlags::SPFlagDefinition | llvm::DISubprogram::DISPFlags::SPFlagOptimized); 364 | auto parent_scope = std::exchange(mb.dbg_scope_, dbg_fn_scope); 365 | fn->setSubprogram(dbg_fn_scope); 366 | 367 | mb.ir_builder_.SetCurrentDebugLocation(llvm::DebugLoc{}); 368 | 369 | auto block = llvm::BasicBlock::Create(*mb.context_, "entry", fn); 370 | mb.ir_builder_.SetInsertPoint(block); 371 | 372 | mb.function_ = fn; 373 | call_builder(std::index_sequence_for{}, name, fb, fn->arg_begin()); 374 | 375 | mb.dbg_scope_ = parent_scope; 376 | 377 | return function_ref{name, fn}; 378 | } 379 | }; 380 | 381 | } // namespace detail 382 | 383 | template 384 | auto module_builder::create_function(std::string const& name, FunctionBuilder&& fb) { 385 | assert(detail::current_builder == this || !detail::current_builder); 386 | auto prev_builder = std::exchange(detail::current_builder, this); 387 | exited_block_ = false; 388 | auto fn_ref = detail::function_builder{}(name, fb); 389 | set_function_attributes(fn_ref); 390 | detail::current_builder = prev_builder; 391 | return fn_ref; 392 | } 393 | 394 | namespace detail { 395 | 396 | template class function_declaration_builder; 397 | 398 | template class function_declaration_builder { 399 | public: 400 | function_ref operator()(std::string const& name) { 401 | auto& mb = *current_builder; 402 | 403 | auto fn_type = llvm::FunctionType::get(type::llvm(), {type::llvm()...}, false); 404 | auto fn = llvm::Function::Create(fn_type, llvm::GlobalValue::LinkageTypes::ExternalLinkage, name, mb.module_.get()); 405 | 406 | return function_ref{name, fn}; 407 | } 408 | }; 409 | 410 | } // namespace detail 411 | 412 | template 413 | auto module_builder::declare_external_function(std::string const& name, FunctionType* fn) { 414 | assert(detail::current_builder == this || !detail::current_builder); 415 | 416 | auto prev_builder = std::exchange(detail::current_builder, this); 417 | auto fn_ref = detail::function_declaration_builder{}(name); 418 | detail::current_builder = prev_builder; 419 | 420 | declare_external_symbol(name, reinterpret_cast(fn)); 421 | 422 | return fn_ref; 423 | } 424 | 425 | } // namespace codegen 426 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CodeGen 2 | 3 | [![Build Status](https://travis-ci.com/pdziepak/codegen.svg?branch=master)](https://travis-ci.com/pdziepak/codegen) 4 | [![codecov](https://codecov.io/gh/pdziepak/codegen/branch/master/graph/badge.svg)](https://codecov.io/gh/pdziepak/codegen) 5 | 6 | Experimental wrapper over LLVM for generating and compiling code at run-time. 7 | 8 | ## About 9 | 10 | CodeGen is a library that builds on top of LLVM. It facilitates just-in-time code generation and compilation, including debugging information and human-readable source code. C++ type system is employed to guard against, at least some, errors in the generated intermediate representation. The intention is to allow the application to improve performance by taking advantage of information that becomes available only once it is running. A sample use case would be prepared statements in of database engines. 11 | 12 | The general idea is not unlike that described in [P1609R0: C++ Should Support Just-in-Time Compilation](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1609r0.html). 13 | 14 | ## Building 15 | 16 | The build requirements are as follows: 17 | 18 | * CMake 3.12 19 | * GCC 8+ or Clang 8+ 20 | * LLVM 8 21 | * fmt 22 | * Google Test (optional) 23 | 24 | `fedora:30` docker container may be a good place to start. 25 | 26 | The build instructions are quite usual for a CMake-based project: 27 | 28 | ``` 29 | cd 30 | cmake -DCMAKE_BUILD_TYPE= -G Ninja 31 | ninja 32 | ninja test 33 | ``` 34 | 35 | ## Design 36 | 37 | The main object representing the JIT compiler is `codegen::compiler`. All function pointers to the compiled code remain valid during its lifetime. `codegen::module_builder` allows creating an LLVM builder, while `codegen::module` represents an already compiled module. The general template that for CodeGen use looks as follows: 38 | 39 | ```c++ 40 | namespace cg = codegen; 41 | auto compiler = cg::compiler{}; 42 | auto builder = cg::module_builder(compiler, "module_name"); 43 | 44 | auto function_reference = builder.create_function("function_name", 45 | [](cg::value v) { 46 | /* more logic here */ 47 | cg::return_(v + cg::constant(1)); 48 | }); 49 | 50 | auto module = std::move(builder).build(); 51 | using function_pointer_type = int(*)(int); 52 | function_pointer_type function_pointer = module.get_address(function_reference); 53 | ``` 54 | 55 | The code above compiles a function that returns an integer that was passed to it as an argument incremented by one. Each module may contain multiple functions. `codegen::module_builder::create_function` returns a function reference that can be used to obtain a pointer to the function after the module is compiled (as in this example) or to call it from another function generated with CodeGen. 56 | 57 | `codegen::value` is a typed equivalent of `llvm::Value` and represents a SSA value. As of now, only fundamental types are supported. CodeGen provides operators for those arithmetic and relational operations that make sense for a given type. Expression templates are used in a limited fashion to allow producing more concise human-readable source code. Unlike C++ there are no automatic promotions or implicit casts of any kind. Instead, `bit_cast` or `cast` need to be explicitly used where needed. 58 | 59 | SSA starts getting a bit more cumbersome to use once the control flow diverges, and a Φ function is required. This can be avoided by using local variables `codegen::variable`. The resulting IR is not going to be perfect, but the LLVM optimisation passes tend to do an excellent job converting those memory accesses. 60 | 61 | ### Statements 62 | 63 | 64 | * `return_()`, `return_(Value)` – returns from function. Note, that the type of the returned value is not verified and CodeGen will not prevent generating a function of type `int()` that returns `void`. 65 | * `load(Pointer)` – takes a pointer of type `T*` and loads the value from memory, `codegen::value`. 66 | * `store(Value, Pointer)` – stores `Value` of type `T` at the location pointed to by `Pointer`. The type of the pointer needs to be `T*`. 67 | * `if_(Value, TrueBlock, FalseBlock)`, `if_(Value, TrueBlock)` – an `if` conditional statement. The type of the provided value needs to be `bool`. `TrueBlock` and `FalseBlock` are expected to be lambdas. For example: 68 | 69 | ```c++ 70 | auto silly_function = builder.create_function("silly_function", 71 | [](cg::value is_true) { 72 | cg::if_(is_true, [] { cg::return_(cg::true_()); }, [] { cg::return_(cg::false_()); }); 73 | }); 74 | ``` 75 | 76 | * `while_(Condition, LoopBody)` – a `while` loop. `Condition` is a lambda returning a value of type `bool`. `LoopBody` is a lambda that generates the body of the loop. For example: 77 | 78 | ```c++ 79 | auto silly_function2 = builder.create_function("silly_function2", 80 | [](cg::value target) { 81 | auto var = cg::variable("var", cg::constant(0)); 82 | cg::while_([&] { return var.get() < target; }, 83 | [&] { 84 | var.set(var.get() + cg::constant(1)); 85 | }); 86 | cg::return_(var.get()); 87 | }); 88 | ``` 89 | 90 | * `call(Function, Arguments...)` – a function call. `Function` is a function reference. `Arguments...` is a list of arguments matching the function type. 91 | 92 | ## Examples 93 | 94 | ### Tuple comparator 95 | 96 | 97 | In this example, let's consider tuples which element's types are known only at run-time. If the goal is to write a less-comparator for such tuples, the naive approach would be to have a virtual function call for each element. That is far from ideal if the actual comparison is very cheap, e.g. the elements are integers. With CodeGen, we can do better. First, let's write a comparator for an element of a fundamental type: 98 | 99 | ```c++ 100 | template 101 | size_t less_cmp(cg::value a_ptr, cg::value b_ptr, size_t off) { 102 | auto a_val = cg::load(cg::bit_cast(a_ptr + cg::constant(off))); 103 | auto b_val = cg::load(cg::bit_cast(b_ptr + cg::constant(off))); 104 | cg::if_(a_val < b_val, [&] { cg::return_(cg::true_()); }); 105 | cg::if_(a_val > b_val, [&] { cg::return_(cg::false_()); }); 106 | return sizeof(T) + off; 107 | } 108 | ``` 109 | 110 | This function template generates comparison code for any fundamental type. The arguments are pointers to buffers containing both tuples and an offset at which the element is located. The return value is the offset of the next element. 111 | 112 | Now, let's say we want to generate a less-comparator for `tuple`. 113 | 114 | ```c++ 115 | auto less = builder.create_function( 116 | "less", [&](cg::value a_ptr, cg::value b_ptr) { 117 | size_t offset = 0; 118 | offset = less_cmp(a_ptr, b_ptr, offset); 119 | offset = less_cmp(a_ptr, b_ptr, offset); 120 | offset = less_cmp(a_ptr, b_ptr, offset); 121 | (void)offset; 122 | cg::return_(cg::false_()); 123 | }); 124 | ``` 125 | 126 | As we can see, building the actual comparator is quite straightforward. The human-readable source code that CodeGen generates looks like this: 127 | 128 | ```c 129 | 1 bool less(byte* arg0, byte* arg1) { 130 | 2 val0 = *bit_cast((arg0 + 0)) 131 | 3 val1 = *bit_cast((arg1 + 0)) 132 | 4 if ((val0 < val1)) { 133 | 5 return true; 134 | 6 } 135 | 7 if ((val0 > val1)) { 136 | 8 return false; 137 | 9 } 138 | 10 val2 = *bit_cast((arg0 + 4)) 139 | 11 val3 = *bit_cast((arg1 + 4)) 140 | 12 if ((val2 < val3)) { 141 | 13 return true; 142 | 14 } 143 | 15 if ((val2 > val3)) { 144 | 16 return false; 145 | 17 } 146 | 18 val4 = *bit_cast((arg0 + 8)) 147 | 19 val5 = *bit_cast((arg1 + 8)) 148 | 20 if ((val4 < val5)) { 149 | 21 return true; 150 | 22 } 151 | 23 if ((val4 > val5)) { 152 | 24 return false; 153 | 25 } 154 | 26 return false; 155 | 27 } 156 | 157 | ``` 158 | 159 | The assembly that LLVM emits: 160 | 161 | ```x86asm 162 | 0x00007fffefd57000 <+0>: mov (%rdi),%ecx 163 | 0x00007fffefd57002 <+2>: mov (%rsi),%edx 164 | 0x00007fffefd57004 <+4>: mov $0x1,%al 165 | 0x00007fffefd57006 <+6>: cmp %edx,%ecx 166 | 0x00007fffefd57008 <+8>: jl 0x7fffefd57026 167 | 0x00007fffefd5700a <+10>: cmp %edx,%ecx 168 | 0x00007fffefd5700c <+12>: jg 0x7fffefd57024 169 | 0x00007fffefd5700e <+14>: vmovss 0x4(%rdi),%xmm0 170 | 0x00007fffefd57013 <+19>: vmovss 0x4(%rsi),%xmm1 171 | 0x00007fffefd57018 <+24>: vucomiss %xmm0,%xmm1 172 | 0x00007fffefd5701c <+28>: ja 0x7fffefd57026 173 | 0x00007fffefd5701e <+30>: vucomiss %xmm1,%xmm0 174 | 0x00007fffefd57022 <+34>: jbe 0x7fffefd57027 175 | 0x00007fffefd57024 <+36>: xor %eax,%eax 176 | 0x00007fffefd57026 <+38>: retq 177 | 0x00007fffefd57027 <+39>: movzwl 0x8(%rdi),%eax 178 | 0x00007fffefd5702b <+43>: cmp 0x8(%rsi),%ax 179 | 0x00007fffefd5702f <+47>: setb %al 180 | 0x00007fffefd57032 <+50>: retq 181 | ``` 182 | 183 | Since CodeGen takes care of emitting all necessary debugging information, and informing GDB about the JIT-ed functions, the debugging experience shouldn't be too bad: 184 | 185 | ``` 186 | (gdb) b 3 187 | Breakpoint 2 at 0x7fffefd57002: file /tmp/examples-11076310111440055155/tuple_i32f32u16_less.txt, line 3. 188 | (gdb) c 189 | Continuing. 190 | 191 | Breakpoint 2, less (arg0=0x60200001c7b0 "", arg1=0x60200001c790 "\001") at /tmp/examples-11076310111440055155/tuple_i32f32u16_less.txt:3 192 | 3 val1 = *bit_cast((arg1 + 0)) 193 | (gdb) p val0 194 | $1 = 0 195 | (gdb) n 196 | 4 if ((val0 < val1)) { 197 | (gdb) p val1 198 | $3 = 1 199 | (gdb) n 200 | less (arg0=0x60200001c7b0 "", arg1=0x60200001c790 "\001") at /tmp/examples-11076310111440055155/tuple_i32f32u16_less.txt:5 201 | 5 return true; 202 | ``` 203 | 204 | A more complicated example would be if one of the tuple elements was an ASCII string. The following code generates a comparator for `tuple` assuming that a string is serialised in the form of ``: 205 | 206 | ```c++ 207 | auto less = builder.create_function( 208 | "less", [&](cg::value a_ptr, cg::value b_ptr) { 209 | size_t offset = 0; 210 | offset = less_cmp(a_ptr, b_ptr, offset); 211 | 212 | auto a_len = cg::load(cg::bit_cast(a_ptr + cg::constant(offset))); 213 | auto b_len = cg::load(cg::bit_cast(b_ptr + cg::constant(offset))); 214 | // TODO: extract to a separate function 215 | auto len = cg::call(min, a_len, b_len); 216 | auto ret = cg::builtin::memcmp(a_ptr + cg::constant(offset) + 4_u64, 217 | b_ptr + cg::constant(offset) + 4_u64, len); 218 | cg::if_(ret < 0_i32, [&] { cg::return_(cg::true_()); }); 219 | cg::if_(ret > 0_i32, [&] { cg::return_(cg::false_()); }); 220 | cg::return_(a_len < b_len); 221 | }); 222 | ``` 223 | 224 | Let's look at the emitted assembly mixed with human-readable source code: 225 | 226 | ``` 227 | (gdb) disas /s less 228 | Dump of assembler code for function less: 229 | /tmp/examples-12144749341750180701/tuple_i32str_less.txt: 230 | 7 bool less(byte* arg0, byte* arg1) { 231 | 0x00007fffefd47010 <+0>: push %rbp 232 | 0x00007fffefd47011 <+1>: push %r14 233 | 0x00007fffefd47013 <+3>: push %rbx 234 | 235 | 8 val6 = *bit_cast((arg0 + 0)) 236 | 0x00007fffefd47014 <+4>: mov (%rdi),%eax 237 | 238 | 9 val7 = *bit_cast((arg1 + 0)) 239 | 0x00007fffefd47016 <+6>: mov (%rsi),%ecx 240 | 0x00007fffefd47018 <+8>: mov $0x1,%bl 241 | 242 | 10 if ((val6 < val7)) { 243 | 0x00007fffefd4701a <+10>: cmp %ecx,%eax 244 | 0x00007fffefd4701c <+12>: jl 0x7fffefd4704e 245 | 246 | 12 } 247 | 13 if ((val6 > val7)) { 248 | 0x00007fffefd4701e <+14>: cmp %ecx,%eax 249 | 0x00007fffefd47020 <+16>: jle 0x7fffefd47026 250 | 0x00007fffefd47022 <+18>: xor %ebx,%ebx 251 | 0x00007fffefd47024 <+20>: jmp 0x7fffefd4704e 252 | 253 | 14 return false; 254 | 15 } 255 | 16 val8 = *bit_cast((arg0 + 4)) 256 | 0x00007fffefd47026 <+22>: mov 0x4(%rdi),%r14d 257 | 258 | 17 val9 = *bit_cast((arg1 + 4)) 259 | 0x00007fffefd4702a <+26>: mov 0x4(%rsi),%ebp 260 | 261 | 2 if ((arg0 < arg1)) { 262 | 0x00007fffefd4702d <+29>: cmp %ebp,%r14d 263 | 0x00007fffefd47030 <+32>: mov %ebp,%edx 264 | 0x00007fffefd47032 <+34>: cmovb %r14d,%edx 265 | 266 | 18 min_ret = min(val8, val9, ); 267 | 19 memcmp_ret = memcmp(((arg0 + 4) + 4), ((arg1 + 4) + 4), min_ret); 268 | 0x00007fffefd47036 <+38>: add $0x8,%rdi 269 | 0x00007fffefd4703a <+42>: add $0x8,%rsi 270 | 0x00007fffefd4703e <+46>: movabs $0x7ffff764bd90,%rax 271 | 0x00007fffefd47048 <+56>: callq *%rax 272 | 273 | 20 if ((memcmp_ret < 0)) { 274 | 0x00007fffefd4704a <+58>: test %eax,%eax 275 | 0x00007fffefd4704c <+60>: jns 0x7fffefd47055 276 | 277 | 11 return true; 278 | 0x00007fffefd4704e <+62>: mov %ebx,%eax 279 | 0x00007fffefd47050 <+64>: pop %rbx 280 | 0x00007fffefd47051 <+65>: pop %r14 281 | 0x00007fffefd47053 <+67>: pop %rbp 282 | 0x00007fffefd47054 <+68>: retq 283 | 284 | 2 if ((arg0 < arg1)) { 285 | 0x00007fffefd47055 <+69>: cmp %ebp,%r14d 286 | 0x00007fffefd47058 <+72>: setb %cl 287 | 288 | 21 return true; 289 | 22 } 290 | 23 if ((memcmp_ret > 0)) { 291 | 0x00007fffefd4705b <+75>: test %eax,%eax 292 | 0x00007fffefd4705d <+77>: sete %al 293 | 0x00007fffefd47060 <+80>: and %cl,%al 294 | 0x00007fffefd47062 <+82>: pop %rbx 295 | 0x00007fffefd47063 <+83>: pop %r14 296 | 0x00007fffefd47065 <+85>: pop %rbp 297 | 0x00007fffefd47066 <+86>: retq 298 | ``` 299 | 300 | As we can see, LLVM has inlined calls to `min`. `memcmp` is an external function, so it could never be inlined. The source code lines match the assembly most of the time, but slight confusion there is expected since the code is compiled with aggressive optimisations. 301 | 302 | ### Vectorisation 303 | 304 | In the previous example, we knew the computations that we wanted to perform but didn't know the data. Let's now look at the opposite situation. The data organised as a structure of arrays, but we don't know ahead of time what arithmetic operations the application will need to execute. How the information about the desired computations is represented is out of the scope of CodeGen, though we may suspect an abstract syntax tree being involved there. The application would have to translate that to appropriate calls to CodeGen. For example, if for a value `a` and arrays `b` and `c` we wanted to compute `d[i] = a * b[i] + c[i]` it could be achieved by the code like this: 305 | 306 | ```c++ 307 | auto compute = builder.create_function( 308 | "compute", [&](cg::value a, cg::value b_ptr, cg::value c_ptr, 309 | cg::value d_ptr, cg::value n) { 310 | auto idx = cg::variable("idx", 0_u64); 311 | cg::while_([&] { return idx.get() < n; }, 312 | [&] { 313 | auto i = idx.get(); 314 | cg::store(a * cg::load(b_ptr + i) + cg::load(c_ptr + i), d_ptr + i); 315 | idx.set(i + 1_u64); 316 | }); 317 | cg::return_(); 318 | }); 319 | ``` 320 | 321 | CodeGen configures LLVM so that it takes advantage of the features available on the CPU it executes on. For instance, Skylake supports AVX2, so it is going to be used to vectorise the loop. 322 | 323 | ```x86asm 324 | 6 val11 = *(arg1 + idx) 325 | 7 *(arg3 + idx) = ((arg0 * val11) + val10) 326 | 0x00007fffefd27140 <+320>: vpmulld (%rsi,%r9,4),%ymm0,%ymm1 327 | 0x00007fffefd27146 <+326>: vpmulld 0x20(%rsi,%r9,4),%ymm0,%ymm2 328 | 0x00007fffefd2714d <+333>: vpmulld 0x40(%rsi,%r9,4),%ymm0,%ymm3 329 | 0x00007fffefd27154 <+340>: vpmulld 0x60(%rsi,%r9,4),%ymm0,%ymm4 330 | 0x00007fffefd2715b <+347>: vpaddd (%rdx,%r9,4),%ymm1,%ymm1 331 | 0x00007fffefd27161 <+353>: vpaddd 0x20(%rdx,%r9,4),%ymm2,%ymm2 332 | 0x00007fffefd27168 <+360>: vpaddd 0x40(%rdx,%r9,4),%ymm3,%ymm3 333 | 0x00007fffefd2716f <+367>: vpaddd 0x60(%rdx,%r9,4),%ymm4,%ymm4 334 | 0x00007fffefd27176 <+374>: vmovdqu %ymm1,(%rcx,%r9,4) 335 | 0x00007fffefd2717c <+380>: vmovdqu %ymm2,0x20(%rcx,%r9,4) 336 | 0x00007fffefd27183 <+387>: vmovdqu %ymm3,0x40(%rcx,%r9,4) 337 | 0x00007fffefd2718a <+394>: vmovdqu %ymm4,0x60(%rcx,%r9,4) 338 | 0x00007fffefd27191 <+401>: vpmulld 0x80(%rsi,%r9,4),%ymm0,%ymm1 339 | 0x00007fffefd2719b <+411>: vpmulld 0xa0(%rsi,%r9,4),%ymm0,%ymm2 340 | 0x00007fffefd271a5 <+421>: vpmulld 0xc0(%rsi,%r9,4),%ymm0,%ymm3 341 | 0x00007fffefd271af <+431>: vpmulld 0xe0(%rsi,%r9,4),%ymm0,%ymm4 342 | 0x00007fffefd271b9 <+441>: vpaddd 0x80(%rdx,%r9,4),%ymm1,%ymm1 343 | 0x00007fffefd271c3 <+451>: vpaddd 0xa0(%rdx,%r9,4),%ymm2,%ymm2 344 | 0x00007fffefd271cd <+461>: vpaddd 0xc0(%rdx,%r9,4),%ymm3,%ymm3 345 | 0x00007fffefd271d7 <+471>: vpaddd 0xe0(%rdx,%r9,4),%ymm4,%ymm4 346 | 0x00007fffefd271e1 <+481>: vmovdqu %ymm1,0x80(%rcx,%r9,4) 347 | 0x00007fffefd271eb <+491>: vmovdqu %ymm2,0xa0(%rcx,%r9,4) 348 | 0x00007fffefd271f5 <+501>: vmovdqu %ymm3,0xc0(%rcx,%r9,4) 349 | 0x00007fffefd271ff <+511>: vmovdqu %ymm4,0xe0(%rcx,%r9,4) 350 | 8 idx = (idx + 1); 351 | 0x00007fffefd27209 <+521>: add $0x40,%r9 352 | 0x00007fffefd2720d <+525>: add $0x2,%r11 353 | 0x00007fffefd27211 <+529>: jne 0x7fffefd27140 354 | 0x00007fffefd27217 <+535>: test %r10,%r10 355 | 0x00007fffefd2721a <+538>: je 0x7fffefd2726d 356 | ``` 357 | 358 | At the moment, CodeGen doesn't need to know anything about the ABI or the hardware architecture, which means that it can easily support all compilation targets that LLVM does. Below is the core part of the same loop compiled for aarch64 Cortex-A53. 359 | 360 | ``` 361 | 5 val10 = *(arg1 + idx) 362 | 0x0000007fb050e070 <+112>: ldp q1, q2, [x9, #-16] 363 | 364 | 6 val11 = *(arg2 + idx) 365 | 0x0000007fb050e074 <+116>: ldp q3, q4, [x10, #-16] 366 | 367 | 8 idx = (idx + 1); 368 | 0x0000007fb050e078 <+120>: add x9, x9, #0x20 369 | 0x0000007fb050e07c <+124>: add x10, x10, #0x20 370 | 371 | 7 *(arg3 + idx) = ((arg0 * val10) + val11) 372 | 0x0000007fb050e080 <+128>: mla v3.4s, v1.4s, v0.4s 373 | 374 | 8 idx = (idx + 1); 375 | 0x0000007fb050e084 <+132>: subs x12, x12, #0x8 376 | 377 | 7 *(arg3 + idx) = ((arg0 * val10) + val11) 378 | 0x0000007fb050e088 <+136>: mla v4.4s, v2.4s, v0.4s 379 | 0x0000007fb050e08c <+140>: stp q3, q4, [x11, #-16] 380 | 381 | 8 idx = (idx + 1); 382 | 0x0000007fb050e090 <+144>: add x11, x11, #0x20 383 | 0x0000007fb050e094 <+148>: b.ne 0x7fb050e070 // b.any 384 | ``` 385 | 386 | ## TODO 387 | 388 | * Support for aggregate types. This requires CodeGen to be aware of the ABI and would benefit if C++ had any form of static reflection. 389 | * Add missing operations (e.g. shifts). 390 | * Type-Based Alias Anaylsis. 391 | * Allow the user to tune optimisation options and disable generation of debugging information. 392 | * Bind compiled functions lifetimes to their module instead of the compiler object. 393 | * Support for other versions of LLVM. 394 | * Allow adding more metadata and attribute, e.g. `noalias` for function parameters. 395 | * Try harder to use C++ type system to prevent generation of invalid LLVM IR. 396 | * The TODO list is incomplete. Add more items to it. 397 | --------------------------------------------------------------------------------