Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add structs to FFI #47

Draft
wants to merge 11 commits into
base: feature/vm-dynload
Choose a base branch
from
2 changes: 1 addition & 1 deletion src/frontends/lean/decl_attributes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ bool decl_attributes::ok_for_inductive_type() const {
for (entry const & e : m_entries) {
name const & n = e.m_attr->get_name();
if (is_system_attribute(n)) {
if ((n != "class" && !is_class_symbol_tracking_attribute(n)) || e.deleted())
if ((n != "class" && n != "ffi" && !is_class_symbol_tracking_attribute(n)) || e.deleted())
return false;
}
}
Expand Down
19 changes: 19 additions & 0 deletions src/frontends/lean/structure_cmd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,25 @@ buffer<name> get_structure_fields(environment const & env, name const & S) {
return fields;
}

buffer<std::tuple<name,expr>> get_structure_field_types(environment const & env, name const & S) {
lean_assert(is_structure_like(env, S));
buffer<std::tuple<name,expr>> fields;
level_param_names ls; unsigned nparams; inductive::intro_rule intro;
std::tie(ls, nparams, intro) = get_structure_info(env, S);
expr intro_type = inductive::intro_rule_type(intro);
unsigned i = 0;
while (is_pi(intro_type)) {
if (i >= nparams)
fields.push_back(std::make_tuple(deinternalize_field_name(binding_name(intro_type)),
binding_domain(intro_type)));
i++;
auto local = mk_local(mk_fresh_name(), binding_name(intro_type),
binding_domain(intro_type), binding_info(intro_type));
intro_type = instantiate(binding_body(intro_type), local);
}
return fields;
}

bool is_structure(environment const & env, name const & S) {
if (!is_structure_like(env, S))
return false;
Expand Down
2 changes: 2 additions & 0 deletions src/frontends/lean/structure_cmd.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ namespace lean {
environment structure_cmd(parser & p, cmd_meta const & meta);
environment class_cmd(parser & p, cmd_meta const & meta);
buffer<name> get_structure_fields(environment const & env, name const & S);
buffer<std::tuple<name,expr>> get_structure_field_types(environment const & env, name const & S);

void register_structure_cmd(cmd_table & r);
/** \brief Return true iff \c S is a structure created with the structure command */
bool is_structure(environment const & env, name const & S);
Expand Down
67 changes: 62 additions & 5 deletions src/library/vm/vm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ Author: Leonardo de Moura
#include "util/small_object_allocator.h"
#include "util/sexpr/option_declarations.h"
#include "util/shared_mutex.h"
#include "frontends/lean/structure_cmd.h"
#include "library/constants.h"
#include "library/kernel_serializer.h"
#include "library/trace.h"
Expand Down Expand Up @@ -1047,8 +1048,8 @@ vm_decl_cell::vm_decl_cell(name const & n, unsigned idx, unsigned arity, vm_cfun
m_rc(0), m_kind(vm_decl_kind::CFun), m_name(n), m_idx(idx), m_arity(arity), m_cfn(fn) {}

vm_decl_cell::vm_decl_cell(name const & n, unsigned idx,
unique_ptr<vm_cfun_sig> sig, vm_cfunction fn):
m_rc(0), m_kind(vm_decl_kind::CFun), m_name(n), m_idx(idx), m_arity(sig->arity()), m_sig(std::move(sig)), m_cfn(fn) {}
unique_ptr<vm_ffi_call> sig, unsigned arity, vm_cfunction fn):
m_rc(0), m_kind(vm_decl_kind::CFun), m_name(n), m_idx(idx), m_arity(arity), m_sig(std::move(sig)), m_cfn(fn) {}

vm_decl_cell::vm_decl_cell(name const & n, unsigned idx, unsigned arity, unsigned code_sz, vm_instr const * code,
list<vm_local_info> const & args_info, optional<pos_info> const & pos,
Expand Down Expand Up @@ -1145,11 +1146,17 @@ void declare_vm_cases_builtin(name const & n, char const * i, vm_cases_function
g_vm_cases_builtins->insert(n, std::make_tuple(i, fn));
}

struct vm_ffi_struct {
ffi_type m_struct;
buffer<ffi_type *> m_fields;
};

/** \brief VM function/constant declarations are stored in an environment extension. */
struct vm_decls : public environment_extension {
unsigned_map<vm_decl> m_decls;
unsigned_map<vm_cases_function> m_cases;
std::unordered_map<name, vm_foreign_obj, name_hash> m_foreign;
std::unordered_map<name, std::shared_ptr<vm_ffi_struct>, name_hash> m_native_types;

name m_monitor;

Expand Down Expand Up @@ -1187,6 +1194,11 @@ struct vm_decls : public environment_extension {
m_decls.insert(idx, m_foreign[obj].get_cfun(decl, idx, c_fun, args, t));
}

void bind_foreign_constructor(name const & decl) {
auto idx = get_vm_index(decl);
m_decls.insert(idx, _);
}

unsigned reserve(name const & n, unsigned arity) {
unsigned idx = get_vm_index(n);
if (m_decls.contains(idx))
Expand Down Expand Up @@ -1285,6 +1297,41 @@ environment add_foreign_symbol(environment const & env, name const & obj, name c
return update(env, ext);
}

environment add_foreign_struct(environment const & env, name const & n) {
auto ext = get_extension(env);
buffer<std::tuple<name,expr>> struct_fields = get_structure_field_types(env, n);
std::shared_ptr<vm_ffi_struct> m_struct = std::make_shared<vm_ffi_struct>();

ffi_type & tm_type = m_struct->m_struct;
buffer<ffi_type *> & tm_elements = m_struct->m_fields;

tm_type.size = tm_type.alignment = 0;
tm_type.type = FFI_TYPE_STRUCT;

// field_typ
name field; expr ftype;
for (auto & fn : struct_fields) {
std::tie(field, ftype) = fn;
// declaration decl = env.get({n, fn});
// expr t = decl.get_type();
// my_struct -> int_32
if (!is_constant(ftype))
throw exception("Only constant expressions are allowed in struct fields");
auto n = const_name(ftype);
tm_elements.push_back( &ext.m_native_types[n]->m_struct );
}
tm_elements.push_back( NULL );

tm_type.elements = m_struct->m_fields.data();

lean_assert(ext.m_native_types.find(n) == ext.m_native_types.end());

ext.m_native_types[n] = m_struct;
// ext.

return update(env, ext);
}

bool is_vm_function(environment const & env, name const & fn) {
auto const & ext = get_extension(env);
return ext.m_decls.contains(get_vm_index(fn)) || g_vm_builtins->contains(fn);
Expand Down Expand Up @@ -2946,9 +2993,19 @@ void vm_state::invoke_cfun(vm_decl const & d) {
unique_lock<mutex> lk(m_call_stack_mtx);
push_frame_core(0, 0, d.get_idx());
}
if (d.is_ffi())
invoke_ffi_call(d.get_cfn(), d.get_sig());
else
if (d.is_ffi()) {
switch (d.get_sig().m_kind) {
case vm_ffi_call_kind::Ctor:
throw exception("not implemented");
case vm_ffi_call_kind::Cases:
throw exception("not implemented");
case vm_ffi_call_kind::CFun:
throw exception("not implemented");
case vm_ffi_call_kind::Field:
throw exception("not implemented");
}
// invoke_ffi_call(d.get_cfn(), d.get_sig());
} else
invoke_fn(d.get_cfn(), d.get_arity());
if (m_profiling) {
unique_lock<mutex> lk(m_call_stack_mtx);
Expand Down
26 changes: 20 additions & 6 deletions src/library/vm/vm.h
Original file line number Diff line number Diff line change
Expand Up @@ -537,7 +537,20 @@ vm_instr mk_local_info_instr(unsigned idx, name const & n, optional<expr> const
class vm_state;
class vm_instr;

struct vm_cfun_sig {
enum class vm_ffi_call_kind { Ctor, Cases, CFun, Field };

struct vm_ffi_call {
vm_ffi_call_kind m_kind;
};

/* used for implementing constructor, eliminator and projections of ffi structures */
struct vm_cstruct_fields : vm_ffi_call {
ffi_type m_struct;
buffer<ffi_type*> m_fields;
buffer<ffi_type> m_alloc;
};

struct vm_cfun_sig : vm_ffi_call {
buffer<ffi_type *> m_args;
ffi_type *m_rtype;
ffi_cif m_cif;
Expand All @@ -558,7 +571,7 @@ struct vm_decl_cell {
list<vm_local_info> m_args_info;
optional<pos_info> m_pos;
optional<std::string> m_olean;
unique_ptr<vm_cfun_sig> m_sig;
unique_ptr<vm_ffi_call> m_sig;
union {
struct {
unsigned m_code_size;
Expand All @@ -569,7 +582,7 @@ struct vm_decl_cell {
};
vm_decl_cell(name const & n, unsigned idx, unsigned arity, vm_function fn);
vm_decl_cell(name const & n, unsigned idx, unsigned arity, vm_cfunction fn);
vm_decl_cell(name const & n, unsigned idx, unique_ptr<vm_cfun_sig> sig, vm_cfunction fn);
vm_decl_cell(name const & n, unsigned idx, unique_ptr<vm_ffi_call> sig, unsigned arity, vm_cfunction fn);
vm_decl_cell(name const & n, unsigned idx, unsigned arity, unsigned code_sz, vm_instr const * code,
list<vm_local_info> const & args_info, optional<pos_info> const & pos,
optional<std::string> const & olean);
Expand All @@ -585,8 +598,8 @@ class vm_decl {
vm_decl():m_ptr(nullptr) {}
vm_decl(name const & n, unsigned idx, unsigned arity, vm_function fn):
vm_decl(new vm_decl_cell(n, idx, arity, fn)) {}
vm_decl(name const & n, unsigned idx, unique_ptr<vm_cfun_sig> sig, vm_cfunction fn):
vm_decl(new vm_decl_cell(n, idx, std::move(sig), fn)) {}
vm_decl(name const & n, unsigned idx, unique_ptr<vm_ffi_call> sig, unsigned arity, vm_cfunction fn):
vm_decl(new vm_decl_cell(n, idx, std::move(sig), arity, fn)) {}
vm_decl(name const & n, unsigned idx, unsigned arity, vm_cfunction fn):
vm_decl(new vm_decl_cell(n, idx, arity, fn)) {}
vm_decl(name const & n, unsigned idx, unsigned arity, unsigned code_sz, vm_instr const * code,
Expand All @@ -611,7 +624,7 @@ class vm_decl {
bool is_ffi() const { lean_assert(m_ptr); return is_cfun() && m_ptr->m_sig.get() != nullptr; }
unsigned get_idx() const { lean_assert(m_ptr); return m_ptr->m_idx; }
name get_name() const { lean_assert(m_ptr); return m_ptr->m_name; }
vm_cfun_sig const & get_sig() const { lean_assert(m_ptr && is_ffi()); return *m_ptr->m_sig; }
vm_ffi_call const & get_sig() const { lean_assert(m_ptr && is_ffi()); return *m_ptr->m_sig; }
unsigned get_arity() const { lean_assert(m_ptr); return m_ptr->m_arity; }
unsigned get_code_size() const { lean_assert(is_bytecode()); return m_ptr->m_code_size; }
vm_instr const * get_code() const { lean_assert(is_bytecode()); return m_ptr->m_code; }
Expand Down Expand Up @@ -929,6 +942,7 @@ environment add_native(environment const & env, name const & n, unsigned arity,
environment load_foreign_object(environment const & env, name const & n, std::string const & file_name);
environment add_foreign_symbol(environment const & env, name const & obj, name const & fn,
std::string const & symbol);
environment add_foreign_struct(environment const & env, name const & n);

unsigned get_vm_index(name const & n);
unsigned get_vm_index_bound();
Expand Down
29 changes: 23 additions & 6 deletions src/library/vm/vm_ffi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Author: James King <[email protected]>, Simon Hudon
#include "util/lean_path.h"
#include "library/vm/vm_ffi.h"
#include "library/vm/vm_parser.h"
#include "frontends/lean/structure_cmd.h"

namespace lean {

Expand Down Expand Up @@ -67,7 +68,13 @@ namespace lean {
for (auto e : _args) { args.push_back(to_ffi_type(e)); }
ffi_type * rt = to_ffi_type(_rt);
unique_ptr<vm_cfun_sig> sig(new vm_cfun_sig(FFI_DEFAULT_ABI, *rt, std::move(args)));
return vm_decl(n, idx, std::move(sig), fn);
return vm_decl(n, idx, std::move(sig), args.size(), fn);
}

vm_decl vm_foreign_obj_cell::mk_ctor(name const & n, unsigned idx, string const & fun_name,
buffer<expr> const & _args, expr const & _rt) {

return vm_decl( );
}

vm_foreign_obj_cell::~vm_foreign_obj_cell() {
Expand All @@ -89,6 +96,12 @@ namespace lean {
m_ptr->inc_ref();
}

vm_foreign_value * vm_foreign_value::make(size_t size, void * data) {
auto result = new (new char[ sizeof(vm_foreign_value) + size ]) vm_foreign_value(size);
memcpy(result->data(), data, size);
return result;
}

struct vm_ffi_attribute_data : public attr_data {
name m_obj;
optional<name> m_c_fun;
Expand Down Expand Up @@ -137,12 +150,16 @@ namespace lean {
g_int64_name = new name ({"foreign", "int_64"});

register_system_attribute(vm_ffi_attribute(
*g_vm_ffi, "Registers a binding to a foreign function.",
*g_vm_ffi, "Registers a binding to a foreign function or structure.",
[](environment const & env, io_state const &, name const & d, unsigned, bool) -> environment {
auto data = get_vm_ffi_attribute().get(env, d);
name sym = data->m_c_fun? *data->m_c_fun : d;
auto b = add_foreign_symbol(env, data->m_obj, d, sym.to_string());
return b;
if (is_structure(env, d)) {
// auto struct_fields = get_structure_fields(env, d);
return add_foreign_struct(env, d);
} else {
auto ffi_attr = get_vm_ffi_attribute().get(env, d);
name sym = ffi_attr->m_c_fun? *ffi_attr->m_c_fun : d;
return add_foreign_symbol(env, ffi_attr->m_obj, d, sym.to_string());
}
}));
}

Expand Down
22 changes: 19 additions & 3 deletions src/library/vm/vm_ffi.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,25 @@ namespace lean {
~vm_foreign_obj() { if (m_ptr) { m_ptr->dec_ref(); } }
};

void initialize_vm_ffi();

void finalize_vm_ffi();
class vm_foreign_value : public vm_external {
private:
size_t m_size;
vm_foreign_value(unsigned size) : m_size(size) { }
virtual void dealloc() override {
delete [] reinterpret_cast<char *>(this); }
public:
static vm_foreign_value * make(size_t size, void * data);
template <typename T>
static vm_foreign_value * make(T const & x) { return make(sizeof(T), &x); }
void * data() { return &m_size + 1; }
size_t size() const { return m_size; }
virtual vm_external * ts_clone(vm_clone_fn const &) override {
return new (new char[ sizeof(vm_foreign_value) + m_size ]) vm_foreign_value(m_size);
}
virtual vm_external * clone(vm_clone_fn const &) override {
return new (get_vm_allocator().allocate(sizeof(vm_foreign_value) + m_size)) vm_foreign_value(m_size);
}
};

void initialize_vm_ffi();

Expand Down
4 changes: 4 additions & 0 deletions tests/lean/ffi.lean
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,9 @@ run_cmd trace "\nnext!\n"
@[ffi foo]
constant my_fun : uint_32 → int_64 → int_64

@[ffi foo]
structure my_basic_struct :=
(count : uint_32) (result : int_64)


#eval my_fun 7 3
4 changes: 4 additions & 0 deletions tests/lean/vm_dynload/some_file.c
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
struct my_basic_struct {
int count;
long result;
} my_struct;

long my_fun (int x, long y) {
return x * y;
Expand Down