Readnone/Readonly Function Attributes and Optimization

Dear All,

Are functions marked as readnone or readonly in the LLVM IR allowed to generate output or to exhibit exceptional behavior (e.g., calling abort(), generating an MMU fault, etc.)?

The SAFECode compiler has a set of run-time checks that pass or fail based solely on the input arguments and, in some cases, global state. They do not modify a program's global state, but they do print output on failures and can abort the program. I'm wondering if adding the readnone/readonly attributes to these run-time checks is safe.

-- John T.

John,

Hi John,

Are functions marked as readnone or readonly in the LLVM IR allowed to
generate output or to exhibit exceptional behavior (e.g., calling
abort(), generating an MMU fault, etc.)?

they are allowed to unwind exceptions for example, since in theory this
can occur without scrunching externally visible memory (for example unwinding
can be done by having functions return an additional boolean value indicating
whether an exception was thrown, and have the caller examine this value and do
the appropriate branch; such an approach is 100% readnone if the underlying
function is). That said, dwarf exception handling can't be used since it
writes to global memory all over the place. Anyway, this is why readnone calls
with no uses aren't removed unless they are also nounwind.

Note that readnone/readonly functions aren't supposed to cause the program to
exit, or produce output, i.e. they can do it if they like, but the optimizers
will readily remove calls to them even though this changes program behaviour.

The SAFECode compiler has a set of run-time checks that pass or fail
based solely on the input arguments and, in some cases, global state.
They do not modify a program's global state, but they do print output on
failures and can abort the program. I'm wondering if adding the
readnone/readonly attributes to these run-time checks is safe.

Instead, how about having a readnone function check_xyz_passed that returns
a boolean saying whether the check passed. Call check_xyz, examine the return
value, and call the abort routine if it returned false.

Ciao, Duncan.

Hi John,

Are functions marked as readnone or readonly in the LLVM IR allowed to
generate output or to exhibit exceptional behavior (e.g., calling
abort(), generating an MMU fault, etc.)?

they are allowed to unwind exceptions for example, since in theory this
can occur without scrunching externally visible memory (for example unwinding
can be done by having functions return an additional boolean value indicating
whether an exception was thrown, and have the caller examine this value and do
the appropriate branch; such an approach is 100% readnone if the underlying
function is). That said, dwarf exception handling can't be used since it
writes to global memory all over the place. Anyway, this is why readnone calls
with no uses aren't removed unless they are also nounwind.

Note that readnone/readonly functions aren't supposed to cause the program to
exit, or produce output, i.e. they can do it if they like, but the optimizers
will readily remove calls to them even though this changes program behaviour.

Okay, so given several calls with the same input arguments to a function marked readnone or readonly but not nounwind, if the return values of all the calls are unused, will the optimizers remove *all* the calls or will they remove all but one call because there may be at least one call that unwinds the stack? To put it another way, do the optimizers ensure that the unwind behavior is preserved on all paths after optimization?

The SAFECode compiler has a set of run-time checks that pass or fail
based solely on the input arguments and, in some cases, global state.
They do not modify a program's global state, but they do print output on
failures and can abort the program. I'm wondering if adding the
readnone/readonly attributes to these run-time checks is safe.

Instead, how about having a readnone function check_xyz_passed that returns
a boolean saying whether the check passed. Call check_xyz, examine the return
value, and call the abort routine if it returned false.

We can do that for load/store checks. GEP checks may be a little more tricky (at least for SAFECode); they either return the real pointer if the GEP stays within bounds or a special OOB pointer value if the GEP goes out of bounds. I suppose we could add a dummy function that uses the GEP check's result, do the optimization, and then remove calls to the dummy function after running the LLVM optimizers.

-- John T.

Hi John,

Are functions marked as readnone or readonly in the LLVM IR allowed to
generate output or to exhibit exceptional behavior (e.g., calling
abort(), generating an MMU fault, etc.)?

they are allowed to unwind exceptions for example, since in theory this
can occur without scrunching externally visible memory (for example unwinding
can be done by having functions return an additional boolean value indicating
whether an exception was thrown, and have the caller examine this value and do
the appropriate branch; such an approach is 100% readnone if the underlying
function is). That said, dwarf exception handling can't be used since it
writes to global memory all over the place. Anyway, this is why readnone calls
with no uses aren't removed unless they are also nounwind.

Note that readnone/readonly functions aren't supposed to cause the program to
exit, or produce output, i.e. they can do it if they like, but the optimizers
will readily remove calls to them even though this changes program behaviour.

Okay, so given several calls with the same input arguments to a function marked
readnone or readonly but not nounwind, if the return values of all the calls are
unused, will the optimizers remove *all* the calls or will they remove all but
one call because there may be at least one call that unwinds the stack? To put
it another way, do the optimizers ensure that the unwind behavior is preserved
on all paths after optimization?

they are supposed to, so yes there should be one call left.

Ciao, Duncan.