This is an archive of the discontinued LLVM Phabricator instance.

[InstCombineCalls] Optimize call of bitcast even w/ parameter attributes
ClosedPublic

Authored by jdoerfert on Feb 16 2022, 12:38 PM.

Details

Summary

Before we gave up if a call through bitcast had parameter attributes.
Interestingly, we allowed attributes for the return value already. We
now handle both the same way, namely, we drop the ones that are
incompatible with the new type and keep the rest. This cannot cause
"more UB" than initially present.

Diff Detail

Event Timeline

jdoerfert created this revision.Feb 16 2022, 12:38 PM
jdoerfert requested review of this revision.Feb 16 2022, 12:39 PM
Herald added a project: Restricted Project. · View Herald TranscriptFeb 16 2022, 12:39 PM

I'm not convinced this is right when there are ABI affecting attributes involved. Consider the first example, where zeroext is dropped. Let's hypothetically assume that this indicates a zeroext up to 64-bit, but we have 32-bit pointers. Doing the transform and dropping the zeroext means that the top bits will be left uninitialized now.

For non-ABI attributes, I agree that just dropping them is fine.

I'm not convinced this is right when there are ABI affecting attributes involved. Consider the first example, where zeroext is dropped. Let's hypothetically assume that this indicates a zeroext up to 64-bit, but we have 32-bit pointers. Doing the transform and dropping the zeroext means that the top bits will be left uninitialized now.

I don't think zeroext is a problem. The 64 bit case shows why, isBitOrNoopPointerCastable is false and we won't transform it.

For non-ABI attributes, I agree that just dropping them is fine.

aeubanks added inline comments.Feb 21 2022, 4:40 PM
llvm/test/Transforms/InstCombine/call-cast-attrs.ll
36

is losing the nocapture worth changing this to a direct call?

jdoerfert added inline comments.Feb 21 2022, 7:41 PM
llvm/test/Transforms/InstCombine/call-cast-attrs.ll
36

Yes, because we cannot analyze or inline indirect calls. The nocapture here would not be derived by any pass.

I'm not convinced this is right when there are ABI affecting attributes involved. Consider the first example, where zeroext is dropped. Let's hypothetically assume that this indicates a zeroext up to 64-bit, but we have 32-bit pointers. Doing the transform and dropping the zeroext means that the top bits will be left uninitialized now.

I don't think zeroext is a problem. The 64 bit case shows why, isBitOrNoopPointerCastable is false and we won't transform it.

I'm referring to the case where the argument is pointer-sized (so it is castable), but the (target-specific) semantics of zeroext require an extension beyond pointer size.

I'm not convinced this is right when there are ABI affecting attributes involved. Consider the first example, where zeroext is dropped. Let's hypothetically assume that this indicates a zeroext up to 64-bit, but we have 32-bit pointers. Doing the transform and dropping the zeroext means that the top bits will be left uninitialized now.

I don't think zeroext is a problem. The 64 bit case shows why, isBitOrNoopPointerCastable is false and we won't transform it.

I'm referring to the case where the argument is pointer-sized (so it is castable), but the (target-specific) semantics of zeroext require an extension beyond pointer size.

So you are saying it is legal and expected that we extend the argument even though the callee expects a pointer (of smaller size)? (How is that not UB?)

friendly ping :)

Herald added a project: Restricted Project. · View Herald TranscriptMar 18 2022, 4:15 PM

In general, the reason we don't just treat calling a bitcast as instant UB is that we want to avoid breaking cases where different signatures represent equivalent calls, but the IR types aren't compatible. We don't have any standard rule for lowering C calls to IR, so different language frontends might do slightly different things.

Along those lines, some attributes hide significant differences in how the call is actually lowered; for example, byval means that a "pointer" isn't really a pointer at all. Some attributes are obviously irrelevant, like noundef.

Going through the LangRef list, zeroext/signext/inreg/sret/nest/swiftself/swiftasync/swifterror are the parameter attributes which this code currently doesn't explicitly check for, but are ABI significant. But... given the motivation, they're unlikely to be relevant, I guess? If there's a mismatch, and the call otherwise passes the checks, the untransformed call is probably UB anyway.

That said, I don't really want to try to write up a coherent justification for each of those, or worry about attributes that will be implemented in the future. An explicit list of attributes which are obviously safe to drop would be much simpler to reason about.

Introduce the concept of safe and unsafe attributes wrt. dropping them

Introduce the concept of safe and unsafe attributes wrt. dropping them

Nice!

efriedma accepted this revision.Mar 28 2022, 4:08 PM

LGTM

The classification into safe/unsafe makes sense to me. There's probably some further cleanup we could do here, but this seems like a reasonable intermediate step.

This revision is now accepted and ready to land.Mar 28 2022, 4:08 PM