| // Functions that interpret the shape of a type to perform various low-level |
| // actions, such as copying, freeing, comparing, and so on. |
| |
| #include <algorithm> |
| #include <iomanip> |
| #include <iostream> |
| #include <sstream> |
| #include <utility> |
| #include <cassert> |
| #include <cctype> |
| #include <cstdio> |
| #include <cstdlib> |
| #include "rust_internal.h" |
| #include "rust_shape.h" |
| |
| namespace shape { |
| |
| using namespace shape; |
| |
| // Constants |
| |
| const uint8_t CMP_EQ = 0u; |
| const uint8_t CMP_LT = 1u; |
| const uint8_t CMP_LE = 2u; |
| |
| // Type parameters |
| |
| type_param * |
| type_param::make(const type_desc **tydescs, unsigned n_tydescs, |
| arena &arena) { |
| if (!n_tydescs) |
| return NULL; |
| |
| type_param *ptrs = arena.alloc<type_param>(n_tydescs); |
| for (uint32_t i = 0; i < n_tydescs; i++) { |
| const type_desc *subtydesc = tydescs[i]; |
| ptrs[i].shape = subtydesc->shape; |
| ptrs[i].tables = subtydesc->shape_tables; |
| |
| // FIXME: Doesn't handle a type-parametric object closing over a |
| // type-parametric object type properly. |
| ptrs[i].params = from_tydesc(subtydesc, arena); |
| } |
| return ptrs; |
| } |
| |
| // Constructs type parameters from an object shape. This is a bit messy, |
| // because it requires that the object shape have a specific format. |
| type_param * |
| type_param::from_obj_shape(const uint8_t *sp, ptr dp, arena &arena) { |
| uint8_t shape = *sp++; assert(shape == SHAPE_STRUCT); |
| get_u16_bump(sp); // Skip over the size. |
| shape = *sp++; assert(shape == SHAPE_PTR); |
| shape = *sp++; assert(shape == SHAPE_STRUCT); |
| |
| unsigned n_tydescs = get_u16_bump(sp); |
| |
| // Type descriptors start right after the reference count. |
| const type_desc **descs = (const type_desc **)(dp + sizeof(uintptr_t)); |
| |
| return make(descs, n_tydescs, arena); |
| } |
| |
| |
| // A shape printer, useful for debugging |
| |
| void |
| print::walk_tag1(tag_info &tinfo) { |
| DPRINT("tag%u", tinfo.tag_id); |
| if (!tinfo.n_params) |
| return; |
| |
| DPRINT("<"); |
| |
| bool first = true; |
| for (uint16_t i = 0; i < tinfo.n_params; i++) { |
| if (!first) |
| DPRINT(","); |
| first = false; |
| |
| ctxt<print> sub(*this, tinfo.params[i].shape); |
| sub.walk(); |
| } |
| |
| DPRINT(">"); |
| } |
| |
| void |
| print::walk_struct1(const uint8_t *end_sp) { |
| DPRINT("("); |
| |
| bool first = true; |
| while (sp != end_sp) { |
| if (!first) |
| DPRINT(","); |
| first = false; |
| |
| walk(); |
| } |
| |
| DPRINT(")"); |
| } |
| |
| void |
| print::walk_res1(const rust_fn *dtor, unsigned n_params, |
| const type_param *params, const uint8_t *end_sp) { |
| DPRINT("res@%p", dtor); |
| |
| // Print type parameters. |
| if (n_params) { |
| DPRINT("<"); |
| |
| bool first = true; |
| for (uint16_t i = 0; i < n_params; i++) { |
| if (!first) |
| DPRINT(","); |
| first = false; |
| |
| ctxt<print> sub(*this, params[i].shape); |
| sub.walk(); |
| } |
| |
| DPRINT(">"); |
| } |
| |
| // Print arguments. |
| |
| if (sp == end_sp) |
| return; |
| |
| DPRINT("("); |
| |
| bool first = true; |
| while (sp != end_sp) { |
| if (!first) |
| DPRINT(","); |
| first = false; |
| |
| walk(); |
| } |
| |
| DPRINT(")"); |
| } |
| |
| void |
| print::walk_var1(uint8_t param_index) { |
| DPRINT("%c=", 'T' + param_index); |
| |
| const type_param *param = ¶ms[param_index]; |
| print sub(*this, param->shape, param->params, param->tables); |
| sub.walk(); |
| } |
| |
| template<> |
| void print::walk_number1<uint8_t>() { DPRINT("u8"); } |
| template<> |
| void print::walk_number1<uint16_t>() { DPRINT("u16"); } |
| template<> |
| void print::walk_number1<uint32_t>() { DPRINT("u32"); } |
| template<> |
| void print::walk_number1<uint64_t>() { DPRINT("u64"); } |
| template<> |
| void print::walk_number1<int8_t>() { DPRINT("i8"); } |
| template<> |
| void print::walk_number1<int16_t>() { DPRINT("i16"); } |
| template<> |
| void print::walk_number1<int32_t>() { DPRINT("i32"); } |
| template<> |
| void print::walk_number1<int64_t>() { DPRINT("i64"); } |
| template<> |
| void print::walk_number1<float>() { DPRINT("f32"); } |
| template<> |
| void print::walk_number1<double>() { DPRINT("f64"); } |
| |
| |
| void |
| size_of::compute_tag_size(tag_info &tinfo) { |
| // If the precalculated size and alignment are good, use them. |
| if (tinfo.tag_sa.is_set()) |
| return; |
| |
| uint16_t n_largest_variants = get_u16_bump(tinfo.largest_variants_ptr); |
| tinfo.tag_sa.set(0, 0); |
| for (uint16_t i = 0; i < n_largest_variants; i++) { |
| uint16_t variant_id = get_u16_bump(tinfo.largest_variants_ptr); |
| std::pair<const uint8_t *,const uint8_t *> variant_ptr_and_end = |
| get_variant_sp(tinfo, variant_id); |
| const uint8_t *variant_ptr = variant_ptr_and_end.first; |
| const uint8_t *variant_end = variant_ptr_and_end.second; |
| |
| size_of sub(*this, variant_ptr, tinfo.params, NULL); |
| sub.align = false; |
| |
| // Compute the size of this variant. |
| size_align variant_sa; |
| bool first = true; |
| while (sub.sp != variant_end) { |
| if (!first) |
| variant_sa.size = align_to(variant_sa.size, sub.sa.alignment); |
| sub.walk(); |
| sub.align = true, first = false; |
| |
| variant_sa.add(sub.sa.size, sub.sa.alignment); |
| } |
| |
| if (tinfo.tag_sa.size < variant_sa.size) |
| tinfo.tag_sa = variant_sa; |
| } |
| |
| if (tinfo.variant_count == 1) { |
| if (!tinfo.tag_sa.size) |
| tinfo.tag_sa.set(1, 1); |
| } else { |
| // Add in space for the tag. |
| tinfo.tag_sa.add(sizeof(tag_variant_t), alignof<tag_align_t>()); |
| } |
| } |
| |
| void |
| size_of::walk_tag1(tag_info &tinfo) { |
| compute_tag_size(*this, tinfo); |
| sa = tinfo.tag_sa; |
| } |
| |
| void |
| size_of::walk_struct1(const uint8_t *end_sp) { |
| size_align struct_sa(0, 1); |
| |
| bool first = true; |
| while (sp != end_sp) { |
| if (!first) |
| struct_sa.size = align_to(struct_sa.size, sa.alignment); |
| walk(); |
| align = true, first = false; |
| |
| struct_sa.add(sa); |
| } |
| |
| sa = struct_sa; |
| } |
| |
| // Copy constructors |
| |
| #if 0 |
| |
| class copy : public data<copy,uint8_t *> { |
| // TODO |
| }; |
| |
| #endif |
| |
| |
| // Structural comparison glue. |
| |
| class cmp : public data<cmp,ptr_pair> { |
| friend class data<cmp,ptr_pair>; |
| |
| private: |
| void walk_vec2(bool is_pod, |
| const std::pair<ptr_pair,ptr_pair> &data_range); |
| |
| inline void walk_subcontext2(cmp &sub) { |
| sub.walk(); |
| result = sub.result; |
| } |
| |
| inline void walk_box_contents2(cmp &sub, ptr_pair &ref_count_dp) { |
| sub.align = true; |
| sub.walk(); |
| result = sub.result; |
| } |
| |
| inline void walk_uniq_contents2(cmp &sub) { |
| sub.align = true; |
| sub.walk(); |
| result = sub.result; |
| } |
| |
| inline void cmp_two_pointers() { |
| ALIGN_TO(alignof<void *>()); |
| data_pair<uint8_t *> fst = bump_dp<uint8_t *>(dp); |
| data_pair<uint8_t *> snd = bump_dp<uint8_t *>(dp); |
| cmp_number(fst); |
| if (!result) |
| cmp_number(snd); |
| } |
| |
| inline void cmp_pointer() { |
| ALIGN_TO(alignof<void *>()); |
| cmp_number(bump_dp<uint8_t *>(dp)); |
| } |
| |
| template<typename T> |
| void cmp_number(const data_pair<T> &nums) { |
| result = (nums.fst < nums.snd) ? -1 : (nums.fst == nums.snd) ? 0 : 1; |
| } |
| |
| public: |
| int result; |
| |
| cmp(rust_task *in_task, |
| bool in_align, |
| const uint8_t *in_sp, |
| const type_param *in_params, |
| const rust_shape_tables *in_tables, |
| uint8_t *in_data_0, |
| uint8_t *in_data_1) |
| : data<cmp,ptr_pair>(in_task, in_align, in_sp, in_params, in_tables, |
| ptr_pair::make(in_data_0, in_data_1)), |
| result(0) {} |
| |
| cmp(const cmp &other, |
| const uint8_t *in_sp = NULL, |
| const type_param *in_params = NULL, |
| const rust_shape_tables *in_tables = NULL) |
| : data<cmp,ptr_pair>(other.task, |
| other.align, |
| in_sp ? in_sp : other.sp, |
| in_params ? in_params : other.params, |
| in_tables ? in_tables : other.tables, |
| other.dp), |
| result(0) {} |
| |
| cmp(const cmp &other, const ptr_pair &in_dp) |
| : data<cmp,ptr_pair>(other.task, |
| other.align, |
| other.sp, |
| other.params, |
| other.tables, |
| in_dp), |
| result(0) {} |
| |
| void walk_vec2(bool is_pod, uint16_t sp_size) { |
| walk_vec2(is_pod, get_vec_data_range(dp)); |
| } |
| |
| void walk_box2() { |
| data<cmp,ptr_pair>::walk_box_contents1(); |
| } |
| |
| void walk_uniq2() { |
| data<cmp,ptr_pair>::walk_uniq_contents1(); |
| } |
| |
| void walk_iface2() { |
| data<cmp,ptr_pair>::walk_box_contents1(); |
| } |
| |
| void walk_tydesc2(char) { |
| cmp_pointer(); |
| } |
| |
| void walk_fn2(char) { return cmp_two_pointers(); } |
| void walk_obj2() { return cmp_two_pointers(); } |
| |
| void walk_tag2(tag_info &tinfo, const data_pair<tag_variant_t> &tag_variants); |
| void walk_struct2(const uint8_t *end_sp); |
| void walk_res2(const rust_fn *dtor, uint16_t n_ty_params, |
| const type_param *ty_params_sp, const uint8_t *end_sp, |
| const data_pair<uintptr_t> &live); |
| void walk_variant2(tag_info &tinfo, |
| tag_variant_t variant_id, |
| const std::pair<const uint8_t *,const uint8_t *> |
| variant_ptr_and_end); |
| |
| template<typename T> |
| void walk_number2() { cmp_number(get_dp<T>(dp)); } |
| }; |
| |
| template<> |
| void cmp::cmp_number<int32_t>(const data_pair<int32_t> &nums) { |
| result = (nums.fst < nums.snd) ? -1 : (nums.fst == nums.snd) ? 0 : 1; |
| } |
| |
| void |
| cmp::walk_vec2(bool is_pod, const std::pair<ptr_pair,ptr_pair> &data_range) { |
| cmp sub(*this, data_range.first); |
| ptr_pair data_end = sub.end_dp = data_range.second; |
| while (!result && sub.dp < data_end) { |
| sub.walk_reset(); |
| result = sub.result; |
| sub.align = true; |
| } |
| |
| if (!result) { |
| // If we hit the end, the result comes down to length comparison. |
| int len_fst = data_range.second.fst - data_range.first.fst; |
| int len_snd = data_range.second.snd - data_range.first.snd; |
| cmp_number(data_pair<int>::make(len_fst, len_snd)); |
| } |
| } |
| |
| void |
| cmp::walk_tag2(tag_info &tinfo, const data_pair<tag_variant_t> &tag_variants) { |
| cmp_number(tag_variants); |
| if (result != 0) |
| return; |
| data<cmp,ptr_pair>::walk_variant1(tinfo, tag_variants.fst); |
| } |
| |
| void |
| cmp::walk_struct2(const uint8_t *end_sp) { |
| while (!result && this->sp != end_sp) { |
| this->walk(); |
| align = true; |
| } |
| } |
| |
| void |
| cmp::walk_res2(const rust_fn *dtor, uint16_t n_ty_params, |
| const type_param *ty_params_sp, const uint8_t *end_sp, |
| const data_pair<uintptr_t> &live) { |
| abort(); // TODO |
| } |
| |
| void |
| cmp::walk_variant2(tag_info &tinfo, |
| tag_variant_t variant_id, |
| const std::pair<const uint8_t *,const uint8_t *> |
| variant_ptr_and_end) { |
| cmp sub(*this, variant_ptr_and_end.first, tinfo.params); |
| |
| const uint8_t *variant_end = variant_ptr_and_end.second; |
| while (!result && sub.sp < variant_end) { |
| sub.walk(); |
| result = sub.result; |
| sub.align = true; |
| } |
| } |
| |
| |
| // Polymorphic logging, for convenience |
| |
| void |
| log::walk_string2(const std::pair<ptr,ptr> &data) { |
| out << prefix << "\"" << std::hex; |
| |
| ptr subdp = data.first; |
| while (subdp < data.second) { |
| char ch = *subdp; |
| if (isprint(ch)) |
| out << ch; |
| else if (ch) |
| out << "\\x" << std::setw(2) << std::setfill('0') << (int)ch; |
| ++subdp; |
| } |
| |
| out << "\"" << std::dec; |
| } |
| |
| void |
| log::walk_struct2(const uint8_t *end_sp) { |
| out << prefix << "("; |
| prefix = ""; |
| |
| bool first = true; |
| while (sp != end_sp) { |
| if (!first) |
| out << ", "; |
| walk(); |
| align = true, first = false; |
| } |
| |
| out << ")"; |
| } |
| |
| void |
| log::walk_vec2(bool is_pod, const std::pair<ptr,ptr> &data) { |
| if (peek() == SHAPE_U8) { |
| sp++; // It's a string. We handle this ourselves. |
| walk_string2(data); |
| return; |
| } |
| |
| out << prefix << "["; |
| |
| log sub(*this, data.first); |
| sub.end_dp = data.second; |
| |
| while (sub.dp < data.second) { |
| sub.walk_reset(); |
| sub.align = true; |
| sub.prefix = ", "; |
| } |
| |
| out << "]"; |
| } |
| |
| void |
| log::walk_variant2(tag_info &tinfo, |
| tag_variant_t variant_id, |
| const std::pair<const uint8_t *,const uint8_t *> |
| variant_ptr_and_end) { |
| log sub(*this, variant_ptr_and_end.first, tinfo.params); |
| const uint8_t *variant_end = variant_ptr_and_end.second; |
| |
| bool first = true; |
| while (sub.sp < variant_end) { |
| out << (first ? "(" : ", "); |
| sub.walk(); |
| sub.align = true, first = false; |
| } |
| |
| if (!first) |
| out << ")"; |
| } |
| |
| void |
| log::walk_res2(const rust_fn *dtor, unsigned n_params, |
| const type_param *params, const uint8_t *end_sp, bool live) { |
| out << prefix << "res"; |
| |
| if (this->sp == end_sp) |
| return; |
| |
| out << "("; |
| |
| bool first = true; |
| while (sp != end_sp) { |
| if (!first) |
| out << ", "; |
| walk(); |
| align = true, first = false; |
| } |
| |
| out << ")"; |
| } |
| |
| } // end namespace shape |
| |
| extern "C" void |
| shape_cmp_type(int8_t *result, const type_desc *tydesc, |
| const type_desc **subtydescs, uint8_t *data_0, |
| uint8_t *data_1, uint8_t cmp_type) { |
| rust_task *task = rust_scheduler::get_task(); |
| shape::arena arena; |
| |
| // FIXME: This may well be broken when comparing two closures or objects |
| // that close over different sets of type parameters. |
| shape::type_param *params = |
| shape::type_param::from_tydesc_and_data(tydesc, data_0, arena); |
| |
| shape::cmp cmp(task, true, tydesc->shape, params, tydesc->shape_tables, |
| data_0, data_1); |
| cmp.walk(); |
| |
| switch (cmp_type) { |
| case shape::CMP_EQ: *result = cmp.result == 0; break; |
| case shape::CMP_LT: *result = cmp.result < 0; break; |
| case shape::CMP_LE: *result = cmp.result <= 0; break; |
| } |
| } |
| |
| extern "C" rust_str * |
| shape_log_str(const type_desc *tydesc, uint8_t *data) { |
| rust_task *task = rust_scheduler::get_task(); |
| |
| shape::arena arena; |
| shape::type_param *params = |
| shape::type_param::from_tydesc_and_data(tydesc, data, arena); |
| |
| std::stringstream ss; |
| shape::log log(task, true, tydesc->shape, params, tydesc->shape_tables, |
| data, ss); |
| |
| log.walk(); |
| |
| int len = ss.str().length(); |
| return make_str(task->kernel, ss.str().c_str(), len, "log_str"); |
| } |
| |
| extern "C" void |
| shape_log_type(const type_desc *tydesc, uint8_t *data, uint32_t level) { |
| rust_task *task = rust_scheduler::get_task(); |
| |
| shape::arena arena; |
| shape::type_param *params = |
| shape::type_param::from_tydesc_and_data(tydesc, data, arena); |
| |
| std::stringstream ss; |
| shape::log log(task, true, tydesc->shape, params, tydesc->shape_tables, |
| data, ss); |
| |
| log.walk(); |
| |
| task->sched->log(task, level, "%s", ss.str().c_str()); |
| } |
| |