7 ms·
For a copy on write const ref ABI trick to work, the referenced area has to be stable while the callee is running. Good luck with guarantying that in C or C++ (
by temac 5y ago
For a copy on write const ref ABI trick to work, the referenced area has to be stable while the callee is running. Good luck with guarantying that in C or C++ (and no, it would not necessarily be a race for the refed object to be concurrently unstable, the called function could have internal synchro...)
So I think this idea is virtually impossible. Your ABI is probably not wrong, but your blog post probably is :P
- mort96 5y agoWell, no. The callee would just have to make a copy in cases where the memory might change. The compiler would have to produce a copy in the case of this function: void foo1(struct large l) { bar(); baz(l.x + l.y); } Because the call to `bar()` might change the caller's copy, so the callee `foo1` has to make a local copy before calling `bar()`. But it would not have to produce a copy in this example: void foo2(struct large l) { baz(l.x + l.y); } Because nothing can change the caller's copy before the callee `foo2` is done using it.
- Negitivefrags 5y agoThis isn't correct. The compiler would not have to produce a copy in either case. The call to bar() can not change the value of l because the person who called foo1 gave you an immutable pointer to it. How did foo1's caller know it was immutable? If the value passed to foo1 was a local variable, or one of it's arguments passed to it by immutable pointer, then all the compiler has to do is check if, in that function, it created a mutable pointer to the value and put it somewhere accessable to someone else. If it did not, then it can just pass the pointer right through. If it did create a mutable pointer, or if the value came from some other place the compiler has no knowledge about, then it would make a copy of the value before passing a pointer to foo1, just like it would do for the old ABI.
- mort96 5y agoI don't think you're correct here, but it's totally possible that I'm misunderstanding something. When generating code for `foo1`, the compiler has to know how the parameters are laid out. The code generated for `foo1` can't depend on the code which calls `foo1`. It is my understanding that the proposed ABI says that `foo1` will get `l` passed with a pointer, and that the term "immutable pointer" just means that `foo1` isn't allowed to modify the object through the pointer. The context (invisible to foo1) could look like this: struct large *lptr; void bar() { lptr->x += 10; } void do_thing() { struct large l = { ... }; lptr = &l; foo1(l); } So the compiler must assume that the call to `bar()` can modify the object pointed to by the "immutable pointer". --- Or are you suggesting that the callee's decision on whether to make a copy or not should be a runtime decision, and that the caller passes in information about whether the callee has to copy or not?
- Negitivefrags 5y agoAs you know, in do_thing you have taken a mutable pointer to l and placed it in a location where someone else could access it. Thefore, before the call to foo1, the compiler simply creates a copy of l, and then passes an immutable pointer to that to foo1. Effectively falling back to how the old ABI worked. The code generated for foo1 does depend on the code that calls foo1. That is what an ABI is. An ABI is a specification of the contract between the caller and the callee. The proposed new ABI says "If you are passing a struct by value, then you, the caller, will give me a pointer that you guarantee can't be modified until I return. In turn, I guarantee that I will not modify the pointer you give me."
- mort96 5y agoI think I understand what you're saying now, and where the confusion is. My understanding of the ABI was: "The callee will always receive a pointer to the caller's copy. If the callee can't guarantee that nothing modifies the caller's copy, the callee makes a copy." Your understanding is subtly different, because the caller guarantees that nothing else has a pointer to the parameter (and the caller guarantees that by making a copy when that's the only option). To be honest, I think the ABI according to my understanding is better. It does reduce the amount of cases a copy can be avoided, but it means that the worst-case is that one copy is made (by the callee). With your version, you would have a bunch of situations where the caller has to make a copy (because a pointer has escaped) and the callee has to make a copy (because it modifies the object). With my version, the worst-case is that a copy is made once (by the callee), but the worst-case would be hit more often. I think my understanding is closer to the intent of the article, due to this sentence: "In the event that a copy is needed, it will happen only once, in the callee, rather than needing to be repeated by every caller." In your version, the copy is sometimes made in the caller and sometimes in the callee.