@@ -118,6 +118,9 @@ class ConstantAggregateBuilder : private ConstantAggregateBuilderUtils {
118
118
// / non-packed LLVM struct will give the correct layout.
119
119
bool naturalLayout = true ;
120
120
121
+ bool split (size_t index, CharUnits hint);
122
+ std::optional<size_t > splitAt (CharUnits pos);
123
+
121
124
static mlir::Attribute buildFrom (CIRGenModule &cgm, ArrayRef<Element> elems,
122
125
CharUnits startOffset, CharUnits size,
123
126
bool naturalLayout, mlir::Type desiredTy,
@@ -137,6 +140,10 @@ class ConstantAggregateBuilder : private ConstantAggregateBuilderUtils {
137
140
// / Update or overwrite the bits starting at \p offsetInBits with \p bits.
138
141
bool addBits (llvm::APInt bits, uint64_t offsetInBits, bool allowOverwrite);
139
142
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
+
140
147
// / Produce a constant representing the entire accumulated value, ideally of
141
148
// / the specified type. If \p allowOversized, the constant might be larger
142
149
// / than implied by \p desiredTy (eg, if there is a flexible array member).
@@ -176,6 +183,195 @@ bool ConstantAggregateBuilder::add(mlir::TypedAttr typedAttr, CharUnits offset,
176
183
return false ;
177
184
}
178
185
186
+ bool ConstantAggregateBuilder::addBits (llvm::APInt bits, uint64_t offsetInBits,
187
+ bool allowOverwrite) {
188
+ const ASTContext &astContext = cgm.getASTContext ();
189
+ const uint64_t charWidth = astContext.getCharWidth ();
190
+ mlir::Type charTy = cgm.getBuilder ().getUIntNTy (charWidth);
191
+
192
+ // Offset of where we want the first bit to go within the bits of the
193
+ // current char.
194
+ unsigned offsetWithinChar = offsetInBits % charWidth;
195
+
196
+ // We split bit-fields up into individual bytes. Walk over the bytes and
197
+ // update them.
198
+ for (CharUnits offsetInChars =
199
+ astContext.toCharUnitsFromBits (offsetInBits - offsetWithinChar);
200
+ /* */ ; ++offsetInChars) {
201
+ // Number of bits we want to fill in this char.
202
+ unsigned wantedBits =
203
+ std::min ((uint64_t )bits.getBitWidth (), charWidth - offsetWithinChar);
204
+
205
+ // Get a char containing the bits we want in the right places. The other
206
+ // bits have unspecified values.
207
+ llvm::APInt bitsThisChar = bits;
208
+ if (bitsThisChar.getBitWidth () < charWidth)
209
+ bitsThisChar = bitsThisChar.zext (charWidth);
210
+ if (cgm.getDataLayout ().isBigEndian ()) {
211
+ // Figure out how much to shift by. We may need to left-shift if we have
212
+ // less than one byte of Bits left.
213
+ int shift = bits.getBitWidth () - charWidth + offsetWithinChar;
214
+ if (shift > 0 )
215
+ bitsThisChar.lshrInPlace (shift);
216
+ else if (shift < 0 )
217
+ bitsThisChar = bitsThisChar.shl (-shift);
218
+ } else {
219
+ bitsThisChar = bitsThisChar.shl (offsetWithinChar);
220
+ }
221
+ if (bitsThisChar.getBitWidth () > charWidth)
222
+ bitsThisChar = bitsThisChar.trunc (charWidth);
223
+
224
+ if (wantedBits == charWidth) {
225
+ // Got a full byte: just add it directly.
226
+ add (cir::IntAttr::get (charTy, bitsThisChar), offsetInChars,
227
+ allowOverwrite);
228
+ } else {
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
+ }
277
+ }
278
+
279
+ // Stop if we've added all the bits.
280
+ if (wantedBits == bits.getBitWidth ())
281
+ break ;
282
+
283
+ // Remove the consumed bits from Bits.
284
+ if (!cgm.getDataLayout ().isBigEndian ())
285
+ bits.lshrInPlace (wantedBits);
286
+ bits = bits.trunc (bits.getBitWidth () - wantedBits);
287
+
288
+ // The remaining bits go at the start of the following bytes.
289
+ offsetWithinChar = 0 ;
290
+ }
291
+
292
+ return true ;
293
+ }
294
+
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,
340
+ mlir::Type desiredTy) {
341
+ CharUnits desiredSize = getSize (desiredTy);
342
+
343
+ std::optional<size_t > firstElemToReplace = splitAt (offset);
344
+ if (!firstElemToReplace)
345
+ return ;
346
+ size_t first = *firstElemToReplace;
347
+
348
+ std::optional<size_t > lastElemToReplace = splitAt (offset + desiredSize);
349
+ if (!lastElemToReplace)
350
+ return ;
351
+ size_t last = *lastElemToReplace;
352
+
353
+ size_t length = last - first;
354
+ if (length == 0 )
355
+ return ;
356
+
357
+ if (length == 1 && elements[first].offset == offset &&
358
+ getSize (elements[first].element ) == desiredSize) {
359
+ cgm.errorNYI (" re-wrapping single element records" );
360
+ return ;
361
+ }
362
+
363
+ // Build a new constant from the elements in the range.
364
+ SmallVector<Element> subElems (elements.begin () + first,
365
+ elements.begin () + last);
366
+ mlir::Attribute replacement =
367
+ buildFrom (cgm, subElems, offset, desiredSize,
368
+ /* naturalLayout=*/ false , desiredTy, false );
369
+
370
+ // Replace the range with the condensed constant.
371
+ Element newElt (mlir::cast<mlir::TypedAttr>(replacement), offset);
372
+ replace (elements, first, last, {newElt});
373
+ }
374
+
179
375
mlir::Attribute
180
376
ConstantAggregateBuilder::buildFrom (CIRGenModule &cgm, ArrayRef<Element> elems,
181
377
CharUnits startOffset, CharUnits size,
@@ -301,6 +497,9 @@ class ConstRecordBuilder {
301
497
bool appendBytes (CharUnits fieldOffsetInChars, mlir::TypedAttr initCst,
302
498
bool allowOverwrite = false );
303
499
500
+ bool appendBitField (const FieldDecl *field, uint64_t fieldOffset,
501
+ cir::IntAttr ci, bool allowOverwrite = false );
502
+
304
503
bool build (InitListExpr *ile, bool allowOverwrite);
305
504
bool build (const APValue &val, const RecordDecl *rd, bool isPrimaryBase,
306
505
const CXXRecordDecl *vTableClass, CharUnits baseOffset);
@@ -325,6 +524,30 @@ bool ConstRecordBuilder::appendBytes(CharUnits fieldOffsetInChars,
325
524
return builder.add (initCst, startOffset + fieldOffsetInChars, allowOverwrite);
326
525
}
327
526
527
+ bool ConstRecordBuilder::appendBitField (const FieldDecl *field,
528
+ uint64_t fieldOffset, cir::IntAttr ci,
529
+ bool allowOverwrite) {
530
+ const CIRGenRecordLayout &rl =
531
+ cgm.getTypes ().getCIRGenRecordLayout (field->getParent ());
532
+ const CIRGenBitFieldInfo &info = rl.getBitFieldInfo (field);
533
+ llvm::APInt fieldValue = ci.getValue ();
534
+
535
+ // Promote the size of FieldValue if necessary
536
+ // FIXME: This should never occur, but currently it can because initializer
537
+ // constants are cast to bool, and because clang is not enforcing bitfield
538
+ // width limits.
539
+ if (info.size > fieldValue.getBitWidth ())
540
+ fieldValue = fieldValue.zext (info.size );
541
+
542
+ // Truncate the size of FieldValue to the bit field size.
543
+ if (info.size < fieldValue.getBitWidth ())
544
+ fieldValue = fieldValue.trunc (info.size );
545
+
546
+ return builder.addBits (fieldValue,
547
+ cgm.getASTContext ().toBits (startOffset) + fieldOffset,
548
+ allowOverwrite);
549
+ }
550
+
328
551
bool ConstRecordBuilder::build (InitListExpr *ile, bool allowOverwrite) {
329
552
RecordDecl *rd = ile->getType ()
330
553
->castAs <clang::RecordType>()
@@ -407,12 +630,14 @@ bool ConstRecordBuilder::build(InitListExpr *ile, bool allowOverwrite) {
407
630
} else {
408
631
// Otherwise we have a bitfield.
409
632
if (auto constInt = dyn_cast<cir::IntAttr>(eltInit)) {
410
- assert (!cir::MissingFeatures::bitfields ());
411
- cgm.errorNYI (field->getSourceRange (), " bitfields" );
633
+ if (!appendBitField (field, layout.getFieldOffset (index), constInt,
634
+ allowOverwrite))
635
+ return false ;
636
+ } else {
637
+ // We are trying to initialize a bitfield with a non-trivial constant,
638
+ // this must require run-time code.
639
+ return false ;
412
640
}
413
- // We are trying to initialize a bitfield with a non-trivial constant,
414
- // this must require run-time code.
415
- return false ;
416
641
}
417
642
}
418
643
@@ -510,8 +735,16 @@ bool ConstRecordBuilder::build(const APValue &val, const RecordDecl *rd,
510
735
if (field->hasAttr <NoUniqueAddressAttr>())
511
736
allowOverwrite = true ;
512
737
} else {
513
- assert (!cir::MissingFeatures::bitfields ());
514
- cgm.errorNYI (field->getSourceRange (), " bitfields" );
738
+ // Otherwise we have a bitfield.
739
+ if (auto constInt = dyn_cast<cir::IntAttr>(eltInit)) {
740
+ if (!appendBitField (field, layout.getFieldOffset (index) + offsetBits,
741
+ constInt, allowOverwrite))
742
+ return false ;
743
+ } else {
744
+ // We are trying to initialize a bitfield with a non-trivial constant,
745
+ // this must require run-time code.
746
+ return false ;
747
+ }
515
748
}
516
749
}
517
750
0 commit comments