C++ toolchain support#193
Conversation
|
Hi, Thanks for the PR! if (const RecordType *RT = T->getAs<RecordType>()) {
std::string name = RT->getDecl()->getNameAsString();
if (!name.empty()) return "__uniqtype__" + name;
}You are completely ignoring template names here, everything of The below is not necessarily about the PR itself, but C++ support in general Also, there is an interesting discussion to be had about #21 in terms of C++: is the built-in operator new an allocator or just a wrapper? C++ standard would say: unspecified, implementation-defined, whether we defer to std::malloc. Typically, implementations do. Unfortunately, this is mostly built in, so we would need to transpile to something like void* raw = std::malloc(sizeof(Point));
Point* p = new (raw) Point(42, 17);The way I'd naturally treat this is that the new is a sub-allocaton of malloc. Coming from C, I see why some could say that the unique type of void init_point(Point* p, const int& x, const int&y) {
p->x = x;
p->y=y;
// rest of the constructor body here
}
Point* p = std::reinterpret_cast<Point*>(std::malloc(sizeof(Point)));
init_point(p,42,17)Your code seems to go with the first, and whilst I agree, I think this should be a conscious design decision. There is also the case to consider where the new operator (via an override) allocates something other than the type-id within the new expression. An example of this would be a new operator stashing a cookie at the beginning of an object (Refcount anyone?). struct Point {
int x, y;
using Cookie = int;
static constexpr Cookie kMagic = 0xC0DE;
static constexpr std::size_t kCookieSize = sizeof(Cookie);
static constexpr std::size_t kOffset =
(kCookieSize + alignof(Point) - 1) & ~(alignof(Point) - 1);
void* operator new(std::size_t size) {
char* base = static_cast<char*>(::operator new(size + kOffset));
*reinterpret_cast<Cookie*>(base) = kMagic;
return base + kOffset;
}
void operator delete(void* p) noexcept {
if (!p) return;
char* base = static_cast<char*>(p) - kOffset;
::operator delete(base);
}
private:
static Cookie cookie_of(Point* p) {
return *reinterpret_cast<Cookie*>(reinterpret_cast<char*>(p) - kOffset);
}
};
Point p = new Point(2,3);The underlying allocation is clearly not of type point. But as
In this (overridden) case, new is clearly a sub-allocator of malloc malloc -> new char* -> new Pointwhich is fine. Porting C sizeof analysis can even make the char[] a bit more nuanced and recognise the stashed cookie type as an anonymous struct. (But I'd argue that is well within future work. No sane person codes like this normally: the example reinvents private fields for no good reason. Though I see how injecting said operator override into existing code might have benefits (GC & friends)) But in your code, |
|
Hello @difcsi @stephenrkell ! I forgot to add that this project is a draft. Thank you for the very detailed comments! You raise great points, but I need time to research the issues raised. |
|
No worries! I see you are at King's: I'm around on campus (7th Floor N09) most weekdays in case you'd like to chat! |
|
Added some more ramblings in #90 |
|
As discussed, the template names can be covered by the usual Placement See my comments in #90 for some funky partial approaches to the |
| if (!fdecl) return true; | ||
|
|
||
| std::string qualifiedName = fdecl->getQualifiedNameAsString(); | ||
| if (allocator_funcs.find(qualifiedName) == allocator_funcs.end()) return true; |
There was a problem hiding this comment.
But what about aliases? auto foo = std::malloc
Though they are simple enough to add to the set
There was a problem hiding this comment.
in test case test/malloc/malloc.cpp:26 I checked it
There was a problem hiding this comment.
void *s1 = std::malloc(sizeof(Point)); // __uniqtype__Pointhere you allocate something with stdmalloc. What I mean is
auto& foo = std::malloc;
void *s2 = foo(sizeof(Point))on the same note, I don't know how we handle this in C presently either: maybe its okay to give up
void *(*foo)(size_t) = malloc;There was a problem hiding this comment.
Ah, I see. We could resolve the reference back to the variable's declaration in order to recover which malloc it actually points to. However, there are cases where even this is not enough to determine statically whether a variable holds the standard library's malloc:
using AllocatorFunc = void* (*)(std::size_t);
AllocatorFunc foo;
if (true) {
foo = std::malloc;
} else {
foo = malloc; // some fake allocator
}There was a problem hiding this comment.
Indeed, for indirect calls what the C version of this analysis does is as follows.
- we output an entry in the
.i.allocsfor any indirect call that has a compatible signature with any known allocation function - these get matched with indirect calls at the binary level as we'd expect
- only the call targets that are actually allocation functions get instrumented
- ... so there's no effect if an analysed indirect call happens not to be an allocation function (except a bigger-than-necessary allocation sites table at run time, with some entries that will never be used)
I'd think of regular ('allocating') new as a wrapper that does a |
No description provided.