One disadvantage of working for my current employers is that pretty well everything I do is technically commercially sekrit, no matter how dull it might be. But this little fragment isn’t, and its fun. Perhaps it would make a good interview question.
What does the following C fragment compile into…
{
extern bool other_thing(void);
int a;
if (other_thing())
a = 1;
report_thing(a);
}
…when compiled with gcc, version 4.2, for C89, and certain optimisations that I won’t go into? Specifically, what value does “report_thing” get passed? If you’re thinking “an undefined value, unless other_thing() returns true” then you get some points. Though that would be boring, so its the wrong answer. But assume that other_thing() is external to this compilation unit (so the compiler can’t see it, know anything about its internals, or work out whether its going to return true or false), and assume furthermore (just for the same of simplicity) that it always returns false.
Give up? Try reading this article then have another guess.
OK, here’s the assembler:
: ;; if (other_thing());
: ;; a = 1;
bsr $other_thing
: ;; report_thing(a);
ld AL,#H'0001
bsr $report_thing
Actually that’s not quite the vanilla assembler, but its essentially that (for [[XAP]]). What has happened is that the compiler has put in the call to other_thing(), but ignored the result, and simply called report_thing as though “a” were always 1(the first argument of a function is put in the AL register, in this convention). But why?
The answer of course is nasal demons, or more prosaically optimisation in the face of violation of the language spec. Reading from an uninitialised location in this way is undefined behaviour and allows the compiler to do anything it feels like. A malicious compiler (aren’t they all) would do something odd for reasons of pure evil, but – much as it might seem like in this case – that’s not why its done it. What its done is attempt to apply optimisation (or so I think – I can’t read its mind and don’t know how to get it to tell me what its thinking). Its trying to prune away irrelevant conditionals in order to speed up the code. “Aha”, it thinks, “if other_thing() returns false, then ‘a’ isn’t initialised, and that’s against the spec. Therefore, other_thing() must return true, even though I can’t see it. So, I’ll assume it does.” And since its now deduced, to its own satisfaction, that other_thing() always returns true, it knows that ‘a’ will always be set to 1. Note, BTW, that it still has to put in the call to other_thing(), because it doesn’t know whether it has side effects or not.
from ScienceBlogs http://ift.tt/1IfWuru
One disadvantage of working for my current employers is that pretty well everything I do is technically commercially sekrit, no matter how dull it might be. But this little fragment isn’t, and its fun. Perhaps it would make a good interview question.
What does the following C fragment compile into…
{
extern bool other_thing(void);
int a;
if (other_thing())
a = 1;
report_thing(a);
}
…when compiled with gcc, version 4.2, for C89, and certain optimisations that I won’t go into? Specifically, what value does “report_thing” get passed? If you’re thinking “an undefined value, unless other_thing() returns true” then you get some points. Though that would be boring, so its the wrong answer. But assume that other_thing() is external to this compilation unit (so the compiler can’t see it, know anything about its internals, or work out whether its going to return true or false), and assume furthermore (just for the same of simplicity) that it always returns false.
Give up? Try reading this article then have another guess.
OK, here’s the assembler:
: ;; if (other_thing());
: ;; a = 1;
bsr $other_thing
: ;; report_thing(a);
ld AL,#H'0001
bsr $report_thing
Actually that’s not quite the vanilla assembler, but its essentially that (for [[XAP]]). What has happened is that the compiler has put in the call to other_thing(), but ignored the result, and simply called report_thing as though “a” were always 1(the first argument of a function is put in the AL register, in this convention). But why?
The answer of course is nasal demons, or more prosaically optimisation in the face of violation of the language spec. Reading from an uninitialised location in this way is undefined behaviour and allows the compiler to do anything it feels like. A malicious compiler (aren’t they all) would do something odd for reasons of pure evil, but – much as it might seem like in this case – that’s not why its done it. What its done is attempt to apply optimisation (or so I think – I can’t read its mind and don’t know how to get it to tell me what its thinking). Its trying to prune away irrelevant conditionals in order to speed up the code. “Aha”, it thinks, “if other_thing() returns false, then ‘a’ isn’t initialised, and that’s against the spec. Therefore, other_thing() must return true, even though I can’t see it. So, I’ll assume it does.” And since its now deduced, to its own satisfaction, that other_thing() always returns true, it knows that ‘a’ will always be set to 1. Note, BTW, that it still has to put in the call to other_thing(), because it doesn’t know whether it has side effects or not.
from ScienceBlogs http://ift.tt/1IfWuru
Aucun commentaire:
Enregistrer un commentaire