LLVM  13.0.0git
Endian.h
Go to the documentation of this file.
1 //===- Endian.h - Utilities for IO with endian specific data ----*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file declares generic functions to read and write endian specific data.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #ifndef LLVM_SUPPORT_ENDIAN_H
14 #define LLVM_SUPPORT_ENDIAN_H
15 
16 #include "llvm/Support/Compiler.h"
18 #include <cassert>
19 #include <cstddef>
20 #include <cstdint>
21 #include <cstring>
22 #include <type_traits>
23 
24 namespace llvm {
25 namespace support {
26 
28 
29 // These are named values for common alignments.
30 enum {aligned = 0, unaligned = 1};
31 
32 namespace detail {
33 
34 /// ::value is either alignment, or alignof(T) if alignment is 0.
35 template<class T, int alignment>
36 struct PickAlignment {
37  enum { value = alignment == 0 ? alignof(T) : alignment };
38 };
39 
40 } // end namespace detail
41 
42 namespace endian {
43 
45  return sys::IsBigEndianHost ? big : little;
46 }
47 
48 template <typename value_type>
49 inline value_type byte_swap(value_type value, endianness endian) {
50  if ((endian != native) && (endian != system_endianness()))
51  sys::swapByteOrder(value);
52  return value;
53 }
54 
55 /// Swap the bytes of value to match the given endianness.
56 template<typename value_type, endianness endian>
57 inline value_type byte_swap(value_type value) {
58  return byte_swap(value, endian);
59 }
60 
61 /// Read a value of a particular endianness from memory.
62 template <typename value_type, std::size_t alignment>
63 inline value_type read(const void *memory, endianness endian) {
64  value_type ret;
65 
66  memcpy(&ret,
69  sizeof(value_type));
70  return byte_swap<value_type>(ret, endian);
71 }
72 
73 template<typename value_type,
75  std::size_t alignment>
76 inline value_type read(const void *memory) {
77  return read<value_type, alignment>(memory, endian);
78 }
79 
80 /// Read a value of a particular endianness from a buffer, and increment the
81 /// buffer past that value.
82 template <typename value_type, std::size_t alignment, typename CharT>
83 inline value_type readNext(const CharT *&memory, endianness endian) {
84  value_type ret = read<value_type, alignment>(memory, endian);
85  memory += sizeof(value_type);
86  return ret;
87 }
88 
89 template<typename value_type, endianness endian, std::size_t alignment,
90  typename CharT>
91 inline value_type readNext(const CharT *&memory) {
92  return readNext<value_type, alignment, CharT>(memory, endian);
93 }
94 
95 /// Write a value to memory with a particular endianness.
96 template <typename value_type, std::size_t alignment>
97 inline void write(void *memory, value_type value, endianness endian) {
98  value = byte_swap<value_type>(value, endian);
101  &value, sizeof(value_type));
102 }
103 
104 template<typename value_type,
106  std::size_t alignment>
107 inline void write(void *memory, value_type value) {
108  write<value_type, alignment>(memory, value, endian);
109 }
110 
111 template <typename value_type>
112 using make_unsigned_t = std::make_unsigned_t<value_type>;
113 
114 /// Read a value of a particular endianness from memory, for a location
115 /// that starts at the given bit offset within the first byte.
116 template <typename value_type, endianness endian, std::size_t alignment>
117 inline value_type readAtBitAlignment(const void *memory, uint64_t startBit) {
118  assert(startBit < 8);
119  if (startBit == 0)
120  return read<value_type, endian, alignment>(memory);
121  else {
122  // Read two values and compose the result from them.
123  value_type val[2];
124  memcpy(&val[0],
127  sizeof(value_type) * 2);
128  val[0] = byte_swap<value_type, endian>(val[0]);
129  val[1] = byte_swap<value_type, endian>(val[1]);
130 
131  // Shift bits from the lower value into place.
132  make_unsigned_t<value_type> lowerVal = val[0] >> startBit;
133  // Mask off upper bits after right shift in case of signed type.
134  make_unsigned_t<value_type> numBitsFirstVal =
135  (sizeof(value_type) * 8) - startBit;
136  lowerVal &= ((make_unsigned_t<value_type>)1 << numBitsFirstVal) - 1;
137 
138  // Get the bits from the upper value.
139  make_unsigned_t<value_type> upperVal =
140  val[1] & (((make_unsigned_t<value_type>)1 << startBit) - 1);
141  // Shift them in to place.
142  upperVal <<= numBitsFirstVal;
143 
144  return lowerVal | upperVal;
145  }
146 }
147 
148 /// Write a value to memory with a particular endianness, for a location
149 /// that starts at the given bit offset within the first byte.
150 template <typename value_type, endianness endian, std::size_t alignment>
151 inline void writeAtBitAlignment(void *memory, value_type value,
152  uint64_t startBit) {
153  assert(startBit < 8);
154  if (startBit == 0)
155  write<value_type, endian, alignment>(memory, value);
156  else {
157  // Read two values and shift the result into them.
158  value_type val[2];
159  memcpy(&val[0],
162  sizeof(value_type) * 2);
163  val[0] = byte_swap<value_type, endian>(val[0]);
164  val[1] = byte_swap<value_type, endian>(val[1]);
165 
166  // Mask off any existing bits in the upper part of the lower value that
167  // we want to replace.
168  val[0] &= ((make_unsigned_t<value_type>)1 << startBit) - 1;
169  make_unsigned_t<value_type> numBitsFirstVal =
170  (sizeof(value_type) * 8) - startBit;
171  make_unsigned_t<value_type> lowerVal = value;
172  if (startBit > 0) {
173  // Mask off the upper bits in the new value that are not going to go into
174  // the lower value. This avoids a left shift of a negative value, which
175  // is undefined behavior.
176  lowerVal &= (((make_unsigned_t<value_type>)1 << numBitsFirstVal) - 1);
177  // Now shift the new bits into place
178  lowerVal <<= startBit;
179  }
180  val[0] |= lowerVal;
181 
182  // Mask off any existing bits in the lower part of the upper value that
183  // we want to replace.
184  val[1] &= ~(((make_unsigned_t<value_type>)1 << startBit) - 1);
185  // Next shift the bits that go into the upper value into position.
186  make_unsigned_t<value_type> upperVal = value >> numBitsFirstVal;
187  // Mask off upper bits after right shift in case of signed type.
188  upperVal &= ((make_unsigned_t<value_type>)1 << startBit) - 1;
189  val[1] |= upperVal;
190 
191  // Finally, rewrite values.
192  val[0] = byte_swap<value_type, endian>(val[0]);
193  val[1] = byte_swap<value_type, endian>(val[1]);
196  &val[0], sizeof(value_type) * 2);
197  }
198 }
199 
200 } // end namespace endian
201 
202 namespace detail {
203 
204 template <typename ValueType, endianness Endian, std::size_t Alignment,
205  std::size_t ALIGN = PickAlignment<ValueType, Alignment>::value>
208  static constexpr endianness endian = Endian;
209  static constexpr std::size_t alignment = Alignment;
210 
212 
214 
215  operator value_type() const {
216  return endian::read<value_type, endian, alignment>(
217  (const void*)Value.buffer);
218  }
219 
220  void operator=(value_type newValue) {
221  endian::write<value_type, endian, alignment>(
222  (void*)Value.buffer, newValue);
223  }
224 
226  *this = *this + newValue;
227  return *this;
228  }
229 
231  *this = *this - newValue;
232  return *this;
233  }
234 
236  *this = *this | newValue;
237  return *this;
238  }
239 
241  *this = *this & newValue;
242  return *this;
243  }
244 
245 private:
246  struct {
247  alignas(ALIGN) char buffer[sizeof(value_type)];
248  } Value;
249 
250 public:
251  struct ref {
252  explicit ref(void *Ptr) : Ptr(Ptr) {}
253 
254  operator value_type() const {
255  return endian::read<value_type, endian, alignment>(Ptr);
256  }
257 
258  void operator=(value_type NewValue) {
259  endian::write<value_type, endian, alignment>(Ptr, NewValue);
260  }
261 
262  private:
263  void *Ptr;
264  };
265 };
266 
267 } // end namespace detail
268 
269 using ulittle16_t =
271 using ulittle32_t =
273 using ulittle64_t =
275 
276 using little16_t =
278 using little32_t =
280 using little64_t =
282 
283 using aligned_ulittle16_t =
285 using aligned_ulittle32_t =
287 using aligned_ulittle64_t =
289 
290 using aligned_little16_t =
292 using aligned_little32_t =
294 using aligned_little64_t =
296 
297 using ubig16_t =
299 using ubig32_t =
301 using ubig64_t =
303 
304 using big16_t =
306 using big32_t =
308 using big64_t =
310 
311 using aligned_ubig16_t =
313 using aligned_ubig32_t =
315 using aligned_ubig64_t =
317 
318 using aligned_big16_t =
320 using aligned_big32_t =
322 using aligned_big64_t =
324 
325 using unaligned_uint16_t =
327 using unaligned_uint32_t =
329 using unaligned_uint64_t =
331 
332 using unaligned_int16_t =
334 using unaligned_int32_t =
336 using unaligned_int64_t =
338 
339 template <typename T>
341 template <typename T>
343 
344 template <typename T>
345 using aligned_little_t =
347 template <typename T>
349 
350 namespace endian {
351 
352 template <typename T> inline T read(const void *P, endianness E) {
353  return read<T, unaligned>(P, E);
354 }
355 
356 template <typename T, endianness E> inline T read(const void *P) {
358 }
359 
360 inline uint16_t read16(const void *P, endianness E) {
361  return read<uint16_t>(P, E);
362 }
363 inline uint32_t read32(const void *P, endianness E) {
364  return read<uint32_t>(P, E);
365 }
366 inline uint64_t read64(const void *P, endianness E) {
367  return read<uint64_t>(P, E);
368 }
369 
370 template <endianness E> inline uint16_t read16(const void *P) {
371  return read<uint16_t, E>(P);
372 }
373 template <endianness E> inline uint32_t read32(const void *P) {
374  return read<uint32_t, E>(P);
375 }
376 template <endianness E> inline uint64_t read64(const void *P) {
377  return read<uint64_t, E>(P);
378 }
379 
380 inline uint16_t read16le(const void *P) { return read16<little>(P); }
381 inline uint32_t read32le(const void *P) { return read32<little>(P); }
382 inline uint64_t read64le(const void *P) { return read64<little>(P); }
383 inline uint16_t read16be(const void *P) { return read16<big>(P); }
384 inline uint32_t read32be(const void *P) { return read32<big>(P); }
385 inline uint64_t read64be(const void *P) { return read64<big>(P); }
386 
387 template <typename T> inline void write(void *P, T V, endianness E) {
388  write<T, unaligned>(P, V, E);
389 }
390 
391 template <typename T, endianness E> inline void write(void *P, T V) {
393 }
394 
395 inline void write16(void *P, uint16_t V, endianness E) {
396  write<uint16_t>(P, V, E);
397 }
398 inline void write32(void *P, uint32_t V, endianness E) {
399  write<uint32_t>(P, V, E);
400 }
401 inline void write64(void *P, uint64_t V, endianness E) {
402  write<uint64_t>(P, V, E);
403 }
404 
405 template <endianness E> inline void write16(void *P, uint16_t V) {
406  write<uint16_t, E>(P, V);
407 }
408 template <endianness E> inline void write32(void *P, uint32_t V) {
409  write<uint32_t, E>(P, V);
410 }
411 template <endianness E> inline void write64(void *P, uint64_t V) {
412  write<uint64_t, E>(P, V);
413 }
414 
415 inline void write16le(void *P, uint16_t V) { write16<little>(P, V); }
416 inline void write32le(void *P, uint32_t V) { write32<little>(P, V); }
417 inline void write64le(void *P, uint64_t V) { write64<little>(P, V); }
418 inline void write16be(void *P, uint16_t V) { write16<big>(P, V); }
419 inline void write32be(void *P, uint32_t V) { write32<big>(P, V); }
420 inline void write64be(void *P, uint64_t V) { write64<big>(P, V); }
421 
422 } // end namespace endian
423 
424 } // end namespace support
425 } // end namespace llvm
426 
427 #endif // LLVM_SUPPORT_ENDIAN_H
llvm
Definition: AllocatorList.h:23
llvm::sys::IsBigEndianHost
constexpr bool IsBigEndianHost
Definition: SwapByteOrder.h:98
llvm::support::endian::read16
uint16_t read16(const void *P, endianness E)
Definition: Endian.h:360
llvm::sys::swapByteOrder
void swapByteOrder(T &Value)
Definition: SwapByteOrder.h:158
llvm::support::endian::write32be
void write32be(void *P, uint32_t V)
Definition: Endian.h:419
llvm::support::endian::read32
uint32_t read32(const void *P, endianness E)
Definition: Endian.h:363
llvm::support::detail::packed_endian_specific_integral::ref
Definition: Endian.h:251
llvm::support::detail::packed_endian_specific_integral
Definition: Endian.h:206
P
This currently compiles esp xmm0 movsd esp eax eax esp ret We should use not the dag combiner This is because dagcombine2 needs to be able to see through the X86ISD::Wrapper which DAGCombine can t really do The code for turning x load into a single vector load is target independent and should be moved to the dag combiner The code for turning x load into a vector load can only handle a direct load from a global or a direct load from the stack It should be generalized to handle any load from P
Definition: README-SSE.txt:411
llvm::support::endian::readAtBitAlignment
value_type readAtBitAlignment(const void *memory, uint64_t startBit)
Read a value of a particular endianness from memory, for a location that starts at the given bit offs...
Definition: Endian.h:117
llvm::support::endian::read16le
uint16_t read16le(const void *P)
Definition: Endian.h:380
llvm::support::endian::write32le
void write32le(void *P, uint32_t V)
Definition: Endian.h:416
SwapByteOrder.h
llvm::support::detail::packed_endian_specific_integral::ref::ref
ref(void *Ptr)
Definition: Endian.h:252
llvm::support::endian::write16le
void write16le(void *P, uint16_t V)
Definition: Endian.h:415
T
#define T
Definition: Mips16ISelLowering.cpp:341
ret
to esp esp setne al movzbw ax esp setg cl movzbw cx cmove cx cl jne LBB1_2 esp ret(also really horrible code on ppc). This is due to the expand code for 64-bit compares. GCC produces multiple branches
llvm::support::endian::read32be
uint32_t read32be(const void *P)
Definition: Endian.h:384
llvm::support::endian::read64be
uint64_t read64be(const void *P)
Definition: Endian.h:385
llvm::support::detail::PickAlignment::value
@ value
Definition: Endian.h:37
llvm::support::endian
Definition: Endian.h:42
llvm::support::detail::packed_endian_specific_integral::operator+=
packed_endian_specific_integral & operator+=(value_type newValue)
Definition: Endian.h:225
llvm::support::endian::write32
void write32(void *P, uint32_t V, endianness E)
Definition: Endian.h:398
E
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
llvm::support::little
@ little
Definition: Endian.h:27
llvm::support::detail::packed_endian_specific_integral::packed_endian_specific_integral
packed_endian_specific_integral()=default
llvm::support::unaligned
@ unaligned
Definition: Endian.h:30
llvm::support::endian::write
void write(void *memory, value_type value, endianness endian)
Write a value to memory with a particular endianness.
Definition: Endian.h:97
val
The initial backend is deliberately restricted to z10 We should add support for later architectures at some point If an asm ties an i32 r result to an i64 the input will be treated as an leaving the upper bits uninitialised For i64 store i32 val
Definition: README.txt:15
llvm::support::ulittle16_t
detail::packed_endian_specific_integral< uint16_t, little, unaligned > ulittle16_t
Definition: Endian.h:270
llvm::support::endian::write16
void write16(void *P, uint16_t V, endianness E)
Definition: Endian.h:395
assert
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
memcpy
<%struct.s * > cast struct s *S to sbyte *< sbyte * > sbyte uint cast struct s *agg result to sbyte *< sbyte * > sbyte uint cast struct s *memtmp to sbyte *< sbyte * > sbyte uint ret void llc ends up issuing two memcpy or custom lower memcpy(of small size) to be ldmia/stmia. I think option 2 is better but the current register allocator cannot allocate a chunk of registers at a time. A feasible temporary solution is to use specific physical registers at the lowering time for small(<
llvm::support::endian::readNext
value_type readNext(const CharT *&memory, endianness endian)
Read a value of a particular endianness from a buffer, and increment the buffer past that value.
Definition: Endian.h:83
llvm::support::endian::writeAtBitAlignment
void writeAtBitAlignment(void *memory, value_type value, uint64_t startBit)
Write a value to memory with a particular endianness, for a location that starts at the given bit off...
Definition: Endian.h:151
llvm::support::aligned
@ aligned
Definition: Endian.h:30
uint32_t
Compiler.h
llvm::support::endian::make_unsigned_t
std::make_unsigned_t< value_type > make_unsigned_t
Definition: Endian.h:112
llvm::ValueType
PointerUnion< const Value *, const PseudoSourceValue * > ValueType
Definition: ScheduleDAGInstrs.h:106
llvm::PointerUnion< const Value *, const PseudoSourceValue * >
llvm::support::detail::packed_endian_specific_integral::operator|=
packed_endian_specific_integral & operator|=(value_type newValue)
Definition: Endian.h:235
llvm::support::endian::write16be
void write16be(void *P, uint16_t V)
Definition: Endian.h:418
llvm::support::detail::packed_endian_specific_integral::operator=
void operator=(value_type newValue)
Definition: Endian.h:220
uint16_t
llvm::support::detail::packed_endian_specific_integral::packed_endian_specific_integral
packed_endian_specific_integral(value_type val)
Definition: Endian.h:213
llvm::support::detail::packed_endian_specific_integral::operator-=
packed_endian_specific_integral & operator-=(value_type newValue)
Definition: Endian.h:230
llvm::support::endian::read
value_type read(const void *memory, endianness endian)
Read a value of a particular endianness from memory.
Definition: Endian.h:63
llvm::support::endian::byte_swap
value_type byte_swap(value_type value, endianness endian)
Definition: Endian.h:49
llvm::support::endian::system_endianness
constexpr endianness system_endianness()
Definition: Endian.h:44
llvm::support::detail::packed_endian_specific_integral::ref::operator=
void operator=(value_type NewValue)
Definition: Endian.h:258
llvm::support::detail::packed_endian_specific_integral::alignment
static constexpr std::size_t alignment
Definition: Endian.h:209
llvm::support::detail::packed_endian_specific_integral< ProcessorArchitecture >::value_type
ProcessorArchitecture value_type
Definition: Endian.h:207
llvm::support::endian::read64
uint64_t read64(const void *P, endianness E)
Definition: Endian.h:366
llvm::support::endian::read64le
uint64_t read64le(const void *P)
Definition: Endian.h:382
llvm::support::endian::write64le
void write64le(void *P, uint64_t V)
Definition: Endian.h:417
llvm::support::endian::read32le
uint32_t read32le(const void *P)
Definition: Endian.h:381
llvm::support::endian::write64be
void write64be(void *P, uint64_t V)
Definition: Endian.h:420
support
Reimplement select in terms of SEL *We would really like to support but we need to prove that the add doesn t need to overflow between the two bit chunks *Implement pre post increment support(e.g. PR935) *Implement smarter const ant generation for binops with large immediates. A few ARMv6T2 ops should be pattern matched
Definition: README.txt:10
llvm::support::native
@ native
Definition: Endian.h:27
llvm::support::detail::PickAlignment
::value is either alignment, or alignof(T) if alignment is 0.
Definition: Endian.h:36
llvm::support::endianness
endianness
Definition: Endian.h:27
llvm::support::endian::write64
void write64(void *P, uint64_t V, endianness E)
Definition: Endian.h:401
llvm::support::detail::packed_endian_specific_integral::buffer
char buffer[sizeof(value_type)]
Definition: Endian.h:247
llvm::Value
LLVM Value Representation.
Definition: Value.h:75
llvm::support::big
@ big
Definition: Endian.h:27
LLVM_ASSUME_ALIGNED
#define LLVM_ASSUME_ALIGNED(p, a)
\macro LLVM_ASSUME_ALIGNED Returns a pointer with an assumed alignment.
Definition: Compiler.h:370
llvm::support::detail::packed_endian_specific_integral::operator&=
packed_endian_specific_integral & operator&=(value_type newValue)
Definition: Endian.h:240
llvm::support::endian::read16be
uint16_t read16be(const void *P)
Definition: Endian.h:383