xo-jit: TypeDescr->llvm::Type conv for structs
This commit is contained in:
parent
d06f176c98
commit
be6d7c2aab
4 changed files with 102 additions and 4 deletions
|
|
@ -152,7 +152,6 @@ namespace xo {
|
||||||
|
|
||||||
// ----- this part adapted from kaleidoscope.cpp -----
|
// ----- this part adapted from kaleidoscope.cpp -----
|
||||||
|
|
||||||
public:
|
|
||||||
/** everything below represents a pipeline
|
/** everything below represents a pipeline
|
||||||
* that takes expressions, and turns them into llvm IR.
|
* that takes expressions, and turns them into llvm IR.
|
||||||
*
|
*
|
||||||
|
|
@ -162,7 +161,6 @@ namespace xo {
|
||||||
**/
|
**/
|
||||||
xo::ref::rp<IrPipeline> ir_pipeline_;
|
xo::ref::rp<IrPipeline> ir_pipeline_;
|
||||||
|
|
||||||
private:
|
|
||||||
/** owns + manages core "global" llvm data,
|
/** owns + manages core "global" llvm data,
|
||||||
* including type- and constant- unique-ing tables.
|
* including type- and constant- unique-ing tables.
|
||||||
*
|
*
|
||||||
|
|
@ -184,7 +182,6 @@ namespace xo {
|
||||||
/** map global names to functions/variables **/
|
/** map global names to functions/variables **/
|
||||||
std::map<std::string, xo::ref::rp<Expression>> global_env_;
|
std::map<std::string, xo::ref::rp<Expression>> global_env_;
|
||||||
|
|
||||||
public:
|
|
||||||
/** map variable names (formal parameters) to
|
/** map variable names (formal parameters) to
|
||||||
* corresponding llvm IR.
|
* corresponding llvm IR.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ namespace xo {
|
||||||
using xo::ast::IfExpr;
|
using xo::ast::IfExpr;
|
||||||
using xo::ast::llvmintrinsic;
|
using xo::ast::llvmintrinsic;
|
||||||
using xo::reflect::Reflect;
|
using xo::reflect::Reflect;
|
||||||
|
using xo::reflect::StructMember;
|
||||||
using xo::reflect::TypeDescr;
|
using xo::reflect::TypeDescr;
|
||||||
using std::cerr;
|
using std::cerr;
|
||||||
using std::endl;
|
using std::endl;
|
||||||
|
|
@ -142,6 +143,12 @@ namespace xo {
|
||||||
} /*codegen_constant*/
|
} /*codegen_constant*/
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
/** REMINDER:
|
||||||
|
* 1. creation of llvm types is idempotent
|
||||||
|
* (duplicate calls will receive the same llvm::Type* pointer)
|
||||||
|
* 2. llvm::Types are never deleted.
|
||||||
|
**/
|
||||||
|
|
||||||
llvm::Type *
|
llvm::Type *
|
||||||
td_to_llvm_type(xo::ref::brw<LlvmContext> llvm_cx, TypeDescr td);
|
td_to_llvm_type(xo::ref::brw<LlvmContext> llvm_cx, TypeDescr td);
|
||||||
|
|
||||||
|
|
@ -197,6 +204,73 @@ namespace xo {
|
||||||
return llvm_ptr_type;
|
return llvm_ptr_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate llvm::Type correspoinding to a TypeDescr for a struct.
|
||||||
|
**/
|
||||||
|
llvm::StructType *
|
||||||
|
struct_td_to_llvm_type(xo::ref::brw<LlvmContext> llvm_cx,
|
||||||
|
TypeDescr struct_td)
|
||||||
|
{
|
||||||
|
// see
|
||||||
|
// [[https://stackoverflow.com/questions/32299166/accessing-struct-members-and-arrays-of-structs-from-llvm-ir]]
|
||||||
|
|
||||||
|
auto & llvm_cx_ref = llvm_cx->llvm_cx_ref();
|
||||||
|
|
||||||
|
/* note: object pointer ignored for struct types,
|
||||||
|
* since number of members is known at compile time
|
||||||
|
*/
|
||||||
|
int n_member = struct_td->n_child(nullptr /*&object*/);
|
||||||
|
|
||||||
|
/* one type for each struct member */
|
||||||
|
std::vector<llvm::Type *> llvm_membertype_v;
|
||||||
|
llvm_membertype_v.reserve(n_member);
|
||||||
|
|
||||||
|
for (int i = 0; i < n_member; ++i) {
|
||||||
|
StructMember const & sm = struct_td->struct_member(i);
|
||||||
|
|
||||||
|
llvm_membertype_v.push_back(td_to_llvm_type(llvm_cx,
|
||||||
|
sm.get_member_td()));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string struct_name = std::string(struct_td->short_name());
|
||||||
|
|
||||||
|
/* structs with names: within an llvmcontext, must be unique
|
||||||
|
*
|
||||||
|
* If we don't set isPacked, then padding will be chosen based on DataLayout,
|
||||||
|
* which might C++ compiler's padding, but no guarantees.
|
||||||
|
*
|
||||||
|
* We can however compare the offsets recorded in xo::reflect with
|
||||||
|
* offsets chosen by llvm, *once we've created the llvm type*
|
||||||
|
*
|
||||||
|
* Also, we can't guarantee that a c++ type was completely reflected --
|
||||||
|
* it's possible one or more members were omitted, in which case
|
||||||
|
* it's unlikely at best that llvm chooses the same layout.
|
||||||
|
*
|
||||||
|
* Instead: tell llvm to make packed struct,
|
||||||
|
* and introduce dummy members for padding.
|
||||||
|
*
|
||||||
|
* A consequence is we have to maintain mapping between llvm's
|
||||||
|
* member numbering and xo::reflect's
|
||||||
|
*/
|
||||||
|
llvm::StructType * llvm_struct_type
|
||||||
|
= llvm::StructType::create(llvm_cx_ref,
|
||||||
|
llvm_membertype_v,
|
||||||
|
llvm::StringRef(struct_name),
|
||||||
|
true /*isPacked*/);
|
||||||
|
|
||||||
|
/* TODO: inspect (how) offsets that llvm is using
|
||||||
|
* we need them to match what C++ chose
|
||||||
|
*
|
||||||
|
* (because we want jitted llvm code to interoperate with
|
||||||
|
* C++ library code that has structs)
|
||||||
|
*/
|
||||||
|
|
||||||
|
// GetElementPtrInst is interesting,
|
||||||
|
// but I think that's for generating code
|
||||||
|
|
||||||
|
return llvm_struct_type;
|
||||||
|
} /*struct_td_to_llvm_type*/
|
||||||
|
|
||||||
llvm::Type *
|
llvm::Type *
|
||||||
td_to_llvm_type(xo::ref::brw<LlvmContext> llvm_cx, TypeDescr td) {
|
td_to_llvm_type(xo::ref::brw<LlvmContext> llvm_cx, TypeDescr td) {
|
||||||
auto & llvm_cx_ref = llvm_cx->llvm_cx_ref();
|
auto & llvm_cx_ref = llvm_cx->llvm_cx_ref();
|
||||||
|
|
@ -206,6 +280,8 @@ namespace xo {
|
||||||
* i.e. something that can be stored in a variable
|
* i.e. something that can be stored in a variable
|
||||||
*/
|
*/
|
||||||
return function_td_to_llvm_fnptr_type(llvm_cx, td);
|
return function_td_to_llvm_fnptr_type(llvm_cx, td);
|
||||||
|
} else if (td->is_struct()) {
|
||||||
|
return struct_td_to_llvm_type(llvm_cx, td);
|
||||||
} else if (Reflect::is_native<bool>(td)) {
|
} else if (Reflect::is_native<bool>(td)) {
|
||||||
return llvm::Type::getInt1Ty(llvm_cx_ref);
|
return llvm::Type::getInt1Ty(llvm_cx_ref);
|
||||||
} else if (Reflect::is_native<char>(td)) {
|
} else if (Reflect::is_native<char>(td)) {
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,8 @@ set(SELF_SRCS
|
||||||
if (ENABLE_TESTING)
|
if (ENABLE_TESTING)
|
||||||
xo_add_utest_executable(${SELF_EXE} ${SELF_SRCS})
|
xo_add_utest_executable(${SELF_EXE} ${SELF_SRCS})
|
||||||
xo_self_dependency(${SELF_EXE} xo_jit)
|
xo_self_dependency(${SELF_EXE} xo_jit)
|
||||||
|
xo_dependency(${SELF_EXE} xo_ratio)
|
||||||
|
xo_dependency(${SELF_EXE} xo_reflectutil)
|
||||||
xo_external_target_dependency(${SELF_EXE} Catch2 Catch2::Catch2)
|
xo_external_target_dependency(${SELF_EXE} Catch2 Catch2::Catch2)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
#include "xo/jit/MachPipeline.hpp"
|
#include "xo/jit/MachPipeline.hpp"
|
||||||
#include "xo/expression/Primitive.hpp"
|
#include "xo/expression/Primitive.hpp"
|
||||||
|
#include "xo/ratio/ratio.hpp"
|
||||||
|
#include "xo/reflect/reflect_struct.hpp"
|
||||||
#include "xo/indentlog/scope.hpp"
|
#include "xo/indentlog/scope.hpp"
|
||||||
#include <catch2/catch.hpp>
|
#include <catch2/catch.hpp>
|
||||||
|
|
||||||
|
|
@ -15,6 +17,7 @@ namespace xo {
|
||||||
using xo::ast::Lambda;
|
using xo::ast::Lambda;
|
||||||
using xo::ast::exprtype;
|
using xo::ast::exprtype;
|
||||||
using xo::reflect::Reflect;
|
using xo::reflect::Reflect;
|
||||||
|
using xo::reflect::reflect_struct;
|
||||||
using xo::ref::rp;
|
using xo::ref::rp;
|
||||||
using xo::ref::brw;
|
using xo::ref::brw;
|
||||||
using std::cerr;
|
using std::cerr;
|
||||||
|
|
@ -179,8 +182,28 @@ namespace xo {
|
||||||
REQUIRE(actual == expected);
|
REQUIRE(actual == expected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} /*TEST_CASE(machpipeline)*/
|
} /*TEST_CASE(machpipeline)*/
|
||||||
|
|
||||||
|
TEST_CASE("machpipeline.struct", "[llvm][llvm_struct]") {
|
||||||
|
constexpr bool c_debug_flag = false;
|
||||||
|
|
||||||
|
// can get bits from /dev/random by uncommenting the 2nd line below
|
||||||
|
//uint64_t seed = xxx;
|
||||||
|
//rng::Seed<xoshio256ss> seed;
|
||||||
|
|
||||||
|
//auto rng = xo::rng::xoshiro256ss(seed);
|
||||||
|
|
||||||
|
scope log(XO_DEBUG2(c_debug_flag, "TEST_CASE.machpipeline.struct"));
|
||||||
|
//log && log("(A)", xtag("foo", foo));
|
||||||
|
|
||||||
|
auto jit = MachPipeline::make();
|
||||||
|
|
||||||
|
/* let's reflect xo::ratio::ratio<int> */
|
||||||
|
|
||||||
|
auto struct_td = reflect_struct<xo::ratio::ratio<int>>();
|
||||||
|
|
||||||
|
REQUIRE(struct_td);
|
||||||
|
} /*TEST_CASE(machpipeline.struct)*/
|
||||||
} /*namespace ut*/
|
} /*namespace ut*/
|
||||||
} /*namespace xo*/
|
} /*namespace xo*/
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue