Skip to content

Commit 3d08a40

Browse files
[CIR] Upstream new SetBitfieldOp for handling C and C++ struct bitfields (#147609)
This PR upstreams the `set_bitfield` operation used to assign values to bitfield members in C and C++ struct types. Handling of AAPCS-specific volatile bitfield semantics will be addressed in a future PR.
1 parent d8a2141 commit 3d08a40

File tree

9 files changed

+458
-21
lines changed

9 files changed

+458
-21
lines changed

clang/include/clang/CIR/Dialect/IR/CIROps.td

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1669,6 +1669,94 @@ def GetGlobalOp : CIR_Op<"get_global",
16691669
}];
16701670
}
16711671

1672+
//===----------------------------------------------------------------------===//
1673+
// SetBitfieldOp
1674+
//===----------------------------------------------------------------------===//
1675+
1676+
def SetBitfieldOp : CIR_Op<"set_bitfield"> {
1677+
let summary = "Set the value of a bitfield member";
1678+
let description = [{
1679+
The `cir.set_bitfield` operation provides a store-like access to
1680+
a bit field of a record.
1681+
1682+
A bitfield info attribute must be provided to describe the location of
1683+
the bitfield within the memory referenced by the $addr argument.
1684+
The $src argument is inserted at the appropriate place in the memory and
1685+
the value that was stored. Returns the value being stored.
1686+
1687+
A unit attribute `volatile` can be used to indicate a volatile store of the
1688+
bitfield.
1689+
```mlir
1690+
cir.set_bitfield(#bfi, %0 : !cir.ptr<!u32i>, %1 : !s32i) {is_volatile}
1691+
-> !s32i
1692+
```
1693+
1694+
Example.
1695+
Suppose we have a struct with multiple bitfields stored in
1696+
different storages. The `cir.set_bitfield` operation sets the value
1697+
of the bitfield.
1698+
```C++
1699+
typedef struct {
1700+
int a : 4;
1701+
int b : 27;
1702+
int c : 17;
1703+
int d : 2;
1704+
int e : 15;
1705+
} S;
1706+
1707+
void store_bitfield(S& s) {
1708+
s.e = 3;
1709+
}
1710+
```
1711+
1712+
```mlir
1713+
// 'e' is in the storage with the index 1
1714+
!record_type = !cir.record<struct "S" packed padded {!u64i, !u16i,
1715+
!cir.array<!u8i x 2>} #cir.record.decl.ast>
1716+
#bfi_e = #cir.bitfield_info<name = "e", storage_type = !u16i, size = 15,
1717+
offset = 0, is_signed = true>
1718+
1719+
%1 = cir.const #cir.int<3> : !s32i
1720+
%2 = cir.load %0 : !cir.ptr<!cir.ptr<!record_type>>, !cir.ptr<!record_type>
1721+
%3 = cir.get_member %2[1] {name = "e"} : !cir.ptr<!record_type>
1722+
-> !cir.ptr<!u16i>
1723+
%4 = cir.set_bitfield(#bfi_e, %3 : !cir.ptr<!u16i>, %1 : !s32i) -> !s32i
1724+
```
1725+
}];
1726+
1727+
let arguments = (ins
1728+
Arg<CIR_PointerType, "the address to store the value", [MemWrite]>:$addr,
1729+
CIR_AnyType:$src,
1730+
BitfieldInfoAttr:$bitfield_info,
1731+
UnitAttr:$is_volatile
1732+
);
1733+
1734+
let results = (outs CIR_IntType:$result);
1735+
1736+
let assemblyFormat = [{ `(`$bitfield_info`,` $addr`:`qualified(type($addr))`,`
1737+
$src`:`type($src) `)` attr-dict `->` type($result) }];
1738+
1739+
let builders = [
1740+
OpBuilder<(ins "mlir::Type":$type,
1741+
"mlir::Value":$addr,
1742+
"mlir::Type":$storage_type,
1743+
"mlir::Value":$src,
1744+
"llvm::StringRef":$name,
1745+
"unsigned":$size,
1746+
"unsigned":$offset,
1747+
"bool":$is_signed,
1748+
"bool":$is_volatile
1749+
),
1750+
[{
1751+
BitfieldInfoAttr info =
1752+
BitfieldInfoAttr::get($_builder.getContext(),
1753+
name, storage_type,
1754+
size, offset, is_signed);
1755+
build($_builder, $_state, type, addr, src, info, is_volatile);
1756+
}]>
1757+
];
1758+
}
1759+
16721760
//===----------------------------------------------------------------------===//
16731761
// GetBitfieldOp
16741762
//===----------------------------------------------------------------------===//

clang/lib/CIR/CodeGen/CIRGenBuilder.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,15 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
394394
return createGlobal(module, loc, uniqueName, type, linkage);
395395
}
396396

397+
mlir::Value createSetBitfield(mlir::Location loc, mlir::Type resultType,
398+
mlir::Value dstAddr, mlir::Type storageType,
399+
mlir::Value src, const CIRGenBitFieldInfo &info,
400+
bool isLvalueVolatile, bool useVolatile) {
401+
return create<cir::SetBitfieldOp>(loc, resultType, dstAddr, storageType,
402+
src, info.name, info.size, info.offset,
403+
info.isSigned, isLvalueVolatile);
404+
}
405+
397406
mlir::Value createGetBitfield(mlir::Location loc, mlir::Type resultType,
398407
mlir::Value addr, mlir::Type storageType,
399408
const CIRGenBitFieldInfo &info,

clang/lib/CIR/CodeGen/CIRGenExpr.cpp

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,10 @@ void CIRGenFunction::emitStoreThroughLValue(RValue src, LValue dst,
224224
return;
225225
}
226226

227+
assert(dst.isBitField() && "Unknown LValue type");
228+
emitStoreThroughBitfieldLValue(src, dst);
229+
return;
230+
227231
cgm.errorNYI(dst.getPointer().getLoc(),
228232
"emitStoreThroughLValue: non-simple lvalue");
229233
return;
@@ -321,9 +325,21 @@ void CIRGenFunction::emitStoreOfScalar(mlir::Value value, Address addr,
321325

322326
mlir::Value CIRGenFunction::emitStoreThroughBitfieldLValue(RValue src,
323327
LValue dst) {
324-
assert(!cir::MissingFeatures::bitfields());
325-
cgm.errorNYI("bitfields");
326-
return {};
328+
329+
assert(!cir::MissingFeatures::armComputeVolatileBitfields());
330+
331+
const CIRGenBitFieldInfo &info = dst.getBitFieldInfo();
332+
mlir::Type resLTy = convertTypeForMem(dst.getType());
333+
Address ptr = dst.getBitFieldAddress();
334+
335+
assert(!cir::MissingFeatures::armComputeVolatileBitfields());
336+
const bool useVolatile = false;
337+
338+
mlir::Value dstAddr = dst.getAddress().getPointer();
339+
340+
return builder.createSetBitfield(dstAddr.getLoc(), resLTy, dstAddr,
341+
ptr.getElementType(), src.getValue(), info,
342+
dst.isVolatileQualified(), useVolatile);
327343
}
328344

329345
RValue CIRGenFunction::emitLoadOfBitfieldLValue(LValue lv, SourceLocation loc) {
@@ -1062,11 +1078,10 @@ LValue CIRGenFunction::emitBinaryOperatorLValue(const BinaryOperator *e) {
10621078
LValue lv = emitLValue(e->getLHS());
10631079

10641080
SourceLocRAIIObject loc{*this, getLoc(e->getSourceRange())};
1065-
if (lv.isBitField()) {
1066-
cgm.errorNYI(e->getSourceRange(), "bitfields");
1067-
return {};
1068-
}
1069-
emitStoreThroughLValue(rv, lv);
1081+
if (lv.isBitField())
1082+
emitStoreThroughBitfieldLValue(rv, lv);
1083+
else
1084+
emitStoreThroughLValue(rv, lv);
10701085

10711086
if (getLangOpts().OpenMP) {
10721087
cgm.errorNYI(e->getSourceRange(), "openmp");

clang/lib/CIR/CodeGen/CIRGenValue.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,8 @@ class LValue {
186186
bool isBitField() const { return lvType == BitField; }
187187
bool isVolatile() const { return quals.hasVolatile(); }
188188

189+
bool isVolatileQualified() const { return quals.hasVolatile(); }
190+
189191
unsigned getVRQualifiers() const {
190192
return quals.getCVRQualifiers() & ~clang::Qualifiers::Const;
191193
}

clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp

Lines changed: 84 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2059,6 +2059,7 @@ void ConvertCIRToLLVMPass::runOnOperation() {
20592059
CIRToLLVMGetGlobalOpLowering,
20602060
CIRToLLVMGetMemberOpLowering,
20612061
CIRToLLVMSelectOpLowering,
2062+
CIRToLLVMSetBitfieldOpLowering,
20622063
CIRToLLVMShiftOpLowering,
20632064
CIRToLLVMStackRestoreOpLowering,
20642065
CIRToLLVMStackSaveOpLowering,
@@ -2443,6 +2444,88 @@ mlir::LogicalResult CIRToLLVMComplexImagOpLowering::matchAndRewrite(
24432444
return mlir::success();
24442445
}
24452446

2447+
mlir::IntegerType computeBitfieldIntType(mlir::Type storageType,
2448+
mlir::MLIRContext *context,
2449+
unsigned &storageSize) {
2450+
return TypeSwitch<mlir::Type, mlir::IntegerType>(storageType)
2451+
.Case<cir::ArrayType>([&](cir::ArrayType atTy) {
2452+
storageSize = atTy.getSize() * 8;
2453+
return mlir::IntegerType::get(context, storageSize);
2454+
})
2455+
.Case<cir::IntType>([&](cir::IntType intTy) {
2456+
storageSize = intTy.getWidth();
2457+
return mlir::IntegerType::get(context, storageSize);
2458+
})
2459+
.Default([](mlir::Type) -> mlir::IntegerType {
2460+
llvm_unreachable(
2461+
"Either ArrayType or IntType expected for bitfields storage");
2462+
});
2463+
}
2464+
2465+
mlir::LogicalResult CIRToLLVMSetBitfieldOpLowering::matchAndRewrite(
2466+
cir::SetBitfieldOp op, OpAdaptor adaptor,
2467+
mlir::ConversionPatternRewriter &rewriter) const {
2468+
mlir::OpBuilder::InsertionGuard guard(rewriter);
2469+
rewriter.setInsertionPoint(op);
2470+
2471+
cir::BitfieldInfoAttr info = op.getBitfieldInfo();
2472+
uint64_t size = info.getSize();
2473+
uint64_t offset = info.getOffset();
2474+
mlir::Type storageType = info.getStorageType();
2475+
mlir::MLIRContext *context = storageType.getContext();
2476+
2477+
unsigned storageSize = 0;
2478+
2479+
mlir::IntegerType intType =
2480+
computeBitfieldIntType(storageType, context, storageSize);
2481+
2482+
mlir::Value srcVal = createIntCast(rewriter, adaptor.getSrc(), intType);
2483+
unsigned srcWidth = storageSize;
2484+
mlir::Value resultVal = srcVal;
2485+
2486+
if (storageSize != size) {
2487+
assert(storageSize > size && "Invalid bitfield size.");
2488+
2489+
mlir::Value val = rewriter.create<mlir::LLVM::LoadOp>(
2490+
op.getLoc(), intType, adaptor.getAddr(), /* alignment */ 0,
2491+
op.getIsVolatile());
2492+
2493+
srcVal =
2494+
createAnd(rewriter, srcVal, llvm::APInt::getLowBitsSet(srcWidth, size));
2495+
resultVal = srcVal;
2496+
srcVal = createShL(rewriter, srcVal, offset);
2497+
2498+
// Mask out the original value.
2499+
val = createAnd(rewriter, val,
2500+
~llvm::APInt::getBitsSet(srcWidth, offset, offset + size));
2501+
2502+
// Or together the unchanged values and the source value.
2503+
srcVal = rewriter.create<mlir::LLVM::OrOp>(op.getLoc(), val, srcVal);
2504+
}
2505+
2506+
rewriter.create<mlir::LLVM::StoreOp>(op.getLoc(), srcVal, adaptor.getAddr(),
2507+
/* alignment */ 0, op.getIsVolatile());
2508+
2509+
mlir::Type resultTy = getTypeConverter()->convertType(op.getType());
2510+
2511+
if (info.getIsSigned()) {
2512+
assert(size <= storageSize);
2513+
unsigned highBits = storageSize - size;
2514+
2515+
if (highBits) {
2516+
resultVal = createShL(rewriter, resultVal, highBits);
2517+
resultVal = createAShR(rewriter, resultVal, highBits);
2518+
}
2519+
}
2520+
2521+
resultVal = createIntCast(rewriter, resultVal,
2522+
mlir::cast<mlir::IntegerType>(resultTy),
2523+
info.getIsSigned());
2524+
2525+
rewriter.replaceOp(op, resultVal);
2526+
return mlir::success();
2527+
}
2528+
24462529
mlir::LogicalResult CIRToLLVMGetBitfieldOpLowering::matchAndRewrite(
24472530
cir::GetBitfieldOp op, OpAdaptor adaptor,
24482531
mlir::ConversionPatternRewriter &rewriter) const {
@@ -2458,19 +2541,7 @@ mlir::LogicalResult CIRToLLVMGetBitfieldOpLowering::matchAndRewrite(
24582541
unsigned storageSize = 0;
24592542

24602543
mlir::IntegerType intType =
2461-
TypeSwitch<mlir::Type, mlir::IntegerType>(storageType)
2462-
.Case<cir::ArrayType>([&](cir::ArrayType atTy) {
2463-
storageSize = atTy.getSize() * 8;
2464-
return mlir::IntegerType::get(context, storageSize);
2465-
})
2466-
.Case<cir::IntType>([&](cir::IntType intTy) {
2467-
storageSize = intTy.getWidth();
2468-
return mlir::IntegerType::get(context, storageSize);
2469-
})
2470-
.Default([](mlir::Type) -> mlir::IntegerType {
2471-
llvm_unreachable(
2472-
"Either ArrayType or IntType expected for bitfields storage");
2473-
});
2544+
computeBitfieldIntType(storageType, context, storageSize);
24742545

24752546
mlir::Value val = rewriter.create<mlir::LLVM::LoadOp>(
24762547
op.getLoc(), intType, adaptor.getAddr(), 0, op.getIsVolatile());

clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,16 @@ class CIRToLLVMComplexImagOpLowering
513513
mlir::ConversionPatternRewriter &) const override;
514514
};
515515

516+
class CIRToLLVMSetBitfieldOpLowering
517+
: public mlir::OpConversionPattern<cir::SetBitfieldOp> {
518+
public:
519+
using mlir::OpConversionPattern<cir::SetBitfieldOp>::OpConversionPattern;
520+
521+
mlir::LogicalResult
522+
matchAndRewrite(cir::SetBitfieldOp op, OpAdaptor,
523+
mlir::ConversionPatternRewriter &) const override;
524+
};
525+
516526
class CIRToLLVMGetBitfieldOpLowering
517527
: public mlir::OpConversionPattern<cir::GetBitfieldOp> {
518528
public:

0 commit comments

Comments
 (0)