I noticed that LLVM will only optimize away an asm
with unused outputs if both the pure
and nomem
flags are set.
Based on our current rules for pure
, code using it can't read or write memory anyways.
I can see 2 options:
a) pure
implies nomem
and it is an error to specify both.
b) pure
requires the nomem
flag to also be set. It is an error otherwise.
Oh wait actually it's even worse. LLVM will happily remove a nomem
or readonly
asm, even if it is marked as sideeffect
.
OK, let's consider the cases we need to model:
I would argue that sideeffect
and nomem
being specified at the same time is a bug, as those are mutually exclusive in LLVM AFAIK.
And here are the LLVM attributes we have to work with: readnone, readonly, writeonly, argmemonly, inaccessiblememonly, inaccessiblemem_or_argmemonly
@nagisa Actually sideeffect
is an attribute on the asm, while readnone
is an attribute on the call
instruction that invokes the asm. So they don't actually interact in LLVM.
readonly sideeffect
being optimised out sounds like a bug.
sideeffect
is only actually looked at deep in the backend. By then the call asm
has already been optimized away.
I didn’t look much at the asm
's sideeffect annotation, but the sideeffect
intrinsic does interact with the readnone
etc attributes very much. In particular sideeffect
prevents LLVM from inferring a readnone
attribute where it otherwise would. But if readnone
is already specified somewhere, that takes precedence.
And readnone
is exactly what makes LLVM to delete things it considers to be dead code.
(this most likely applies to readonly
the same way)
I think we should use inaccessiblememonly
instead of readnone
for non-pure asm with nomem
. That tells LLVM that the asm doesn't touch any LLVM-visible memory, but still has some significant side-effect.
I wouldn't add readnone
attribute at all and let LLVM infer it. inaccessiblememonly
or inaccessiblemem_or_argmemonly
sound like saner choices.
LLVM can't infer it because it can't read the asm.
Clang uses readnone
/readonly
only for non-volatile asm.
Well… I guess. I don’t remember the RFC well enough to contribute to the discussion much here.
Hmm I think I'm going to go with option b: pure
must be used with either nomem
or readonly
, otherwise error. And I will relax the rules of pure
to allow reading from memory when used with readonly
.
So that leaves 4 valid combinations:
pure
nomem
pure
readonly
nomem
readonly
Which lower to:
readnone
readonly
inaccessiblememonly
what do writes count as? the "nothing" category?
Lokathor said:
what do writes count as? the "nothing" category?
I believe the default plain asm!
without annotations is the conservative thing and will allow arbitrary things (including writes)
Sorry 5 combinations, the last one being no attributes, which also corresponds to (nothing)
LLVM doesn't have any attributes to represent inaccessiblemem_or_readonly
, so we have to be conservative for readonly
without pure
.