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.
Details
Diff Detail
- Repository
- rG LLVM Github Monorepo
Event Timeline
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 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.
llvm/test/Transforms/InstCombine/call-cast-attrs.ll | ||
---|---|---|
36 | is losing the nocapture worth changing this to a direct call? |
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 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?)
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.
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.
is losing the nocapture worth changing this to a direct call?