xo-jit: initial commit (codegen constants + primitives, sort of)

This commit is contained in:
Roland Conybeare 2024-06-13 15:21:17 -04:00
commit 432c369a66
11 changed files with 394 additions and 0 deletions

8
.gitignore vendored Normal file
View file

@ -0,0 +1,8 @@
# emacs projectile config
.projectile
# clangd working space (see emacs+lsp)
.cache
# typical cmake build directory (source-tree-nephew)
.build*
# symlink to builddir/compile_commands.json; should be set manually in dev sandbox
compile_commands.json

36
CMakeLists.txt Normal file
View file

@ -0,0 +1,36 @@
# jit/CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(xo_jit VERSION 0.1)
include(GNUInstallDirs)
include(cmake/xo-bootstrap-macros.cmake)
xo_cxx_toplevel_options3()
# ----------------------------------------------------------------
# c++ settings
set(PROJECT_CXX_FLAGS "")
#set(PROJECT_CXX_FLAGS "-fconcepts-diagnostics-depth=2") # gcc-only!
add_definitions(${PROJECT_CXX_FLAGS})
# ----------------------------------------------------------------
# must complete definition of jit lib before configuring examples
add_subdirectory(src/jit)
xo_export_cmake_config(${PROJECT_NAME} ${PROJECT_VERSION} ${PROJECT_NAME}Targets)
# ----------------------------------------------------------------
add_subdirectory(example)
#add_subdirectory(utest)
# ----------------------------------------------------------------
# docs targets depend on all the other library/utest targets
#
#add_subdirectory(docs)
# end CMakeLists.txt

29
LICENSE Normal file
View file

@ -0,0 +1,29 @@
Copyright (c) 2024 Roland Conybeare <git3ub@nym.hush.com>, All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Please also refer to the file .github/CONTRIBUTING.md, which clarifies licensing of
external contributions to this project including patches, pull requests, etc.

35
cmake/xo-bootstrap-macros.cmake Executable file
View file

@ -0,0 +1,35 @@
# ----------------------------------------------------------------
# for example:
# $ PREFIX=/usr/local # for example
# $ cmake -DCMAKE_MODULE_PATH=prefix -DCMAKE_INSTALL_PREFIX=$PREFIX -B .build
#
# will get
# CMAKE_MODULE_PATH
# from xo-cmake-config --cmake-module-path
#
# and expect .cmake macros in
# CMAKE_MODULE_PATH/xo_macros/xo_cxx.cmake
# ----------------------------------------------------------------
find_program(XO_CMAKE_CONFIG_EXECUTABLE NAMES xo-cmake-config REQUIRED)
if ("${XO_CMAKE_CONFIG_EXECUTABLE}" STREQUAL "XO_CMAKE_CONFIG_EXECUTABLE-NOT_FOUND")
message(FATAL "could not find xo-cmake-config executable")
endif()
message(STATUS "XO_CMAKE_CONFIG_EXECUTABLE=${XO_CMAKE_CONFIG_EXECUTABLE}")
if (NOT XO_SUBMODULE_BUILD)
if (("${CMAKE_MODULE_PATH}" STREQUAL "") OR ("${CMAKE_MODULE_PATH}" STREQUAL prefix))
# default to typical install location for xo-project-macros
execute_process(COMMAND ${XO_CMAKE_CONFIG_EXECUTABLE} --cmake-module-path OUTPUT_VARIABLE CMAKE_MODULE_PATH)
message(STATUS "CMAKE_MODULE_PATH=${CMAKE_MODULE_PATH}")
endif()
endif()
# needs to have been installed somewhere on CMAKE_MODULE_PATH,
# (e.g. from xo-cmake with the same value for CMAKE_INSTALL_PREFIX)
#
include(xo_macros/xo_cxx)
xo_cxx_bootstrap_message()

View file

@ -0,0 +1,6 @@
@PACKAGE_INIT@
include(CMakeFindDependencyMacro)
find_dependency(xo_expression)
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")
check_required_components("@PROJECT_NAME@")

1
example/CMakeLists.txt Normal file
View file

@ -0,0 +1 @@
add_subdirectory(ex1)

View file

@ -0,0 +1,12 @@
# xo-jit/example/ex1/CMakeLists.txt
set(SELF_EXE xo_jit_ex1)
set(SELF_SRCS ex1.cpp)
if (XO_ENABLE_EXAMPLES)
xo_add_executable(${SELF_EXE} ${SELF_SRCS})
xo_self_dependency(${SELF_EXE} xo_jit)
#xo_dependency(${SELF_EXE} xo_expression)
endif()
# end CMakeLists.txt

56
example/ex1/ex1.cpp Normal file
View file

@ -0,0 +1,56 @@
/** @file ex1.cpp **/
#include "xo/jit/Jit.hpp"
#include "xo/expression/Constant.hpp"
#include "xo/expression/Primitive.hpp"
#include <iostream>
int
main() {
using xo::jit::Jit;
using xo::ast::make_constant;
using xo::ast::make_primitive;
using xo::xtag;
using std::cerr;
using std::endl;
//using xo::ast::make_constant;
auto jit = Jit::make();
{
auto expr = make_constant(7.0);
auto llvm_ircode = jit->codegen(expr);
if (llvm_ircode) {
/* note: llvm:errs() is 'raw stderr stream' */
cerr << "ex1 llvm_ircode:" << endl;
llvm_ircode->print(llvm::errs());
cerr << endl;
} else {
cerr << "ex1: code generation failed"
<< xtag("expr", expr)
<< endl;
}
}
{
auto expr = make_primitive("sqrt", ::sqrt);
auto llvm_ircode = jit->codegen(expr);
if (llvm_ircode) {
/* note: llvm:errs() is 'raw stderr stream' */
cerr << "ex1 llvm_ircode:" << endl;
llvm_ircode->print(llvm::errs());
cerr << endl;
} else {
cerr << "ex1: code generation failed"
<< xtag("expr", expr)
<< endl;
}
}
}
/** end ex1.cpp **/

76
include/xo/jit/Jit.hpp Normal file
View file

@ -0,0 +1,76 @@
/** @file Jit.hpp
*
* Author: Roland Conybeare
**/
#pragma once
//#include <cstdint>
#include "xo/refcnt/Refcounted.hpp"
#include "xo/expression/Expression.hpp"
#include "xo/expression/ConstantInterface.hpp"
#include "xo/expression/PrimitiveInterface.hpp"
#include "llvm/ADT/APFloat.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/PassManager.h"
#include "llvm/IR/Type.h"
#include "llvm/IR/Verifier.h"
#include "llvm/Passes/PassBuilder.h"
#include "llvm/Passes/StandardInstrumentations.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Transforms/InstCombine/InstCombine.h"
#include "llvm/Transforms/Scalar.h"
#include "llvm/Transforms/Scalar/GVN.h"
#include "llvm/Transforms/Scalar/Reassociate.h"
#include "llvm/Transforms/Scalar/SimplifyCFG.h"
namespace xo {
namespace jit {
/** @class Jit
* @brief just-in-time compiler for EGAD
*
* TODO: make module name a parameter?
**/
class Jit : public ref::Refcount {
public:
using Expression = xo::ast::Expression;
//using ConstantInterface = xo::ast::ConstantInterface;
public:
static ref::rp<Jit> make() { return new Jit(); }
llvm::Value * codegen_constant(ref::brw<xo::ast::ConstantInterface> expr);
llvm::Function * codegen_primitive(ref::brw<xo::ast::PrimitiveInterface> expr);
llvm::Value * codegen(ref::brw<Expression> expr);
private:
Jit();
private:
/** owns + manages core "global" llvm data,
* including type- and constant- unique-ing tables.
*
* Not threadsafe, but ok to have multiple threads,
* each with its own LLVMContext
**/
std::unique_ptr<llvm::LLVMContext> llvm_cx_;
/** a module (aka library) being prepared by llvm.
* - function names are unique within a module.
**/
std::unique_ptr<llvm::Module> llvm_module_;
};
} /*namespace jit*/
} /*namespace xo*/
/** end Jit.hpp **/

35
src/jit/CMakeLists.txt Normal file
View file

@ -0,0 +1,35 @@
# jit/CMakeLists.txt
set(SELF_LIB xo_jit)
set(SELF_SRCS
Jit.cpp
)
xo_add_shared_library4(${SELF_LIB} ${PROJECT_NAME}Targets ${PROJECT_VERSION} 1 ${SELF_SRCS})
xo_dependency(${SELF_LIB} xo_expression)
find_package(LLVM REQUIRED CONFIG)
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
message(STATUS "LLVM_DIR=${LLVM_DIR}")
message(STATUS "LLVM_DEFINITIONS=${LLVM_DEFINITIONS}")
message(STATUS "LLVM_INCLUDE_DIRS=[${LLVM_INCLUDE_DIRS}]")
separate_arguments(LLVM_DEFINITIONS_LIST NATIVE_COMMAND ${LLVM_DEFINITIONS})
message(STATUS "LLVM_DEFINITIONS_LIST=[${LLVM_DEFINITIONS_LIST}]")
# Find the libraries that correspond to the LLVM components
execute_process(
COMMAND llvm-config --libs all
COMMAND tr -d '\n'
OUTPUT_VARIABLE LLVM_LIBS
)
message(STATUS "LLVM_LIBS=[${LLVM_LIBS}]")
target_include_directories(${SELF_LIB} PUBLIC ${LLVM_INCLUDE_DIRS})
target_compile_definitions(${SELF_LIB} PUBLIC ${LLVM_DEFINITIONS_LIST})
target_link_libraries(${SELF_LIB} PUBLIC ${LLVM_LIBS})
# end CMakeLists.txt

100
src/jit/Jit.cpp Normal file
View file

@ -0,0 +1,100 @@
/* @file Jit.cpp */
#include "Jit.hpp"
namespace xo {
using xo::ast::exprtype;
using xo::ast::Expression;
using xo::ast::ConstantInterface;
using xo::ast::PrimitiveInterface;
using xo::reflect::TypeDescr;
namespace jit {
Jit::Jit()
: llvm_cx_{std::make_unique<llvm::LLVMContext>()},
llvm_module_{std::make_unique<llvm::Module>("xojit", *llvm_cx_)}
{}
llvm::Value *
Jit::codegen_constant(ref::brw<ConstantInterface> expr)
{
TypeDescr td = expr->value_td();
if (td->is_native<double>()) {
return llvm::ConstantFP::get(*llvm_cx_,
llvm::APFloat(*(expr->value_tp().recover_native<double>())));
} else if (td->is_native<float>()) {
return llvm::ConstantFP::get(*llvm_cx_,
llvm::APFloat(*(expr->value_tp().recover_native<float>())));
}
return nullptr;
}
llvm::Function *
Jit::codegen_primitive(ref::brw<PrimitiveInterface> expr)
{
/** note: documentation (such as it is) for llvm::Function here:
*
* https://llvm.org/doxygen/classllvm_1_1Function.html
**/
auto * fn = llvm_module_->getFunction(expr->name());
if (fn) {
/** function with this name already known to llvm module;
* use that definition
*
* TODO: verify that signatures match!
**/
return fn;
}
/** establish prototype for this function **/
// PLACEHOLDER
// just make prototype for function :: double -> double
std::vector<llvm::Type *> double_v(1, llvm::Type::getDoubleTy(*llvm_cx_));
auto * llvm_fn_type = llvm::FunctionType::get(llvm::Type::getDoubleTy(*llvm_cx_),
double_v,
false /*!varargs*/);
fn = llvm::Function::Create(llvm_fn_type,
llvm::Function::ExternalLinkage,
expr->name(),
llvm_module_.get());
// set names for arguments (for diagonostics?). Money-see-kaleidoscope-monkey-do here
#ifdef NOT_USING
for (auto & arg : fn->args())
arg.setName(formalnameofthisarg);
#endif
return fn;
} /*codegen_primitive*/
llvm::Value *
Jit::codegen(ref::brw<Expression> expr)
{
switch(expr->extype()) {
case exprtype::constant:
return this->codegen_constant(ConstantInterface::from(expr));
case exprtype::primitive:
return this->codegen_primitive(PrimitiveInterface::from(expr));
case exprtype::apply:
break;
case exprtype::invalid:
case exprtype::n_expr:
return nullptr;
break;
}
return nullptr;
} /*codegen*/
} /*namespace jit*/
} /*namespace xo*/
/* end Jit.cpp */