Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -203,13 +203,14 @@ Type serialization for each user-defined type (mode `hybrid`) or *all* types (mo
struct GlobalTypeInfo {
std::int32_t type_id;
const std::uint32_t extent;
const std::uint16_t num_members;
const std::uint16_t flag;
const GlobalTypeInfoData* data; // nullptr for built-ins
};
struct GlobalTypeInfoData {
const char* type_name;
const std::uint16_t* offsets;
const std::uint16_t* array_sizes;
// data : [ num_member, flag, offsets[num_member], array_sizes[num_member] ]:
const std::uint16_t* data;
const GlobalTypeInfo** member_types;
};
}
```

Each type is registered at startup with the TypeART runtime using the callback `void __typeart_register_type(const void* type_ptr);`. This adds the type information to the type database (for user queries) and assigns a unique `type-id`.
Expand Down
86 changes: 64 additions & 22 deletions lib/passes/analysis/MemOpVisitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -167,33 +167,75 @@ std::optional<InstTy*> getSingleUserAs(llvm::Instruction* value) {
using MallocGeps = SmallPtrSet<GetElementPtrInst*, 2>;
using MallocBcasts = SmallPtrSet<BitCastInst*, 4>;

std::pair<MallocGeps, MallocBcasts> collectRelevantMallocUsers(llvm::CallBase& ci) {
auto geps = MallocGeps{};
auto bcasts = MallocBcasts{};
for (auto user : ci.users()) {
// Simple case: Pointer is immediately casted
if (auto inst = dyn_cast<BitCastInst>(user)) {
bcasts.insert(inst);
}
// Pointer is first stored, then loaded and subsequently casted
if (auto storeInst = dyn_cast<StoreInst>(user)) {
auto storeAddr = storeInst->getPointerOperand();
for (auto storeUser : storeAddr->users()) { // TODO: Ensure that load occurs after store?
if (auto loadInst = dyn_cast<LoadInst>(storeUser)) {
for (auto loadUser : loadInst->users()) {
if (auto bcastInst = dyn_cast<BitCastInst>(loadUser)) {
// LOG_MSG(*bcastInst)
bcasts.insert(bcastInst);
}
}
// std::pair<MallocGeps, MallocBcasts> collectRelevantMallocUsers(llvm::CallBase& ci) {
// auto geps = MallocGeps{};
// auto bcasts = MallocBcasts{};
// for (auto user : ci.users()) {
// // Simple case: Pointer is immediately casted
// if (auto inst = dyn_cast<BitCastInst>(user)) {
// bcasts.insert(inst);
// }
// // Pointer is first stored, then loaded and subsequently casted
// if (auto storeInst = dyn_cast<StoreInst>(user)) {
// auto storeAddr = storeInst->getPointerOperand();
// if (!(storeAddr == nullptr || llvm::isa<llvm::ConstantPointerNull>(storeAddr))) {
// for (auto storeUser : storeAddr->users()) { // TODO: Ensure that load occurs after store?
// if (auto loadInst = dyn_cast<LoadInst>(storeUser)) {
// for (auto loadUser : loadInst->users()) {
// if (auto bcastInst = dyn_cast<BitCastInst>(loadUser)) {
// // LOG_MSG(*bcastInst)
// bcasts.insert(bcastInst);
// }
// }
// }
// }
// } else {
// LOG_DEBUG("Null, must skip")
// }
// }
// // GEP indicates that an array cookie is added to the allocation. (Fixes #13)
// if (auto gep = dyn_cast<GetElementPtrInst>(user)) {
// geps.insert(gep);
// }
// }
// return {geps, bcasts};
// }

void collect_casts_from_stack(llvm::StoreInst* store_inst, MallocBcasts& out_bcasts) {
auto* slot = store_inst->getPointerOperand();

// Guard: Skip invalid or null storage locations
if (llvm::isa<llvm::ConstantPointerNull>(slot)) {
LOG_DEBUG("Skipping null storage");
return;
}

for (auto* slot_user : slot->users()) {
// TODO: Ensure that load occurs after store?
if (auto* load_inst = llvm::dyn_cast<llvm::LoadInst>(slot_user)) {
for (auto* load_user : load_inst->users()) {
if (auto* bit_cast = llvm::dyn_cast<llvm::BitCastInst>(load_user)) {
out_bcasts.insert(bit_cast);
}
}
}
// GEP indicates that an array cookie is added to the allocation. (Fixes #13)
if (auto gep = dyn_cast<GetElementPtrInst>(user)) {
geps.insert(gep);
}
}

std::pair<MallocGeps, MallocBcasts> collectRelevantMallocUsers(llvm::CallBase& call_inst) {
auto geps = MallocGeps{};
auto bcasts = MallocBcasts{};

for (auto* user : call_inst.users()) {
if (auto* bit_cast = llvm::dyn_cast<llvm::BitCastInst>(user)) {
bcasts.insert(bit_cast);
} else if (auto* gep_inst = llvm::dyn_cast<llvm::GetElementPtrInst>(user)) {
geps.insert(gep_inst);
} else if (auto* store_inst = llvm::dyn_cast<llvm::StoreInst>(user)) {
collect_casts_from_stack(store_inst, bcasts);
}
}

return {geps, bcasts};
}

Expand Down
132 changes: 88 additions & 44 deletions lib/passes/instrumentation/TypeIDProvider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "support/ConfigurationBase.h"
#include "support/Logger.h"

#include <cstdint>
#include <llvm/ADT/ArrayRef.h>
#include <llvm/ADT/StringMap.h>
#include <llvm/ADT/StringRef.h>
Expand All @@ -29,6 +30,7 @@
#include <optional>
#include <string>
#include <utility>
#include <vector>

namespace typeart {

Expand Down Expand Up @@ -72,6 +74,40 @@ inline std::string create_prefixed_name(Args&&... args) {
return name;
}

namespace detail {
template <typename T, typename SourceT>
T safe_cast(SourceT val) {
// Check if value exceeds the maximum limit of the target type T
// We cast max() to size_t to ensure we are comparing compatible types safely
assert(static_cast<size_t>(val) <= static_cast<size_t>(std::numeric_limits<T>::max()) &&
"Data loss detected: Value exceeds target type limits!");
return static_cast<T>(val);
}
} // namespace detail

template <typename T>
std::vector<T> get_serialized_members_for(const StructTypeInfo& info) {
using namespace detail;
std::vector<T> dest;
const size_t required_space = info.offsets.size() + info.array_sizes.size() + 2;
dest.reserve(required_space);

// Layout : [ num_member, flag, offsets...[num_member], array_sizes...[num_member] ]

dest.push_back(safe_cast<T>(info.num_members));
dest.push_back(safe_cast<T>(static_cast<std::underlying_type_t<StructTypeFlag>>(info.flag)));

for (size_t offset : info.offsets) {
dest.push_back(safe_cast<T>(offset));
}

for (size_t size : info.array_sizes) {
dest.push_back(safe_cast<T>(size));
}

return dest;
}

} // namespace helper

namespace typedb {
Expand Down Expand Up @@ -151,7 +187,8 @@ enum class IGlobalType : short {
member_types,
member_count,
type_flag,
ptr
ptr,
info_holder
};

struct TypeHelper {
Expand Down Expand Up @@ -181,13 +218,14 @@ struct TypeHelper {
}
case IGlobalType::name:
case IGlobalType::ptr:
case IGlobalType::info_holder:
#if LLVM_VERSION_MAJOR < 15
return ir_build_.getInt8PtrTy();
#else
return ir_build_.getPtrTy();
#endif
}
llvm_unreachable("Should not be reached disk");
llvm_unreachable("Should not be reached");
}

llvm::Constant* get_constant_for(IGlobalType type, size_t value) {
Expand Down Expand Up @@ -218,22 +256,23 @@ struct GlobalTypeRegistrar {
llvm::IRBuilder<> ir_build;
GlobalTypeCallback type_callback;
llvm::StructType* struct_layout_type_;
llvm::StructType* struct_layout_type_cold_;
TypeHelper types_helper;
const bool builtin_emit_name{false};

void declare_layout() {
auto& context = module_->getContext();
struct_layout_type_ = llvm::StructType::create(context, "struct._typeart_struct_layout_t");
struct_layout_type_->setBody({
types_helper.get_type_for(IGlobalType::type_id), // int type_id
types_helper.get_type_for(IGlobalType::extent), // uint32 extent
types_helper.get_type_for(IGlobalType::num_members), // uint16 num_members
types_helper.get_type_for(IGlobalType::type_flag), // uint16 type_flag
types_helper.get_type_for(IGlobalType::type_id), // uint32 type_id
types_helper.get_type_for(IGlobalType::extent), // uint32 extent
types_helper.get_type_for(IGlobalType::info_holder),
});
struct_layout_type_cold_ = llvm::StructType::create(context, "struct._typeart_struct_layout_info_t");
struct_layout_type_cold_->setBody({
types_helper.get_type_for(IGlobalType::name), // const char* name
types_helper.get_type_for(IGlobalType::member_offsets), // const uint16* offsets
types_helper.get_type_for(IGlobalType::member_count), // const uint16* count
types_helper.get_type_for(IGlobalType::member_types), // const typeart_struct_layout_t** member_types

});
}

Expand Down Expand Up @@ -274,10 +313,11 @@ struct GlobalTypeRegistrar {
return create_global(global_name, array_ty, constant_array);
}

llvm::Constant* create_global_array_ptr(const llvm::StringRef name, llvm::ArrayRef<uint64_t> values,
template <typename T>
llvm::Constant* create_global_array_ptr(const llvm::StringRef name, llvm::ArrayRef<T> values,
IGlobalType type = IGlobalType::member_offsets) {
return create_global_array_from_range(name, values, types_helper.get_type_for(IGlobalType::member_offsets, true),
[&](uint64_t val) { return types_helper.get_constant_for(type, val); });
[&](const T& val) { return types_helper.get_constant_for(type, val); });
}

llvm::Constant* create_global_member_array_ptr(const llvm::StringRef name, llvm::ArrayRef<int> member_types) {
Expand All @@ -294,51 +334,55 @@ struct GlobalTypeRegistrar {
LOG_DEBUG("Type is forward decl " << base_name)
}

llvm::Constant* offsets_ptr = create_global_array_ptr(helper::concat("offsets_", link_name), type_struct->offsets);
llvm::Constant* counts_ptr = create_global_array_ptr(helper::concat("counts_", link_name), type_struct->array_sizes,
IGlobalType::member_count);
llvm::Constant* members_ptr =
create_global_member_array_ptr(helper::concat("member_types_", link_name), type_struct->member_types);

const bool is_builtin = type_struct->flag == StructTypeFlag::BUILTIN;
const bool emit_name = !is_builtin || builtin_emit_name;

llvm::Constant* name_str_ptr =
emit_name ? create_global_constant_string(link_name, base_name) : types_helper.get_constant_nullptr();

llvm::GlobalVariable* global_struct =
create_global(link_name, struct_layout_type_, nullptr, llvm::GlobalValue::LinkOnceODRLinkage);
global_struct->setConstant(false);

std::vector<llvm::Constant*> init_fields = {
types_helper.get_constant_for(IGlobalType::type_id, type_struct->type_id),
types_helper.get_constant_for(IGlobalType::extent, type_struct->extent),
types_helper.get_constant_for(IGlobalType::member_count, type_struct->member_types.size()),
types_helper.get_constant_for(IGlobalType::type_flag, static_cast<int>(type_struct->flag)),
name_str_ptr,
offsets_ptr,
counts_ptr,
members_ptr};

llvm::Constant* init = llvm::ConstantStruct::get(struct_layout_type_, init_fields);
global_struct->setInitializer(init);
llvm::Comdat* comdat = this->module_->getOrInsertComdat(helper::create_prefixed_name(link_name));
comdat->setSelectionKind(llvm::Comdat::Any);
global_struct->setComdat(comdat);

{
llvm::Comdat* comdat = this->module_->getOrInsertComdat(helper::create_prefixed_name(link_name));
comdat->setSelectionKind(llvm::Comdat::Any);
global_struct->setComdat(comdat);
auto add_to_comdat = [&](llvm::Constant* ptr) {
if (auto* global = llvm::dyn_cast_or_null<llvm::GlobalObject>(ptr)) {
global->setComdat(comdat);
}
};

auto add_to_comdat = [&](llvm::Constant* ptr) {
if (auto* global = llvm::dyn_cast_or_null<llvm::GlobalObject>(ptr)) {
global->setComdat(comdat);
const auto get_info_object = [&]() -> llvm::Constant* {
if (emit_name) {
llvm::Constant* name_str_ptr = create_global_constant_string(link_name, base_name);
const auto info_data = helper::get_serialized_members_for<uint16_t>(*type_struct);
llvm::Constant* data_ptr =
create_global_array_ptr<uint16_t>(helper::concat("info_data_", link_name), info_data);
llvm::Constant* members_ptr =
create_global_member_array_ptr(helper::concat("member_types_", link_name), type_struct->member_types);

llvm::GlobalVariable* global_struct_info =
create_global(helper::concat(link_name, "_info"), struct_layout_type_cold_, nullptr);

std::vector<llvm::Constant*> init_fields_cold = {name_str_ptr, data_ptr, members_ptr};
global_struct_info->setInitializer(llvm::ConstantStruct::get(struct_layout_type_cold_, init_fields_cold));

{
add_to_comdat(global_struct_info);
add_to_comdat(data_ptr);
add_to_comdat(members_ptr);
add_to_comdat(name_str_ptr);
}
};

add_to_comdat(offsets_ptr);
add_to_comdat(counts_ptr);
add_to_comdat(members_ptr);
add_to_comdat(name_str_ptr);
}
return global_struct_info;
}
return types_helper.get_constant_nullptr();
};

std::vector<llvm::Constant*> init_fields = {
types_helper.get_constant_for(IGlobalType::type_id, type_struct->type_id),
types_helper.get_constant_for(IGlobalType::extent, type_struct->extent), get_info_object()};
llvm::Constant* init = llvm::ConstantStruct::get(struct_layout_type_, init_fields);
global_struct->setInitializer(init);

return global_struct;
}
Expand Down
10 changes: 5 additions & 5 deletions lib/runtime/AllocationTracking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ void __typeart_leave_scope_omp(int alloca_count) {
void __typeart_alloc_mty(const void* addr, const void* info, size_t count) {
TYPEART_RUNTIME_GUARD;
const void* retAddr = __builtin_return_address(0);
const auto type_id = reinterpret_cast<const typeart::GlobalTypeInfo*>(info)->type_id;
const auto type_id = reinterpret_cast<const typeart::global_types::GlobalTypeInfo*>(info)->type_id;
auto& rt = typeart::RuntimeSystem::get();
assert(type_id == rt.type_translator().get_type_id_for(info) && "Type ID of global and lookup must match");
rt.allocation_tracker().onAlloc(addr, type_id, count, retAddr);
Expand All @@ -301,7 +301,7 @@ void __typeart_alloc_mty(const void* addr, const void* info, size_t count) {
void __typeart_alloc_stack_mty(const void* addr, const void* info, size_t count) {
TYPEART_RUNTIME_GUARD;
const void* retAddr = __builtin_return_address(0);
const auto type_id = reinterpret_cast<const typeart::GlobalTypeInfo*>(info)->type_id;
const auto type_id = reinterpret_cast<const typeart::global_types::GlobalTypeInfo*>(info)->type_id;
auto& rt = typeart::RuntimeSystem::get();
assert(type_id == rt.type_translator().get_type_id_for(info) && "Type ID of global and lookup must match");
rt.allocation_tracker().onAllocStack(addr, type_id, count, retAddr);
Expand All @@ -310,7 +310,7 @@ void __typeart_alloc_stack_mty(const void* addr, const void* info, size_t count)
void __typeart_alloc_global_mty(const void* addr, const void* info, size_t count) {
TYPEART_RUNTIME_GUARD;
const void* retAddr = __builtin_return_address(0);
const auto type_id = reinterpret_cast<const typeart::GlobalTypeInfo*>(info)->type_id;
const auto type_id = reinterpret_cast<const typeart::global_types::GlobalTypeInfo*>(info)->type_id;
auto& rt = typeart::RuntimeSystem::get();
assert(type_id == rt.type_translator().get_type_id_for(info) && "Type ID of global and lookup must match");
rt.allocation_tracker().onAllocGlobal(addr, type_id, count, retAddr);
Expand All @@ -319,7 +319,7 @@ void __typeart_alloc_global_mty(const void* addr, const void* info, size_t count
void __typeart_alloc_omp_mty(const void* addr, const void* info, size_t count) {
TYPEART_RUNTIME_GUARD;
const void* retAddr = __builtin_return_address(0);
const auto type_id = reinterpret_cast<const typeart::GlobalTypeInfo*>(info)->type_id;
const auto type_id = reinterpret_cast<const typeart::global_types::GlobalTypeInfo*>(info)->type_id;
auto& rt = typeart::RuntimeSystem::get();
assert(type_id == rt.type_translator().get_type_id_for(info) && "Type ID of global and lookup must match");
rt.allocation_tracker().onAlloc(addr, type_id, count, retAddr);
Expand All @@ -328,7 +328,7 @@ void __typeart_alloc_omp_mty(const void* addr, const void* info, size_t count) {
void __typeart_alloc_stack_omp_mty(const void* addr, const void* info, size_t count) {
TYPEART_RUNTIME_GUARD;
const void* retAddr = __builtin_return_address(0);
const auto type_id = reinterpret_cast<const typeart::GlobalTypeInfo*>(info)->type_id;
const auto type_id = reinterpret_cast<const typeart::global_types::GlobalTypeInfo*>(info)->type_id;
auto& rt = typeart::RuntimeSystem::get();
assert(type_id == rt.type_translator().get_type_id_for(info) && "Type ID of global and lookup must match");
rt.allocation_tracker().onAllocStack(addr, type_id, count, retAddr);
Expand Down
Loading