Why can a T* be passed in register, but a unique_ptr cannot?

Mobile Technologies Mobile Computing 2 years ago

0 1 0 0 0 tuteeHUB earn credit +10 pts

5 Star Rating 1 Rating

Posted on 16 Aug 2022, this text provides information on Mobile Computing related to Mobile Technologies. Please note that while accuracy is prioritized, the data presented might not be entirely correct or up-to-date. This information is offered for general knowledge and informational purposes only, and should not be considered as a substitute for professional advice.

Take Quiz To Earn Credits!

Turn Your Knowledge into Earnings.

tuteehub_quiz

Answers (1)

Post Answer
profilepic.png
manpreet Tuteehub forum best answer Best Answer 2 years ago
_x000D_ Is this actually an ABI requirement, or maybe it's just some pessimization in certain scenarios? System V Application Binary Interface (this ABI is followed on Solaris, Linux, FreeBSD, macOS, Windows Subsystem for Linux): If a C++ object has either a non-trivial copy constructor or a non-trivial destructor, it is passed by invisible reference (the object is replaced in the parameter list by a pointer that has class INTEGER). An object with either a non-trivial copy constructor or a non-trivial destructor cannot be passed by value because such objects must have well defined addresses. Similar issues apply when returning an object from a function. Note, that only 2 registers can be used for passing 1 object with a trivial copy constructor and a trivial destructor, i.e. only objects with sizeof no greater than 16 can be passed in registers. See Calling conventions by Agner Fog for a detailed treatment of the calling conventions, in particular §7.1 Passing and returning objects. There are separate calling conventions for passing SIMD types in registers. Why is the ABI like that? That is, if the fields of a struct/class fit within registers, or even a single register - why should we not be able to pass it within that register? It is an implementation detail, but when an exception is handled, during stack unwinding, the objects with automatic storage duration being destroyed must be addressable relative to the function stack frame because the registers have been clobbered by that time. Stack unwinding code needs objects' addresses to invoke their destructors but objects in registers do not have an address. Pedantically, destructors operate on objects: An object occupies a region of storage in its period of construction ([class.cdtor]), throughout its lifetime, and in its period of destruction. and an object cannot exist in C++ if no addressable storage is allocated for it because object's identity is its address. When an address of an object with a trivial copy constructor kept in registers is needed the compiler can just store the object into memory and obtain the address. If the copy constructor is non-trivial, on the other hand, the compiler cannot just store it into memory, it rather needs to call the copy constructor which takes a reference and hence requires the address of the object in the registers. The calling convention probably cannot depend whether the copy constructor was inlined in the callee or not. Another way to think about this, is that for trivially copyable types the compiler transfers the value of an object in registers, from which an object can be recovered by plain memory stores if necessary. E.g.: void f(long*); void g(long a) { f(&a); } on x86_64 with System V ABI compiles into: g(long): // Argument a is in rdi. push rax // Align stack, faster sub rsp, 8. mov qword ptr [rsp], rdi // Store the value of a in rdi into the stack to create an object. mov rdi, rsp // Load the address of the object on the stack into rdi. call f(long*) // Call f with the address in rdi. pop rax // Faster add rsp, 8. ret // The destructor of the stack object is trivial, no code to emit. In his thought-provoking talk Chandler Carruth mentions that a breaking ABI change may be necessary (among other things) to implement the destructive move that could improve things. IMO, the ABI change could be non-breaking if the functions using the new ABI explicitly opt-in to have a new different linkage, e.g. declare them in extern "C++20" {} block (possibly, in a new inline namespace for migrating existing APIs). So that only the code compiled against the new function declarations with the new linkage can use the new ABI. Note that ABI doesn't apply when the called function has been inlined. As well as with link-time code generation the compiler can inline functions defined in other translation units or use custom calling conventions.

No matter what stage you're at in your education or career, TuteeHub will help you reach the next level that you're aiming for. Simply,Choose a subject/topic and get started in self-paced practice sessions to improve your knowledge and scores.

Important Mobile Technologies Links