[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>
================
@@ -0,0 +1,452 @@
+//===--- InterpBitcast.cpp - Interpreter for the constexpr VM ---*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+#include "Boolean.h"
+#include "Interp.h"
+#include "PrimType.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/RecordLayout.h"
+#include "clang/Basic/Builtins.h"
+#include "clang/Basic/TargetInfo.h"
+#include "llvm/ADT/BitVector.h"
+
+namespace clang {
+namespace interp {
+
+/// Used to iterate over pointer fields.
+using DataFunc =
+ llvm::function_ref<bool(const Pointer &P, PrimType Ty, size_t BitOffset)>;
+
+#define BITCAST_TYPE_SWITCH(Expr, B) \
+ do { \
+ switch (Expr) { \
+ TYPE_SWITCH_CASE(PT_Sint8, B) \
+ TYPE_SWITCH_CASE(PT_Uint8, B) \
+ TYPE_SWITCH_CASE(PT_Sint16, B) \
+ TYPE_SWITCH_CASE(PT_Uint16, B) \
+ TYPE_SWITCH_CASE(PT_Sint32, B) \
+ TYPE_SWITCH_CASE(PT_Uint32, B) \
+ TYPE_SWITCH_CASE(PT_Sint64, B) \
+ TYPE_SWITCH_CASE(PT_Uint64, B) \
+ TYPE_SWITCH_CASE(PT_IntAP, B) \
+ TYPE_SWITCH_CASE(PT_IntAPS, B) \
+ TYPE_SWITCH_CASE(PT_Bool, B) \
+ default: \
+ llvm_unreachable("Unhandled bitcast type"); \
+ } \
+ } while (0)
+
+/// Float is a special case that sometimes needs the floating point semantics
+/// to be available.
+#define BITCAST_TYPE_SWITCH_WITH_FLOAT(Expr, B) \
+ do { \
+ switch (Expr) { \
+ TYPE_SWITCH_CASE(PT_Sint8, B) \
+ TYPE_SWITCH_CASE(PT_Uint8, B) \
+ TYPE_SWITCH_CASE(PT_Sint16, B) \
+ TYPE_SWITCH_CASE(PT_Uint16, B) \
+ TYPE_SWITCH_CASE(PT_Sint32, B) \
+ TYPE_SWITCH_CASE(PT_Uint32, B) \
+ TYPE_SWITCH_CASE(PT_Sint64, B) \
+ TYPE_SWITCH_CASE(PT_Uint64, B) \
+ TYPE_SWITCH_CASE(PT_IntAP, B) \
+ TYPE_SWITCH_CASE(PT_IntAPS, B) \
+ TYPE_SWITCH_CASE(PT_Bool, B) \
+ TYPE_SWITCH_CASE(PT_Float, B) \
+ default: \
+ llvm_unreachable("Unhandled bitcast type"); \
+ } \
+ } while (0)
+
+/// Rotate things around for big endian targets.
+static void swapBytes(std::byte *M, size_t N) {
+ for (size_t I = 0; I != (N / 2); ++I)
+ std::swap(M[I], M[N - 1 - I]);
+}
+
+/// Track what bits have been initialized to known values and which ones
+/// have indeterminate value.
+/// All offsets are in bits.
+struct BitTracker {
+ llvm::BitVector Initialized;
+ llvm::BitVector Data;
+
+ BitTracker() = default;
+
+ size_t size() const {
+ assert(Initialized.size() == Data.size());
+ return Initialized.size();
+ }
+
+ const std::byte *getBytes(size_t BitOffset, int a) {
+ assert(BitOffset % 8 == 0);
+ return reinterpret_cast<const std::byte *>(Data.getData().data()) +
+ (BitOffset / 8);
+ }
+
+ bool allInitialized(size_t Offset, size_t Size) const {
+ return Initialized.find_first_unset_in(Offset, Offset + Size) == -1;
+ }
+
+ bool allInitialized() const { return Initialized.all(); }
+
+ void pushData(const std::byte *data, size_t BitOffset, size_t BitWidth) {
+ assert(BitOffset >= Data.size());
+ Data.reserve(BitOffset + BitWidth);
+ Initialized.reserve(BitOffset + BitWidth);
+
+ // First, fill up the bit vector until BitOffset. The bits are all 0
+ // but we record them as indeterminate.
+ {
+ Data.resize(BitOffset, false);
+ Initialized.resize(BitOffset, false);
+ }
+
+ size_t BitsHandled = 0;
+ // Read all full bytes first
+ for (size_t I = 0; I != BitWidth / 8; ++I) {
+ for (unsigned X = 0; X != 8; ++X) {
+ Data.push_back((data[I] & std::byte(1 << X)) != std::byte{0});
+ Initialized.push_back(true);
+ ++BitsHandled;
+ }
+ }
----------------
sethp wrote:
I'll defer to your expertise as to whether this is appropriate, here, but it does seem a little unfortunate to do a bit-by-bit copy. I think the in-band type information means we're more or less stuck doing things field-by-field (because `Interp` will lay out a `struct { char c[3]; };` very differently from a `struct { char c; short s; }`?), as with the existing ExprConstant interpreter, but my intuition says that I ought to expect a `uint64_t` field to take roughly 8x as long to copy as a `char` with this implementation, despite both of them fitting within a machine word on my 64-bit system. Does my thinking match yours, here?
https://github.com/llvm/llvm-project/pull/68288
More information about the cfe-commits
mailing list