[clang] [clang][Interp] Implement __builtin_bit_cast (PR #68288)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Feb 5 10:39:32 PST 2024
Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>,
Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>,
Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>,
Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>,
Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>,
Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>,
Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>,
Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>,
Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>,
Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>,
Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>,
Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>,
Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>
Message-ID:
In-Reply-To: <llvm.org/llvm/llvm-project/pull/68288 at github.com>
================
@@ -107,6 +112,14 @@ class Boolean final {
return Boolean(!Value.isZero());
}
+ static Boolean bitcastFromMemory(const std::byte *Buff, unsigned BitWidth) {
+ assert(BitWidth == 8);
+ bool Val = static_cast<bool>(*Buff);
+ return Boolean(Val);
+ }
+
+ void bitcastToMemory(std::byte *Buff) { std::memcpy(Buff, &V, sizeof(V)); }
----------------
sethp wrote:
There's a tradeoff to be made, here: booleans are among the primitive types that have padding bits, though I believe them to be by far the most commonly used (other contenders include `long double` and `_BitInt(4)`).
The wrinkle is something like this:
```c++
constexpr bool b() {
return std::bit_cast<bool, uint8_t>(0x02);
}
int main() {
if constexpr (b() == 0)
return b();
return 1;
}
```
For that sample, `gcc` produces a binary that exits with code 2: https://compiler-explorer.com/z/jes71o85h , and current clang refuses to compile with a `note: value 2 cannot be represented in type 'bool'`. To preserve that behavior, I think that means here `bitcastToMemory` ought to be a fail-able operation that checks all bits above the LSB are zero.
There's another question about whether it's worth giving `bool` special treatment: should _only_ `Boolean::bitcastToMemory` be a fail-able operation? In other words, given
```c++
constexpr auto a = std::bit_cast<uint8_t>(false);
constexpr auto b = std::bit_cast<__int128_t>((long double)0);
constexpr auto c = std::bit_cast<bool>('\x02');
constexpr auto d = std::bit_cast<long double>((__int128_t)~0);
```
Right now, `a` & `d` succeed. `b` & `c` fail to produce constant values for different reasons: `c` because the evaluator sees that it'd lose information by allowing the bit-cast, and `b` because it doesn't want to check.
I see two fully consistent interpretations, here (considering both implementation & semantics):
1. `a` fails for the same reason as `b`, and then `Interp` is free to allow `c` & `d` as a simple memcpy + mask (to throw away any padding bits)
2. `a` and `b` both succeed, but then `c` and `d` must fail for the same reason.
Both are backwards-incompatible changes, though: code that used to compile with the old constant interpreter would fail under the new one. The third choice preserves backwards compatibility by holding `bool` out as the [only type whose values get checked for represent-ability](https://github.com/llvm/llvm-project/blob/2e0d3f81e4f5b623bb476bfac0278cfc6d1bd4bc/clang/lib/AST/ExprConstant.cpp#L7626-L7651). (NB: that's a link to the code from #74775 (the current implementation is subtly different, but the behavior is the same because the current evaluator doesn't [fully] support `_BitInt(X)`)
https://github.com/llvm/llvm-project/pull/68288
More information about the cfe-commits
mailing list