Skip to content

Commit 38b98a5

Browse files
[CIR] Upstream new SetBitfieldOp for handling C and C++ struct bitfields
1 parent 68309ad commit 38b98a5

File tree

9 files changed

+324
-6
lines changed

9 files changed

+324
-6
lines changed

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

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1669,6 +1669,90 @@ 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+
It expects an address of a storage where to store, a type of the storage,
1683+
a value being stored, a name of a bit field, a pointer to the storage in the
1684+
base record, a size of the storage, a size the bit field, an offset
1685+
of the bit field and a sign. Returns a value being stored.
1686+
1687+
A unit attribute `volatile` can be used to indicate a volatile load of the
1688+
bitfield.
1689+
1690+
Example.
1691+
Suppose we have a struct with multiple bitfields stored in
1692+
different storages. The `cir.set_bitfield` operation sets the value
1693+
of the bitfield.
1694+
```C++
1695+
typedef struct {
1696+
int a : 4;
1697+
int b : 27;
1698+
int c : 17;
1699+
int d : 2;
1700+
int e : 15;
1701+
} S;
1702+
1703+
void store_bitfield(S& s) {
1704+
s.e = 3;
1705+
}
1706+
```
1707+
1708+
```mlir
1709+
// 'e' is in the storage with the index 1
1710+
!record_type = !cir.record<struct "S" packed padded {!u64i, !u16i,
1711+
!cir.array<!u8i x 2>} #cir.record.decl.ast>
1712+
#bfi_e = #cir.bitfield_info<name = "e", storage_type = !u16i, size = 15,
1713+
offset = 0, is_signed = true>
1714+
1715+
%1 = cir.const #cir.int<3> : !s32i
1716+
%2 = cir.load %0 : !cir.ptr<!cir.ptr<!record_type>>, !cir.ptr<!record_type>
1717+
%3 = cir.get_member %2[1] {name = "e"} : !cir.ptr<!record_type>
1718+
-> !cir.ptr<!u16i>
1719+
%4 = cir.set_bitfield(#bfi_e, %3 : !cir.ptr<!u16i>, %1 : !s32i) -> !s32i
1720+
```
1721+
}];
1722+
1723+
let arguments = (ins
1724+
Arg<CIR_PointerType, "the address to store the value", [MemWrite]>:$addr,
1725+
CIR_AnyType:$src,
1726+
BitfieldInfoAttr:$bitfield_info,
1727+
UnitAttr:$is_volatile
1728+
);
1729+
1730+
let results = (outs CIR_IntType:$result);
1731+
1732+
let assemblyFormat = [{ `(`$bitfield_info`,` $addr`:`qualified(type($addr))`,`
1733+
$src`:`type($src) `)` attr-dict `->` type($result) }];
1734+
1735+
let builders = [
1736+
OpBuilder<(ins "mlir::Type":$type,
1737+
"mlir::Value":$addr,
1738+
"mlir::Type":$storage_type,
1739+
"mlir::Value":$src,
1740+
"llvm::StringRef":$name,
1741+
"unsigned":$size,
1742+
"unsigned":$offset,
1743+
"bool":$is_signed,
1744+
"bool":$is_volatile
1745+
),
1746+
[{
1747+
BitfieldInfoAttr info =
1748+
BitfieldInfoAttr::get($_builder.getContext(),
1749+
name, storage_type,
1750+
size, offset, is_signed);
1751+
build($_builder, $_state, type, addr, src, info, is_volatile);
1752+
}]>
1753+
];
1754+
}
1755+
16721756
//===----------------------------------------------------------------------===//
16731757
// GetBitfieldOp
16741758
//===----------------------------------------------------------------------===//

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: 21 additions & 6 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() && "NIY 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,20 @@ 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+
const bool useVolatile = false;
336+
337+
mlir::Value dstAddr = dst.getAddress().getPointer();
338+
339+
return builder.createSetBitfield(dstAddr.getLoc(), resLTy, dstAddr,
340+
ptr.getElementType(), src.getValue(), info,
341+
dst.isVolatileQualified(), useVolatile);
327342
}
328343

329344
RValue CIRGenFunction::emitLoadOfBitfieldLValue(LValue lv, SourceLocation loc) {
@@ -1063,10 +1078,10 @@ LValue CIRGenFunction::emitBinaryOperatorLValue(const BinaryOperator *e) {
10631078

10641079
SourceLocRAIIObject loc{*this, getLoc(e->getSourceRange())};
10651080
if (lv.isBitField()) {
1066-
cgm.errorNYI(e->getSourceRange(), "bitfields");
1067-
return {};
1081+
emitStoreThroughBitfieldLValue(rv, lv);
1082+
} else {
1083+
emitStoreThroughLValue(rv, lv);
10681084
}
1069-
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: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2049,6 +2049,7 @@ void ConvertCIRToLLVMPass::runOnOperation() {
20492049
CIRToLLVMGetGlobalOpLowering,
20502050
CIRToLLVMGetMemberOpLowering,
20512051
CIRToLLVMSelectOpLowering,
2052+
CIRToLLVMSetBitfieldOpLowering,
20522053
CIRToLLVMShiftOpLowering,
20532054
CIRToLLVMStackRestoreOpLowering,
20542055
CIRToLLVMStackSaveOpLowering,
@@ -2384,6 +2385,82 @@ mlir::LogicalResult CIRToLLVMComplexImagOpLowering::matchAndRewrite(
23842385
return mlir::success();
23852386
}
23862387

2388+
mlir::LogicalResult CIRToLLVMSetBitfieldOpLowering::matchAndRewrite(
2389+
cir::SetBitfieldOp op, OpAdaptor adaptor,
2390+
mlir::ConversionPatternRewriter &rewriter) const {
2391+
mlir::OpBuilder::InsertionGuard guard(rewriter);
2392+
rewriter.setInsertionPoint(op);
2393+
2394+
cir::BitfieldInfoAttr info = op.getBitfieldInfo();
2395+
uint64_t size = info.getSize();
2396+
uint64_t offset = info.getOffset();
2397+
mlir::Type storageType = info.getStorageType();
2398+
mlir::MLIRContext *context = storageType.getContext();
2399+
2400+
unsigned storageSize = 0;
2401+
2402+
mlir::IntegerType intType =
2403+
TypeSwitch<mlir::Type, mlir::IntegerType>(storageType)
2404+
.Case<cir::ArrayType>([&](cir::ArrayType atTy) {
2405+
storageSize = atTy.getSize() * 8;
2406+
return mlir::IntegerType::get(context, storageSize);
2407+
})
2408+
.Case<cir::IntType>([&](cir::IntType intTy) {
2409+
storageSize = intTy.getWidth();
2410+
return mlir::IntegerType::get(context, storageSize);
2411+
})
2412+
.Default([](mlir::Type) -> mlir::IntegerType {
2413+
llvm_unreachable(
2414+
"Either ArrayType or IntType expected for bitfields storage");
2415+
});
2416+
2417+
mlir::Value srcVal = createIntCast(rewriter, adaptor.getSrc(), intType);
2418+
unsigned srcWidth = storageSize;
2419+
mlir::Value resultVal = srcVal;
2420+
2421+
if (storageSize != size) {
2422+
assert(storageSize > size && "Invalid bitfield size.");
2423+
2424+
mlir::Value val = rewriter.create<mlir::LLVM::LoadOp>(
2425+
op.getLoc(), intType, adaptor.getAddr(), /* alignment */ 0,
2426+
op.getIsVolatile());
2427+
2428+
srcVal =
2429+
createAnd(rewriter, srcVal, llvm::APInt::getLowBitsSet(srcWidth, size));
2430+
resultVal = srcVal;
2431+
srcVal = createShL(rewriter, srcVal, offset);
2432+
2433+
// Mask out the original value.
2434+
val = createAnd(rewriter, val,
2435+
~llvm::APInt::getBitsSet(srcWidth, offset, offset + size));
2436+
2437+
// Or together the unchanged values and the source value.
2438+
srcVal = rewriter.create<mlir::LLVM::OrOp>(op.getLoc(), val, srcVal);
2439+
}
2440+
2441+
rewriter.create<mlir::LLVM::StoreOp>(op.getLoc(), srcVal, adaptor.getAddr(),
2442+
/* alignment */ 0, op.getIsVolatile());
2443+
2444+
mlir::Type resultTy = getTypeConverter()->convertType(op.getType());
2445+
2446+
if (info.getIsSigned()) {
2447+
assert(size <= storageSize);
2448+
unsigned highBits = storageSize - size;
2449+
2450+
if (highBits) {
2451+
resultVal = createShL(rewriter, resultVal, highBits);
2452+
resultVal = createAShR(rewriter, resultVal, highBits);
2453+
}
2454+
}
2455+
2456+
resultVal = createIntCast(rewriter, resultVal,
2457+
mlir::cast<mlir::IntegerType>(resultTy),
2458+
info.getIsSigned());
2459+
2460+
rewriter.replaceOp(op, resultVal);
2461+
return mlir::success();
2462+
}
2463+
23872464
mlir::LogicalResult CIRToLLVMGetBitfieldOpLowering::matchAndRewrite(
23882465
cir::GetBitfieldOp op, OpAdaptor adaptor,
23892466
mlir::ConversionPatternRewriter &rewriter) const {

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:

clang/test/CIR/CodeGen/bitfields.c

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,3 +134,29 @@ unsigned int load_field_unsigned(A* s) {
134134
//OGCG: [[TMP4:%.*]] = lshr i16 [[TMP3]], 3
135135
//OGCG: [[TMP5:%.*]] = and i16 [[TMP4]], 15
136136
//OGCG: [[TMP6:%.*]] = zext i16 [[TMP5]] to i32
137+
138+
void store_field() {
139+
S s;
140+
s.e = 3;
141+
}
142+
// CIR: cir.func {{.*@store_field}}
143+
// CIR: [[TMP0:%.*]] = cir.alloca !rec_S, !cir.ptr<!rec_S>
144+
// CIR: [[TMP1:%.*]] = cir.const #cir.int<3> : !s32i
145+
// CIR: [[TMP2:%.*]] = cir.get_member [[TMP0]][1] {name = "e"} : !cir.ptr<!rec_S> -> !cir.ptr<!u16i>
146+
// CIR: cir.set_bitfield(#bfi_e, [[TMP2]] : !cir.ptr<!u16i>, [[TMP1]] : !s32i)
147+
148+
// LLVM: define dso_local void @store_field()
149+
// LLVM: [[TMP0:%.*]] = alloca %struct.S, i64 1, align 4
150+
// LLVM: [[TMP1:%.*]] = getelementptr %struct.S, ptr [[TMP0]], i32 0, i32 1
151+
// LLVM: [[TMP2:%.*]] = load i16, ptr [[TMP1]], align 2
152+
// LLVM: [[TMP3:%.*]] = and i16 [[TMP2]], -32768
153+
// LLVM: [[TMP4:%.*]] = or i16 [[TMP3]], 3
154+
// LLVM: store i16 [[TMP4]], ptr [[TMP1]], align 2
155+
156+
// OGCG: define dso_local void @store_field()
157+
// OGCG: [[TMP0:%.*]] = alloca %struct.S, align 4
158+
// OGCG: [[TMP1:%.*]] = getelementptr inbounds nuw %struct.S, ptr [[TMP0]], i32 0, i32 1
159+
// OGCG: [[TMP2:%.*]] = load i16, ptr [[TMP1]], align 4
160+
// OGCG: [[TMP3:%.*]] = and i16 [[TMP2]], -32768
161+
// OGCG: [[TMP4:%.*]] = or i16 [[TMP3]], 3
162+
// OGCG: store i16 [[TMP4]], ptr [[TMP1]], align 4

clang/test/CIR/CodeGen/bitfields.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,28 @@ int load_field(S* s) {
5858
// OGCG: [[TMP3:%.*]] = shl i64 [[TMP2]], 15
5959
// OGCG: [[TMP4:%.*]] = ashr i64 [[TMP3]], 47
6060
// OGCG: [[TMP5:%.*]] = trunc i64 [[TMP4]] to i32
61+
62+
void store_field() {
63+
S s;
64+
s.a = 3;
65+
}
66+
// CIR: cir.func dso_local @_Z11store_field
67+
// CIR: [[TMP0:%.*]] = cir.alloca !rec_S, !cir.ptr<!rec_S>
68+
// CIR: [[TMP1:%.*]] = cir.const #cir.int<3> : !s32i
69+
// CIR: [[TMP2:%.*]] = cir.get_member [[TMP0]][0] {name = "a"} : !cir.ptr<!rec_S> -> !cir.ptr<!u64i>
70+
// CIR: cir.set_bitfield(#bfi_a, [[TMP2]] : !cir.ptr<!u64i>, [[TMP1]] : !s32i)
71+
72+
// LLVM: define dso_local void @_Z11store_fieldv
73+
// LLVM: [[TMP0:%.*]] = alloca %struct.S, i64 1, align 4
74+
// LLVM: [[TMP1:%.*]] = getelementptr %struct.S, ptr [[TMP0]], i32 0, i32 0
75+
// LLVM: [[TMP2:%.*]] = load i64, ptr [[TMP1]], align 8
76+
// LLVM: [[TMP3:%.*]] = and i64 [[TMP2]], -16
77+
// LLVM: [[TMP4:%.*]] = or i64 [[TMP3]], 3
78+
// LLVM: store i64 [[TMP4]], ptr [[TMP1]], align 8
79+
80+
// OGCG: define dso_local void @_Z11store_fieldv()
81+
// OGCG: [[TMP0:%.*]] = alloca %struct.S, align 4
82+
// OGCG: [[TMP1:%.*]] = load i64, ptr [[TMP0]], align 4
83+
// OGCG: [[TMP2:%.*]] = and i64 [[TMP1]], -16
84+
// OGCG: [[TMP3:%.*]] = or i64 [[TMP2]], 3
85+
// OGCG: store i64 [[TMP3]], ptr [[TMP0]], align 4

0 commit comments

Comments
 (0)