#include #include #include static UID next_uid = 0; static std::unordered_map targets; static tree get_record_main_variant(tree t) { if (!t || TREE_CODE(t) != RECORD_TYPE) return NULL_TREE; return TYPE_MAIN_VARIANT(t); } TargetType::TargetType(tree t) : m_uid{ UID_INVALID }, m_flags{ 0 }, m_size{ 0 } { if (!(m_main_variant = get_record_main_variant(t))) return; m_flags |= FLAG_MAIN_VARIANT; } TargetType::~TargetType() {} bool TargetType::valid() const { return (m_flags & FLAG_MAIN_VARIANT) != 0; } bool TargetType::has_fields() const { return (m_flags & FLAG_FIELDS) != 0; } const std::map& TargetType::fields() const { return m_fields; } std::string TargetType::name() const { const char* error_name = ""; const char* anonymous_name = ""; if (!valid()) return { error_name }; tree name_tree = TYPE_NAME(m_main_variant); if (!name_tree) return { anonymous_name }; if (TREE_CODE(name_tree) == TYPE_DECL && DECL_NAME(name_tree)) return { IDENTIFIER_POINTER(DECL_NAME(name_tree)) }; else if (TREE_CODE(name_tree) == IDENTIFIER_NODE) return { IDENTIFIER_POINTER(name_tree) }; return { anonymous_name }; } const TargetType::Field* TargetType::field(std::size_t off, bool exact) const { if (!valid() || !(m_flags & FLAG_FIELDS)) return nullptr; auto it = m_fields.upper_bound(off); // Next element if (it == m_fields.begin()) return nullptr; --it; // Element of interest const TargetType::Field& maybe = it->second; if (off >= maybe.offset + maybe.size) return nullptr; if (exact && maybe.offset != off) return nullptr; return &maybe; } UID TargetType::uid() const { return m_uid; } std::size_t TargetType::size() const { if (!valid() || !(m_flags & FLAG_FIELDS)) return 0; return m_size; } void TargetType::add(tree t) { if (find(t) != nullptr) return; TargetType tmp { t }; if (!tmp.valid()) return; tmp.m_uid = next_uid++; targets.emplace(tmp.m_uid, tmp); } std::size_t TargetType::count() { return targets.size(); } const TargetType* TargetType::find(tree t) { tree main_variant = get_record_main_variant(t); if (!main_variant) return nullptr; for (const auto& [uid, target] : targets) { if (lang_hooks.types_compatible_p(main_variant, target.m_main_variant)) return ⌖ } return nullptr; } TargetType* TargetType::find_mutable(tree t) { tree main_variant = get_record_main_variant(t); if (!main_variant) return nullptr; for (auto& [uid, target] : targets) { if (lang_hooks.types_compatible_p(main_variant, target.m_main_variant)) return ⌖ } return nullptr; } const TargetType* TargetType::find(UID uid) { auto it = targets.find(uid); if (it == targets.end()) return nullptr; return &it->second; } bool field_info(tree field_decl, std::size_t* offset, std::size_t* size, bool* bitfield) { if (!field_decl || TREE_CODE(field_decl) != FIELD_DECL) return false; HOST_WIDE_INT tmp_byte_offset = 0; if (TREE_CODE(DECL_FIELD_OFFSET(field_decl)) == INTEGER_CST) tmp_byte_offset = tree_to_uhwi(DECL_FIELD_OFFSET(field_decl)); else return false; HOST_WIDE_INT tmp_bit_offset = 0; if (TREE_CODE(DECL_FIELD_BIT_OFFSET(field_decl)) == INTEGER_CST) tmp_bit_offset = tree_to_uhwi(DECL_FIELD_BIT_OFFSET(field_decl)); else return false; HOST_WIDE_INT bit_offset_bytes = tmp_bit_offset / 8; tmp_byte_offset += bit_offset_bytes; tmp_bit_offset -= bit_offset_bytes * 8; HOST_WIDE_INT tmp_bit_size = 0; if (TREE_CODE(DECL_SIZE(field_decl)) == INTEGER_CST) tmp_bit_size = tree_to_uhwi(DECL_SIZE(field_decl)); else return false; bool tmp_bitfield = (DECL_BIT_FIELD_TYPE(field_decl) != NULL_TREE); tmp_bitfield |= !(tmp_bit_size % 8 == 0 && tmp_bit_offset == 0); // Intra-byte offset counts towards size tmp_bit_size += tmp_bit_offset; // Round size up to entire byte HOST_WIDE_INT tmp_bit_overhang = tmp_bit_size % 8; if (tmp_bit_overhang != 0) tmp_bit_size += (8 - tmp_bit_overhang); // Set all outputs if (offset) *offset = static_cast(tmp_byte_offset); if (size) *size = static_cast(tmp_bit_size / 8); if (bitfield) *bitfield = tmp_bitfield; return true; } bool TargetType::reference(tree ref, UID& target, std::size_t& offset) { if (!ref || TREE_CODE(ref) != COMPONENT_REF) return false; tree base = TREE_OPERAND(ref, 0); if (!base) return false; tree base_type = TREE_TYPE(base); if (!base_type) return false; const TargetType* base_target = TargetType::find(base_type); if (!base_target) return false; target = base_target->uid(); tree field_decl = TREE_OPERAND(ref, 1); if (!field_info(field_decl, &offset, nullptr, nullptr)) return false; const Field* f = base_target->field(offset, false); if (!f || (f->flags & Field::FLAG_DANGEROUS)) return false; return true; } const std::unordered_map& TargetType::all() { return targets; } void TargetType::reset() { targets.clear(); next_uid = 0; } static bool foreach_record_field(tree t, std::function callback) { if (!t || TREE_CODE(t) != RECORD_TYPE) return false; if (!COMPLETE_TYPE_P(t)) return false; for (tree field_decl = TYPE_FIELDS(t); field_decl; field_decl = DECL_CHAIN(field_decl)) { if (TREE_CODE(field_decl) != FIELD_DECL) continue; TargetType::Field field; bool is_bitfield; if (!field_info(field_decl, &field.offset, &field.size, &is_bitfield)) return false; field.flags = (is_bitfield ? TargetType::Field::FLAG_DANGEROUS : 0); if (!callback(field)) return false; } return true; } static bool field_map_add(std::map& map, const TargetType::Field& field) { TargetType::Field tmp_field; tmp_field.offset = field.offset; tmp_field.size = (field.size == 0 ? 1 : field.size); tmp_field.flags = (field.size == 0 ? TargetType::Field::FLAG_DANGEROUS : 0) | field.flags; // Overlaps are dangerous -> remove and integrate into member auto overlap_end = map.lower_bound(tmp_field.offset + tmp_field.size); for (auto it = std::make_reverse_iterator(overlap_end); it != map.rend();) { const TargetType::Field& existing_field = it->second; if (existing_field.offset + existing_field.size <= tmp_field.offset) break; auto combined_end = std::max(tmp_field.offset + tmp_field.size, existing_field.offset + existing_field.size); auto combined_offset = std::min(tmp_field.offset, existing_field.offset); auto combined_size = combined_end - combined_offset; tmp_field.flags |= (existing_field.flags | TargetType::Field::FLAG_DANGEROUS); tmp_field.offset = combined_offset; tmp_field.size = combined_size; // Erase overlapping member auto tmp_forward = std::prev(it.base()); tmp_forward = map.erase(tmp_forward); it = std::make_reverse_iterator(tmp_forward); } map.emplace(tmp_field.offset, tmp_field); return true; } bool TargetType::fetch_fields(bool redo) { if (!valid()) return false; if ((m_flags & FLAG_FIELDS) != 0 && !redo) return true; m_flags &= ~FLAG_FIELDS; m_fields.clear(); std::map tmp_fields; auto per_field_callback = [&tmp_fields](const Field& field) -> bool { return field_map_add(tmp_fields, field); }; if (!foreach_record_field(m_main_variant, per_field_callback)) return false; // Get struct size tree size_tree = TYPE_SIZE(m_main_variant); if (!size_tree || TREE_CODE(size_tree) != INTEGER_CST) return false; HOST_WIDE_INT size_bits = tree_to_uhwi(size_tree); if (size_bits < 0 || size_bits % 8 != 0) return false; m_size = static_cast(size_bits / 8); // Everything done m_fields = std::move(tmp_fields); m_flags |= FLAG_FIELDS; return true; }