@@ -405,8 +405,103 @@ void CIRRecordLowering::computeVolatileBitfields() {
405
405
!cirGenTypes.getModule ().getCodeGenOpts ().AAPCSBitfieldWidth )
406
406
return ;
407
407
408
- for ([[maybe_unused]] auto &I : bitFields) {
409
- assert (!cir::MissingFeatures::armComputeVolatileBitfields ());
408
+ for (auto &[field, info] : bitFields) {
409
+ mlir::Type resLTy = cirGenTypes.convertTypeForMem (field->getType ());
410
+
411
+ if (astContext.toBits (astRecordLayout.getAlignment ()) <
412
+ getSizeInBits (resLTy).getQuantity ())
413
+ continue ;
414
+
415
+ // CIRRecordLowering::setBitFieldInfo() pre-adjusts the bit-field offsets
416
+ // for big-endian targets, but it assumes a container of width
417
+ // info.storageSize. Since AAPCS uses a different container size (width
418
+ // of the type), we first undo that calculation here and redo it once
419
+ // the bit-field offset within the new container is calculated.
420
+ const unsigned oldOffset =
421
+ isBE () ? info.StorageSize - (info.Offset + info.Size ) : info.Offset ;
422
+ // Offset to the bit-field from the beginning of the struct.
423
+ const unsigned absoluteOffset =
424
+ astContext.toBits (info.StorageOffset ) + oldOffset;
425
+
426
+ // Container size is the width of the bit-field type.
427
+ const unsigned storageSize = getSizeInBits (resLTy).getQuantity ();
428
+ // Nothing to do if the access uses the desired
429
+ // container width and is naturally aligned.
430
+ if (info.StorageSize == storageSize && (oldOffset % storageSize == 0 ))
431
+ continue ;
432
+
433
+ // Offset within the container.
434
+ unsigned offset = absoluteOffset & (storageSize - 1 );
435
+ // Bail out if an aligned load of the container cannot cover the entire
436
+ // bit-field. This can happen for example, if the bit-field is part of a
437
+ // packed struct. AAPCS does not define access rules for such cases, we let
438
+ // clang to follow its own rules.
439
+ if (offset + info.Size > storageSize)
440
+ continue ;
441
+
442
+ // Re-adjust offsets for big-endian targets.
443
+ if (isBE ())
444
+ offset = storageSize - (offset + info.Size );
445
+
446
+ const CharUnits storageOffset =
447
+ astContext.toCharUnitsFromBits (absoluteOffset & ~(storageSize - 1 ));
448
+ const CharUnits end = storageOffset +
449
+ astContext.toCharUnitsFromBits (storageSize) -
450
+ CharUnits::One ();
451
+
452
+ const ASTRecordLayout &layout =
453
+ astContext.getASTRecordLayout (field->getParent ());
454
+ // If we access outside memory outside the record, than bail out.
455
+ const CharUnits recordSize = layout.getSize ();
456
+ if (end >= recordSize)
457
+ continue ;
458
+
459
+ // Bail out if performing this load would access non-bit-fields members.
460
+ bool conflict = false ;
461
+ for (const auto *f : recordDecl->fields ()) {
462
+ // Allow sized bit-fields overlaps.
463
+ if (f->isBitField () && !f->isZeroLengthBitField ())
464
+ continue ;
465
+
466
+ const CharUnits fOffset = astContext.toCharUnitsFromBits (
467
+ layout.getFieldOffset (f->getFieldIndex ()));
468
+
469
+ // As C11 defines, a zero sized bit-field defines a barrier, so
470
+ // fields after and before it should be race condition free.
471
+ // The AAPCS acknowledges it and imposes no restritions when the
472
+ // natural container overlaps a zero-length bit-field.
473
+ if (f->isZeroLengthBitField ()) {
474
+ if (end > fOffset && storageOffset < fOffset ) {
475
+ conflict = true ;
476
+ break ;
477
+ }
478
+ }
479
+
480
+ const CharUnits fEnd =
481
+ fOffset +
482
+ astContext.toCharUnitsFromBits (
483
+ getSizeInBits (cirGenTypes.convertTypeForMem (f->getType ()))
484
+ .getQuantity ()) -
485
+ CharUnits::One ();
486
+ // If no overlap, continue.
487
+ if (end < fOffset || fEnd < storageOffset)
488
+ continue ;
489
+
490
+ // The desired load overlaps a non-bit-field member, bail out.
491
+ conflict = true ;
492
+ break ;
493
+ }
494
+
495
+ if (conflict)
496
+ continue ;
497
+ // Write the new bit-field access parameters.
498
+ // As the storage offset now is defined as the number of elements from the
499
+ // start of the structure, we should divide the Offset by the element size.
500
+ info.VolatileStorageOffset =
501
+ storageOffset /
502
+ astContext.toCharUnitsFromBits (storageSize).getQuantity ();
503
+ info.VolatileStorageSize = storageSize;
504
+ info.VolatileOffset = offset;
410
505
}
411
506
}
412
507
0 commit comments