Skip to content

Commit 1ef9938

Browse files
committed
[CIR] Implement constant expression bitfield initialization
This adds support for emitting bitfields in constant aggregatge initializers with the exception of single element records and decomposing large bitfields into smaller constants.
1 parent 71130bf commit 1ef9938

File tree

2 files changed

+187
-17
lines changed

2 files changed

+187
-17
lines changed

clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp

Lines changed: 134 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,9 @@ class ConstantAggregateBuilder : private ConstantAggregateBuilderUtils {
118118
/// non-packed LLVM struct will give the correct layout.
119119
bool naturalLayout = true;
120120

121+
bool split(size_t index, CharUnits hint);
122+
std::optional<size_t> splitAt(CharUnits pos);
123+
121124
static mlir::Attribute buildFrom(CIRGenModule &cgm, ArrayRef<Element> elems,
122125
CharUnits startOffset, CharUnits size,
123126
bool naturalLayout, mlir::Type desiredTy,
@@ -137,6 +140,10 @@ class ConstantAggregateBuilder : private ConstantAggregateBuilderUtils {
137140
/// Update or overwrite the bits starting at \p offsetInBits with \p bits.
138141
bool addBits(llvm::APInt bits, uint64_t offsetInBits, bool allowOverwrite);
139142

143+
/// Attempt to condense the value starting at \p offset to a constant of type
144+
/// \p desiredTy.
145+
void condense(CharUnits offset, mlir::Type desiredTy);
146+
140147
/// Produce a constant representing the entire accumulated value, ideally of
141148
/// the specified type. If \p allowOversized, the constant might be larger
142149
/// than implied by \p desiredTy (eg, if there is a flexible array member).
@@ -219,8 +226,54 @@ bool ConstantAggregateBuilder::addBits(llvm::APInt bits, uint64_t offsetInBits,
219226
add(cir::IntAttr::get(charTy, bitsThisChar), offsetInChars,
220227
allowOverwrite);
221228
} else {
222-
cgm.errorNYI("partial byte bitfield updates");
223-
return false;
229+
// Partial byte: update the existing integer if there is one. If we
230+
// can't split out a 1-CharUnit range to update, then we can't add
231+
// these bits and fail the entire constant emission.
232+
std::optional<size_t> firstElemToUpdate = splitAt(offsetInChars);
233+
if (!firstElemToUpdate)
234+
return false;
235+
std::optional<size_t> lastElemToUpdate =
236+
splitAt(offsetInChars + CharUnits::One());
237+
if (!lastElemToUpdate)
238+
return false;
239+
assert(*lastElemToUpdate - *firstElemToUpdate < 2 &&
240+
"should have at most one element covering one byte");
241+
242+
// Figure out which bits we want and discard the rest.
243+
llvm::APInt updateMask(charWidth, 0);
244+
if (cgm.getDataLayout().isBigEndian())
245+
updateMask.setBits(charWidth - offsetWithinChar - wantedBits,
246+
charWidth - offsetWithinChar);
247+
else
248+
updateMask.setBits(offsetWithinChar, offsetWithinChar + wantedBits);
249+
bitsThisChar &= updateMask;
250+
bool isNull = false;
251+
if (*firstElemToUpdate < elements.size()) {
252+
auto firstEltToUpdate =
253+
mlir::dyn_cast<cir::IntAttr>(elements[*firstElemToUpdate].element);
254+
isNull = firstEltToUpdate && firstEltToUpdate.isNullValue();
255+
}
256+
257+
if (*firstElemToUpdate == *lastElemToUpdate || isNull) {
258+
// All existing bits are either zero or undef.
259+
add(cir::IntAttr::get(charTy, bitsThisChar), offsetInChars,
260+
/*allowOverwrite*/ true);
261+
} else {
262+
cir::IntAttr ci =
263+
mlir::dyn_cast<cir::IntAttr>(elements[*firstElemToUpdate].element);
264+
// In order to perform a partial update, we need the existing bitwise
265+
// value, which we can only extract for a constant int.
266+
if (!ci)
267+
return false;
268+
// Because this is a 1-CharUnit range, the constant occupying it must
269+
// be exactly one CharUnit wide.
270+
assert(ci.getBitWidth() == charWidth && "splitAt failed");
271+
assert((!(ci.getValue() & updateMask) || allowOverwrite) &&
272+
"unexpectedly overwriting bitfield");
273+
bitsThisChar |= (ci.getValue() & ~updateMask);
274+
elements[*firstElemToUpdate].element =
275+
cir::IntAttr::get(charTy, bitsThisChar);
276+
}
224277
}
225278

226279
// Stop if we've added all the bits.
@@ -239,6 +292,85 @@ bool ConstantAggregateBuilder::addBits(llvm::APInt bits, uint64_t offsetInBits,
239292
return true;
240293
}
241294

295+
/// Returns a position within elements such that all elements
296+
/// before the returned index end before pos and all elements at or after
297+
/// the returned index begin at or after pos. Splits elements as necessary
298+
/// to ensure this. Returns std::nullopt if we find something we can't split.
299+
std::optional<size_t> ConstantAggregateBuilder::splitAt(CharUnits pos) {
300+
if (pos >= size)
301+
return elements.size();
302+
303+
while (true) {
304+
// Find the first element that starts after pos.
305+
Element *iter =
306+
llvm::upper_bound(elements, pos, [](CharUnits pos, const Element &elt) {
307+
return pos < elt.offset;
308+
});
309+
310+
if (iter == elements.begin())
311+
return 0;
312+
313+
size_t index = iter - elements.begin() - 1;
314+
const Element &elt = elements[index];
315+
316+
// If we already have an element starting at pos, we're done.
317+
if (elt.offset == pos)
318+
return index;
319+
320+
// Check for overlap with the element that starts before pos.
321+
CharUnits eltEnd = elt.offset + getSize(elt.element);
322+
if (eltEnd <= pos)
323+
return index + 1;
324+
325+
// Try to decompose it into smaller constants.
326+
if (!split(index, pos))
327+
return std::nullopt;
328+
}
329+
}
330+
331+
/// Split the constant at index, if possible. Return true if we did.
332+
/// Hint indicates the location at which we'd like to split, but may be
333+
/// ignored.
334+
bool ConstantAggregateBuilder::split(size_t index, CharUnits hint) {
335+
cgm.errorNYI("split constant at index");
336+
return false;
337+
}
338+
339+
void ConstantAggregateBuilder::condense(CharUnits offset, mlir::Type desiredTy) {
340+
CharUnits desiredSize = getSize(desiredTy);
341+
342+
std::optional<size_t> firstElemToReplace = splitAt(offset);
343+
if (!firstElemToReplace)
344+
return;
345+
size_t first = *firstElemToReplace;
346+
347+
std::optional<size_t> lastElemToReplace = splitAt(offset + desiredSize);
348+
if (!lastElemToReplace)
349+
return;
350+
size_t last = *lastElemToReplace;
351+
352+
size_t length = last - first;
353+
if (length == 0)
354+
return;
355+
356+
if (length == 1 && elements[first].offset == offset &&
357+
getSize(elements[first].element) == desiredSize) {
358+
cgm.errorNYI("re-wrapping single element records");
359+
return;
360+
}
361+
362+
// Build a new constant from the elements in the range.
363+
SmallVector<Element> subElems(elements.begin() + first,
364+
elements.begin() + last);
365+
mlir::Attribute replacement =
366+
buildFrom(cgm, subElems, offset, desiredSize,
367+
/*naturalLayout=*/false, desiredTy, false);
368+
369+
// Replace the range with the condensed constant.
370+
Element newElt(mlir::cast<mlir::TypedAttr>(replacement), offset);
371+
replace(elements, first, last, {newElt});
372+
}
373+
242374
mlir::Attribute
243375
ConstantAggregateBuilder::buildFrom(CIRGenModule &cgm, ArrayRef<Element> elems,
244376
CharUnits startOffset, CharUnits size,

clang/test/CIR/CodeGen/constant-inits.cpp

Lines changed: 53 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -30,21 +30,41 @@ struct simple {
3030
int a, b;
3131
};
3232

33-
struct Bitfields {
33+
// Byte-aligned bitfields
34+
struct byte_aligned_bitfields {
3435
unsigned int a : 8;
3536
unsigned int b : 8;
3637
unsigned int c : 16;
3738
};
3839

39-
struct SignedBitfields {
40+
struct signed_byte_aligned_bitfields {
4041
int x : 8;
4142
int y : 8;
4243
};
4344

44-
struct ByteBitfields {
45+
struct single_byte_bitfield {
4546
unsigned char a : 8;
4647
};
4748

49+
// Partial bitfields (sub-byte)
50+
struct partial_bitfields {
51+
unsigned int a : 3;
52+
unsigned int b : 5;
53+
unsigned int c : 8;
54+
};
55+
56+
struct signed_partial_bitfields {
57+
int x : 4;
58+
int y : 4;
59+
};
60+
61+
struct mixed_partial_bitfields {
62+
unsigned char a : 1;
63+
unsigned char b : 1;
64+
unsigned char c : 1;
65+
unsigned char d : 5;
66+
};
67+
4868
void function() {
4969
constexpr static empty e;
5070

@@ -70,9 +90,15 @@ void function() {
7090
s, {1111, 2222}, s
7191
};
7292

73-
constexpr static Bitfields bf1 = {0xFF, 0xAA, 0x1234};
74-
constexpr static SignedBitfields bf2 = {-1, 127};
75-
constexpr static ByteBitfields bf3 = {42};
93+
// Byte-aligned bitfield tests
94+
constexpr static byte_aligned_bitfields ba_bf1 = {0xFF, 0xAA, 0x1234};
95+
constexpr static signed_byte_aligned_bitfields ba_bf2 = {-1, 127};
96+
constexpr static single_byte_bitfield ba_bf3 = {42};
97+
98+
// Partial bitfield tests
99+
constexpr static partial_bitfields p_bf1 = {1, 2, 3};
100+
constexpr static signed_partial_bitfields p_bf2 = {-1, 7};
101+
constexpr static mixed_partial_bitfields p_bf3 = {1, 0, 1, 15};
76102
}
77103

78104
// CIR-DAG: cir.global "private" internal dso_local @_ZZ8functionvE1e = #cir.zero : !rec_empty
@@ -102,9 +128,15 @@ void function() {
102128
// CIR-DAG-SAME: #cir.zero : !rec_packed_and_aligned
103129
// CIR-DAG-SAME: ]> : !cir.array<!rec_packed_and_aligned x 2>
104130

105-
// CIR-DAG: cir.global "private" internal dso_local @_ZZ8functionvE3bf1 = #cir.const_record<{#cir.int<255> : !u8i, #cir.int<170> : !u8i, #cir.int<52> : !u8i, #cir.int<18> : !u8i}> : !rec_anon_struct
106-
// CIR-DAG: cir.global "private" internal dso_local @_ZZ8functionvE3bf2 = #cir.const_record<{#cir.int<255> : !u8i, #cir.int<127> : !u8i, #cir.const_array<[#cir.zero : !u8i, #cir.zero : !u8i]> : !cir.array<!u8i x 2>}> : !rec_anon_struct
107-
// CIR-DAG: cir.global "private" internal dso_local @_ZZ8functionvE3bf3 = #cir.const_record<{#cir.int<42> : !u8i}> : !rec_ByteBitfields
131+
// Byte-aligned bitfield checks
132+
// CIR-DAG: cir.global "private" internal dso_local @_ZZ8functionvE6ba_bf1 = #cir.const_record<{#cir.int<255> : !u8i, #cir.int<170> : !u8i, #cir.int<52> : !u8i, #cir.int<18> : !u8i}> : !rec_anon_struct
133+
// CIR-DAG: cir.global "private" internal dso_local @_ZZ8functionvE6ba_bf2 = #cir.const_record<{#cir.int<255> : !u8i, #cir.int<127> : !u8i, #cir.const_array<[#cir.zero : !u8i, #cir.zero : !u8i]> : !cir.array<!u8i x 2>}> : !rec_anon_struct
134+
// CIR-DAG: cir.global "private" internal dso_local @_ZZ8functionvE6ba_bf3 = #cir.const_record<{#cir.int<42> : !u8i}> : !rec_single_byte_bitfield
135+
136+
// Partial bitfield checks
137+
// CIR-DAG: cir.global "private" internal dso_local @_ZZ8functionvE5p_bf1 = #cir.const_record<{#cir.int<17> : !u8i, #cir.int<3> : !u8i, #cir.const_array<[#cir.zero : !u8i, #cir.zero : !u8i]> : !cir.array<!u8i x 2>}> : !rec_anon_struct
138+
// CIR-DAG: cir.global "private" internal dso_local @_ZZ8functionvE5p_bf2 = #cir.const_record<{#cir.int<127> : !u8i, #cir.const_array<[#cir.zero : !u8i, #cir.zero : !u8i, #cir.zero : !u8i]> : !cir.array<!u8i x 3>}> : !rec_signed_partial_bitfields
139+
// CIR-DAG: cir.global "private" internal dso_local @_ZZ8functionvE5p_bf3 = #cir.const_record<{#cir.int<125> : !u8i}> : !rec_mixed_partial_bitfields
108140

109141
// CIR-LABEL: cir.func dso_local @_Z8functionv()
110142
// CIR: cir.return
@@ -119,9 +151,12 @@ void function() {
119151
// LLVM-DAG: @_ZZ8functionvE3paa = internal global %struct.packed_and_aligned <{ i16 1, i8 2, float 3.000000e+00, i8 0 }>
120152
// LLVM-DAG: @_ZZ8functionvE5array = internal global [2 x %struct.Point] [%struct.Point { i32 123, i32 456, [3 x i8] c"\0B\16!", i32 789 }, %struct.Point { i32 10, i32 20, [3 x i8] zeroinitializer, i32 40 }]
121153
// LLVM-DAG: @_ZZ8functionvE9paa_array = internal global [2 x %struct.packed_and_aligned] [%struct.packed_and_aligned <{ i16 1, i8 2, float 3.000000e+00, i8 0 }>, %struct.packed_and_aligned zeroinitializer]
122-
// LLVM-DAG: @_ZZ8functionvE3bf1 = internal global { i8, i8, i8, i8 } { i8 -1, i8 -86, i8 52, i8 18 }
123-
// LLVM-DAG: @_ZZ8functionvE3bf2 = internal global { i8, i8, [2 x i8] } { i8 -1, i8 127, [2 x i8] zeroinitializer }
124-
// LLVM-DAG: @_ZZ8functionvE3bf3 = internal global %struct.ByteBitfields { i8 42 }
154+
// LLVM-DAG: @_ZZ8functionvE6ba_bf1 = internal global { i8, i8, i8, i8 } { i8 -1, i8 -86, i8 52, i8 18 }
155+
// LLVM-DAG: @_ZZ8functionvE6ba_bf2 = internal global { i8, i8, [2 x i8] } { i8 -1, i8 127, [2 x i8] zeroinitializer }
156+
// LLVM-DAG: @_ZZ8functionvE6ba_bf3 = internal global %struct.single_byte_bitfield { i8 42 }
157+
// LLVM-DAG: @_ZZ8functionvE5p_bf1 = internal global { i8, i8, [2 x i8] } { i8 17, i8 3, [2 x i8] zeroinitializer }
158+
// LLVM-DAG: @_ZZ8functionvE5p_bf2 = internal global %struct.signed_partial_bitfields { i8 127, [3 x i8] zeroinitializer }
159+
// LLVM-DAG: @_ZZ8functionvE5p_bf3 = internal global %struct.mixed_partial_bitfields { i8 125 }
125160

126161
// LLVM-LABEL: define{{.*}} void @_Z8functionv
127162
// LLVM: ret void
@@ -136,9 +171,12 @@ void function() {
136171
// OGCG-DAG: @_ZZ8functionvE3paa = internal constant %struct.packed_and_aligned <{ i16 1, i8 2, float 3.000000e+00, i8 undef }>
137172
// OGCG-DAG: @_ZZ8functionvE5array = internal constant [2 x %struct.Point] [%struct.Point { i32 123, i32 456, [3 x i8] c"\0B\16!", i32 789 }, %struct.Point { i32 10, i32 20, [3 x i8] zeroinitializer, i32 40 }]
138173
// OGCG-DAG: @_ZZ8functionvE9paa_array = internal constant [2 x %struct.packed_and_aligned] [%struct.packed_and_aligned <{ i16 1, i8 2, float 3.000000e+00, i8 undef }>, %struct.packed_and_aligned <{ i16 0, i8 0, float 0.000000e+00, i8 undef }>]
139-
// OGCG-DAG: @_ZZ8functionvE3bf1 = internal constant { i8, i8, i8, i8 } { i8 -1, i8 -86, i8 52, i8 18 }
140-
// OGCG-DAG: @_ZZ8functionvE3bf2 = internal constant { i8, i8, [2 x i8] } { i8 -1, i8 127, [2 x i8] undef }
141-
// OGCG-DAG: @_ZZ8functionvE3bf3 = internal constant %struct.ByteBitfields { i8 42 }
174+
// OGCG-DAG: @_ZZ8functionvE6ba_bf1 = internal constant { i8, i8, i8, i8 } { i8 -1, i8 -86, i8 52, i8 18 }
175+
// OGCG-DAG: @_ZZ8functionvE6ba_bf2 = internal constant { i8, i8, [2 x i8] } { i8 -1, i8 127, [2 x i8] undef }
176+
// OGCG-DAG: @_ZZ8functionvE6ba_bf3 = internal constant %struct.single_byte_bitfield { i8 42 }
177+
// OGCG-DAG: @_ZZ8functionvE5p_bf1 = internal constant { i8, i8, [2 x i8] } { i8 17, i8 3, [2 x i8] undef }
178+
// OGCG-DAG: @_ZZ8functionvE5p_bf2 = internal constant %struct.signed_partial_bitfields { i8 127, [3 x i8] undef }
179+
// OGCG-DAG: @_ZZ8functionvE5p_bf3 = internal constant %struct.mixed_partial_bitfields { i8 125 }
142180

143181
// OGCG-LABEL: define{{.*}} void @_Z8functionv
144182
// OGCG: ret void

0 commit comments

Comments
 (0)