The problem is that C compilers have very advanced optimizations. These make the language more dangerous. And yet, the programmer still has to take care of it!
I have a litmus test. Every few years, I compile code which does doubly-linked list operations:
struct node {
struct node *next, *prev;
};
/* Original: pred and succ must not overlap, but this is not expressed. */
void insert_after_A(struct node *pred, struct node *succ)
{
succ->prev = pred;
succ->next = pred->next;
pred->next->prev = succ;
pred->next = succ;
}
/* Optimize with restrict: pred and succ do not overlap. */
void insert_after_B(struct node *restrict pred, struct node *restrict succ)
{
succ->prev = pred;
succ->next = pred->next;
pred->next->prev = succ;
pred->next = succ;
}
/* Optimize by hand in "load-store style". */
void insert_after_C(struct node *pred, struct node *succ)
{
struct node *aft = pred->next;
succ->prev = pred;
succ->next = aft;
aft->prev = succ;
pred->next = succ;
}
I'm consistently finding that the manual optimizations in B and C are required to get the instruction count down.
Simply adding restrict is a simpler manual optimization, but it's dangerous; now demons are allowed to fly out of our noses when the arguments point to overlapping objects, which doesn't happen in function C.
And what's the use of restrict, when I can get the same benefit by coding in a certain way.
I have a litmus test. Every few years, I compile code which does doubly-linked list operations:
I'm consistently finding that the manual optimizations in B and C are required to get the instruction count down.Simply adding restrict is a simpler manual optimization, but it's dangerous; now demons are allowed to fly out of our noses when the arguments point to overlapping objects, which doesn't happen in function C.
And what's the use of restrict, when I can get the same benefit by coding in a certain way.