[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