diff --git a/src/runtime/object.cpp b/src/runtime/object.cpp index c2c695a161..8f31791be9 100644 --- a/src/runtime/object.cpp +++ b/src/runtime/object.cpp @@ -777,10 +777,68 @@ static task_object * alloc_task(obj_arg v) { return new (alloc_heap_object(sizeof(task_object))) task_object(v); // NOLINT } +void to_mt(object * o); +static obj_res to_mt_fn(obj_arg o) { + to_mt(o); + dec(o); + return box(0); +} + +void to_mt(object * o) { + if (is_scalar(o) || !is_st_heap_obj(o)) return; + o->m_mem_kind = static_cast(object_memory_kind::MTHeap); + + switch (get_kind(o)) { + case object_kind::ScalarArray: + case object_kind::String: + case object_kind::MPZ: + return; + case object_kind::PArrayPop: + case object_kind::PArrayPush: + case object_kind::PArraySet: + case object_kind::PArrayRoot: + /* `to_mt` cannot be used with parray. They must be copied when used in multiple threads. */ + lean_unreachable(); + return; + case object_kind::External: { + object * fn = alloc_closure(reinterpret_cast(to_mt_fn), 1, 0); + to_external(o)->for_each_nested(fn); + dec(fn); + return; + } + case object_kind::Task: + to_mt(task_get(o)); + return; + case object_kind::Constructor: { + object ** it = cnstr_obj_cptr(o); + object ** end = it + cnstr_num_objs(o); + for (; it != end; ++it) to_mt(*it); + break; + } + case object_kind::Closure: { + object ** it = closure_arg_cptr(o); + object ** end = it + closure_num_fixed(o); + for (; it != end; ++it) to_mt(*it); + break; + } + case object_kind::Array: { + object ** it = array_cptr(o); + object ** end = it + array_size(o); + for (; it != end; ++it) to_mt(*it); + break; + } + case object_kind::Thunk: + if (object * c = to_thunk(o)->m_closure) to_mt(c); + if (object * v = to_thunk(o)->m_value) to_mt(v); + break; + } +} + obj_res mk_task(obj_arg c, unsigned prio) { if (!g_task_manager) { return mk_thunk(c); } else { + to_mt(c); task_object * new_task = alloc_task(c, prio); g_task_manager->enqueue(new_task); return new_task; diff --git a/src/runtime/object.h b/src/runtime/object.h index 84b025db7d..2974639864 100644 --- a/src/runtime/object.h +++ b/src/runtime/object.h @@ -77,6 +77,38 @@ struct constructor_object : public object { object(object_kind::Constructor, m), m_tag(tag), m_num_objs(num_objs), m_scalar_size(scalar_sz) {} }; +/* +In our runtime, a Lean function consume the reference counter (RC) of its argument or not. +We say this behavior is part of the "calling convention" for the function. We say an argument uses: + +1- "standard" calling convention if it consumes/decrements the RC. + In this calling convention each argument should be viewed as a resource that is consumed by the function. + This is roughly equivalent to `S && a` in C++, where `S` is a smart pointer, and `a` is the argument. + When this calling convention is used for an argument `x`, then it is safe to perform destructive updates to + `x` if its RC is 1. + +2- "borrowed" calling convention if it doesn't consume/decrement the RC, and it is the responsability of the caller + to decrement the RC. + This is roughly equivalent to `S const & a` in C++, where `S` is a smart pointer, and `a` is the argument. + +For returning objects, we also have two conventions + +1- "standard" result. The caller is responsible for consuming the RC of the result. + This is roughly equivalent to returning a smart point `S` by value in C++. + +2- "borrowed" result. The caller is not responsible for decreasing the RC. + This is roughly equivalent to returning a smart point reference `S const &` in C++. + +Functions stored in closures use the "standard" calling convention. +*/ + +/* The following typedef's are used to document the calling convention for the primitives. */ +typedef object * obj_arg; /* Standard object argument. */ +typedef object * b_obj_arg; /* Borrowed object argument. */ +typedef object * u_obj_arg; /* Unique (aka non shared) object argument. */ +typedef object * obj_res; /* Standard object result. */ +typedef object * b_obj_res; /* Borrowed object result. */ + /* Array of objects. Header size: 16 bytes in 32 bit machines and 32 bytes in 64 bit machines. */ struct array_object : public object { @@ -175,8 +207,11 @@ struct task_object : public object { For example, we use it to wrap the Lean environment object. */ struct external_object : public object { explicit external_object(object_memory_kind m = c_init_mem_kind): object(object_kind::External, m) {} - virtual void dealloc() {} virtual ~external_object() {} + virtual void dealloc() {} + /* For each nested object `a`, execute `apply_1(fn, a)`. + Recall that apply_1 consumes the RC of `fn` and `a`. */ + virtual void for_each_nested(b_obj_arg /* fn */) {} }; inline bool is_null(object * o) { return o == nullptr; } @@ -473,38 +508,6 @@ bool int_big_eq(object * a1, object * a2); bool int_big_le(object * a1, object * a2); bool int_big_lt(object * a1, object * a2); -/* -In our runtime, a Lean function consume the reference counter (RC) of its argument or not. -We say this behavior is part of the "calling convention" for the function. We say an argument uses: - -1- "standard" calling convention if it consumes/decrements the RC. - In this calling convention each argument should be viewed as a resource that is consumed by the function. - This is roughly equivalent to `S && a` in C++, where `S` is a smart pointer, and `a` is the argument. - When this calling convention is used for an argument `x`, then it is safe to perform destructive updates to - `x` if its RC is 1. - -2- "borrowed" calling convention if it doesn't consume/decrement the RC, and it is the responsability of the caller - to decrement the RC. - This is roughly equivalent to `S const & a` in C++, where `S` is a smart pointer, and `a` is the argument. - -For returning objects, we also have two conventions - -1- "standard" result. The caller is responsible for consuming the RC of the result. - This is roughly equivalent to returning a smart point `S` by value in C++. - -2- "borrowed" result. The caller is not responsible for decreasing the RC. - This is roughly equivalent to returning a smart point reference `S const &` in C++. - -Functions stored in closures use the "standard" calling convention. -*/ - -/* The following typedef's are used to document the calling convention for the primitives. */ -typedef object * obj_arg; /* Standard object argument. */ -typedef object * b_obj_arg; /* Borrowed object argument. */ -typedef object * u_obj_arg; /* Unique (aka non shared) object argument. */ -typedef object * obj_res; /* Standard object result. */ -typedef object * b_obj_res; /* Borrowed object result. */ - // ======================================= // Constructor objects inline obj_res alloc_cnstr(unsigned tag, unsigned num_objs, unsigned scalar_sz) {