Skip to content

Commit 717899c

Browse files
[CIR] Upstream get_bitfield operation to load bit-field members from structs (#145971)
This PR adds support for loading bit-field members from structs using the `get_bitfield` operation. It enables retrieving the address of the bitfield-packed member but does **not** yet support volatile bitfields this will be addressed in a future PR.
1 parent 925588c commit 717899c

File tree

14 files changed

+482
-5
lines changed

14 files changed

+482
-5
lines changed

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

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,4 +453,67 @@ def CIR_VisibilityAttr : CIR_EnumAttr<CIR_VisibilityKind, "visibility"> {
453453
}];
454454
}
455455

456+
//===----------------------------------------------------------------------===//
457+
// BitfieldInfoAttr
458+
//===----------------------------------------------------------------------===//
459+
460+
def BitfieldInfoAttr : CIR_Attr<"BitfieldInfo", "bitfield_info"> {
461+
let summary = "Represents info for a bit-field member";
462+
let description = [{
463+
Holds the following information about bitfields: name, storage type, size
464+
and position in the storage, and signedness.
465+
Example:
466+
Given the following struct with bitfields:
467+
```c++
468+
typedef struct {
469+
int a : 4;
470+
int b : 27;
471+
int c : 17;
472+
int d : 2;
473+
int e : 15;
474+
} S;
475+
```
476+
477+
The CIR representation of the struct `S` might look like:
478+
```mlir
479+
!rec_S = !cir.record<struct "S" packed padded {!u64i, !u16i,
480+
!cir.array<!u8i x 2>}>
481+
```
482+
And the bitfield info attribute for member `a` would be:
483+
```mlir
484+
#bfi_a = #cir.bitfield_info<name = "a", storage_type = !u64i,
485+
size = 4, offset = 0, is_signed = true>
486+
```
487+
488+
This metadata describes that field `a` is stored in a 64-bit integer,
489+
is 4 bits wide, starts at offset 0, and is signed.
490+
}];
491+
let parameters = (ins "mlir::StringAttr":$name,
492+
"mlir::Type":$storageType,
493+
"uint64_t":$size,
494+
"uint64_t":$offset,
495+
"bool":$isSigned);
496+
497+
let assemblyFormat = [{`<` struct($name,
498+
$storageType,
499+
$size,
500+
$offset,
501+
$isSigned)
502+
`>`
503+
}];
504+
505+
let builders = [
506+
AttrBuilder<(ins "llvm::StringRef":$name,
507+
"mlir::Type":$storageType,
508+
"uint64_t":$size,
509+
"uint64_t":$offset,
510+
"bool":$isSigned
511+
), [{
512+
return $_get($_ctxt, mlir::StringAttr::get($_ctxt, name), storageType,
513+
size, offset, isSigned);
514+
}]>
515+
];
516+
}
517+
518+
456519
#endif // LLVM_CLANG_CIR_DIALECT_IR_CIRATTRS_TD

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

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

1672+
//===----------------------------------------------------------------------===//
1673+
// GetBitfieldOp
1674+
//===----------------------------------------------------------------------===//
1675+
1676+
def GetBitfieldOp : CIR_Op<"get_bitfield"> {
1677+
let summary = "Get the information for a bitfield member";
1678+
let description = [{
1679+
The `cir.get_bitfield` operation provides a load-like access to
1680+
a bit field of a record.
1681+
1682+
It expects a name if a bit field, a pointer to a storage in the
1683+
base record, a type of the storage, a name of the bitfield,
1684+
a size the bit field, an offset of the bit field and a sign.
1685+
1686+
A unit attribute `volatile` can be used to indicate a volatile load of the
1687+
bitfield.
1688+
1689+
Example:
1690+
Suppose we have a struct with multiple bitfields stored in
1691+
different members. The `cir.get_bitfield` operation gets the value
1692+
of the bitfield.
1693+
```C++
1694+
typedef struct {
1695+
int a : 4;
1696+
int b : 27;
1697+
int c : 17;
1698+
int d : 2;
1699+
int e : 15;
1700+
} S;
1701+
1702+
int load_bitfield(S& s) {
1703+
return s.e;
1704+
}
1705+
```
1706+
1707+
```mlir
1708+
// 'e' is in the storage with the index 1
1709+
!cir.record<struct "S" packed padded {!u64i, !u16i, !cir.array<!u8i x 2>}>
1710+
#bfi_e = #cir.bitfield_info<name = "e", storage_type = !u16i, size = 15,
1711+
offset = 0, is_signed = true>
1712+
1713+
%2 = cir.load %0 : !cir.ptr<!cir.ptr<!record_type>>, !cir.ptr<!record_type>
1714+
%3 = cir.get_member %2[1] {name = "e"} : !cir.ptr<!record_type>
1715+
-> !cir.ptr<!u16i>
1716+
%4 = cir.get_bitfield(#bfi_e, %3 : !cir.ptr<!u16i>) -> !s32i
1717+
```
1718+
}];
1719+
1720+
let arguments = (ins
1721+
Arg<CIR_PointerType, "the address to load from", [MemRead]>:$addr,
1722+
BitfieldInfoAttr:$bitfield_info,
1723+
UnitAttr:$is_volatile
1724+
);
1725+
1726+
let results = (outs CIR_IntType:$result);
1727+
1728+
let assemblyFormat = [{ `(`$bitfield_info `,` $addr attr-dict `:`
1729+
qualified(type($addr)) `)` `->` type($result) }];
1730+
1731+
let builders = [
1732+
OpBuilder<(ins "mlir::Type":$type,
1733+
"mlir::Value":$addr,
1734+
"mlir::Type":$storage_type,
1735+
"llvm::StringRef":$name,
1736+
"unsigned":$size,
1737+
"unsigned":$offset,
1738+
"bool":$is_signed,
1739+
"bool":$is_volatile
1740+
),
1741+
[{
1742+
BitfieldInfoAttr info =
1743+
BitfieldInfoAttr::get($_builder.getContext(),
1744+
name, storage_type,
1745+
size, offset, is_signed);
1746+
build($_builder, $_state, type, addr, info, is_volatile);
1747+
}]>
1748+
];
1749+
}
1750+
16721751
//===----------------------------------------------------------------------===//
16731752
// GetMemberOp
16741753
//===----------------------------------------------------------------------===//

clang/include/clang/CIR/LoweringHelpers.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,18 @@ std::optional<mlir::Attribute>
3737
lowerConstArrayAttr(cir::ConstArrayAttr constArr,
3838
const mlir::TypeConverter *converter);
3939

40+
mlir::Value getConstAPInt(mlir::OpBuilder &bld, mlir::Location loc,
41+
mlir::Type typ, const llvm::APInt &val);
42+
43+
mlir::Value getConst(mlir::OpBuilder &bld, mlir::Location loc, mlir::Type typ,
44+
unsigned val);
45+
46+
mlir::Value createShL(mlir::OpBuilder &bld, mlir::Value lhs, unsigned rhs);
47+
48+
mlir::Value createAShR(mlir::OpBuilder &bld, mlir::Value lhs, unsigned rhs);
49+
50+
mlir::Value createAnd(mlir::OpBuilder &bld, mlir::Value lhs,
51+
const llvm::APInt &rhs);
52+
53+
mlir::Value createLShR(mlir::OpBuilder &bld, mlir::Value lhs, unsigned rhs);
4054
#endif

clang/lib/CIR/CodeGen/CIRGenBuilder.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#define LLVM_CLANG_LIB_CIR_CODEGEN_CIRGENBUILDER_H
1111

1212
#include "Address.h"
13+
#include "CIRGenRecordLayout.h"
1314
#include "CIRGenTypeCache.h"
1415
#include "clang/CIR/Interfaces/CIRTypeInterfaces.h"
1516
#include "clang/CIR/MissingFeatures.h"
@@ -392,6 +393,15 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
392393

393394
return createGlobal(module, loc, uniqueName, type, linkage);
394395
}
396+
397+
mlir::Value createGetBitfield(mlir::Location loc, mlir::Type resultType,
398+
mlir::Value addr, mlir::Type storageType,
399+
const CIRGenBitFieldInfo &info,
400+
bool isLvalueVolatile, bool useVolatile) {
401+
return create<cir::GetBitfieldOp>(loc, resultType, addr, storageType,
402+
info.name, info.size, info.offset,
403+
info.isSigned, isLvalueVolatile);
404+
}
395405
};
396406

397407
} // namespace clang::CIRGen

clang/lib/CIR/CodeGen/CIRGenExpr.cpp

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -326,13 +326,62 @@ mlir::Value CIRGenFunction::emitStoreThroughBitfieldLValue(RValue src,
326326
return {};
327327
}
328328

329+
RValue CIRGenFunction::emitLoadOfBitfieldLValue(LValue lv, SourceLocation loc) {
330+
const CIRGenBitFieldInfo &info = lv.getBitFieldInfo();
331+
332+
// Get the output type.
333+
mlir::Type resLTy = convertType(lv.getType());
334+
Address ptr = lv.getBitFieldAddress();
335+
336+
assert(!cir::MissingFeatures::armComputeVolatileBitfields());
337+
338+
mlir::Value field = builder.createGetBitfield(
339+
getLoc(loc), resLTy, ptr.getPointer(), ptr.getElementType(), info,
340+
lv.isVolatile(), false);
341+
assert(!cir::MissingFeatures::opLoadEmitScalarRangeCheck() && "NYI");
342+
return RValue::get(field);
343+
}
344+
345+
Address CIRGenFunction::getAddrOfBitFieldStorage(LValue base,
346+
const FieldDecl *field,
347+
mlir::Type fieldType,
348+
unsigned index) {
349+
mlir::Location loc = getLoc(field->getLocation());
350+
cir::PointerType fieldPtr = cir::PointerType::get(fieldType);
351+
cir::GetMemberOp sea = getBuilder().createGetMember(
352+
loc, fieldPtr, base.getPointer(), field->getName(), index);
353+
return Address(sea, CharUnits::One());
354+
}
355+
356+
LValue CIRGenFunction::emitLValueForBitField(LValue base,
357+
const FieldDecl *field) {
358+
LValueBaseInfo baseInfo = base.getBaseInfo();
359+
const CIRGenRecordLayout &layout =
360+
cgm.getTypes().getCIRGenRecordLayout(field->getParent());
361+
const CIRGenBitFieldInfo &info = layout.getBitFieldInfo(field);
362+
assert(!cir::MissingFeatures::armComputeVolatileBitfields());
363+
assert(!cir::MissingFeatures::preservedAccessIndexRegion());
364+
unsigned idx = layout.getCIRFieldNo(field);
365+
366+
Address addr = getAddrOfBitFieldStorage(base, field, info.storageType, idx);
367+
368+
mlir::Location loc = getLoc(field->getLocation());
369+
if (addr.getElementType() != info.storageType)
370+
addr = builder.createElementBitCast(loc, addr, info.storageType);
371+
372+
QualType fieldType =
373+
field->getType().withCVRQualifiers(base.getVRQualifiers());
374+
// TODO(cir): Support TBAA for bit fields.
375+
assert(!cir::MissingFeatures::opTBAA());
376+
LValueBaseInfo fieldBaseInfo(baseInfo.getAlignmentSource());
377+
return LValue::makeBitfield(addr, info, fieldType, fieldBaseInfo);
378+
}
379+
329380
LValue CIRGenFunction::emitLValueForField(LValue base, const FieldDecl *field) {
330381
LValueBaseInfo baseInfo = base.getBaseInfo();
331382

332-
if (field->isBitField()) {
333-
cgm.errorNYI(field->getSourceRange(), "emitLValueForField: bitfield");
334-
return LValue();
335-
}
383+
if (field->isBitField())
384+
return emitLValueForBitField(base, field);
336385

337386
QualType fieldType = field->getType();
338387
const RecordDecl *rec = field->getParent();
@@ -460,6 +509,9 @@ RValue CIRGenFunction::emitLoadOfLValue(LValue lv, SourceLocation loc) {
460509
assert(!lv.getType()->isFunctionType());
461510
assert(!(lv.getType()->isConstantMatrixType()) && "not implemented");
462511

512+
if (lv.isBitField())
513+
return emitLoadOfBitfieldLValue(lv, loc);
514+
463515
if (lv.isSimple())
464516
return RValue::get(emitLoadOfScalar(lv, loc));
465517

clang/lib/CIR/CodeGen/CIRGenFunction.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,9 @@ class CIRGenFunction : public CIRGenTypeCache {
550550
return it->second;
551551
}
552552

553+
Address getAddrOfBitFieldStorage(LValue base, const clang::FieldDecl *field,
554+
mlir::Type fieldType, unsigned index);
555+
553556
/// Load the value for 'this'. This function is only valid while generating
554557
/// code for an C++ member function.
555558
/// FIXME(cir): this should return a mlir::Value!
@@ -956,6 +959,8 @@ class CIRGenFunction : public CIRGenTypeCache {
956959
/// ignoring the result.
957960
void emitIgnoredExpr(const clang::Expr *e);
958961

962+
RValue emitLoadOfBitfieldLValue(LValue lv, SourceLocation loc);
963+
959964
/// Given an expression that represents a value lvalue, this method emits
960965
/// the address of the lvalue, then loads the result as an rvalue,
961966
/// returning the rvalue.
@@ -976,6 +981,7 @@ class CIRGenFunction : public CIRGenTypeCache {
976981
/// of the expression.
977982
/// FIXME: document this function better.
978983
LValue emitLValue(const clang::Expr *e);
984+
LValue emitLValueForBitField(LValue base, const FieldDecl *field);
979985
LValue emitLValueForField(LValue base, const clang::FieldDecl *field);
980986

981987
/// Like emitLValueForField, excpet that if the Field is a reference, this

clang/lib/CIR/CodeGen/CIRGenValue.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "clang/AST/CharUnits.h"
2020
#include "clang/AST/Type.h"
2121

22+
#include "CIRGenRecordLayout.h"
2223
#include "mlir/IR/Value.h"
2324

2425
#include "clang/CIR/MissingFeatures.h"
@@ -162,6 +163,7 @@ class LValue {
162163
mlir::Value vectorIdx; // Index for vector subscript
163164
mlir::Type elementType;
164165
LValueBaseInfo baseInfo;
166+
const CIRGenBitFieldInfo *bitFieldInfo{nullptr};
165167

166168
void initialize(clang::QualType type, clang::Qualifiers quals,
167169
clang::CharUnits alignment, LValueBaseInfo baseInfo) {
@@ -245,6 +247,38 @@ class LValue {
245247
r.initialize(t, t.getQualifiers(), vecAddress.getAlignment(), baseInfo);
246248
return r;
247249
}
250+
251+
// bitfield lvalue
252+
Address getBitFieldAddress() const {
253+
return Address(getBitFieldPointer(), elementType, getAlignment());
254+
}
255+
256+
mlir::Value getBitFieldPointer() const {
257+
assert(isBitField());
258+
return v;
259+
}
260+
261+
const CIRGenBitFieldInfo &getBitFieldInfo() const {
262+
assert(isBitField());
263+
return *bitFieldInfo;
264+
}
265+
266+
/// Create a new object to represent a bit-field access.
267+
///
268+
/// \param Addr - The base address of the bit-field sequence this
269+
/// bit-field refers to.
270+
/// \param Info - The information describing how to perform the bit-field
271+
/// access.
272+
static LValue makeBitfield(Address addr, const CIRGenBitFieldInfo &info,
273+
clang::QualType type, LValueBaseInfo baseInfo) {
274+
LValue r;
275+
r.lvType = BitField;
276+
r.v = addr.getPointer();
277+
r.elementType = addr.getElementType();
278+
r.bitFieldInfo = &info;
279+
r.initialize(type, type.getQualifiers(), addr.getAlignment(), baseInfo);
280+
return r;
281+
}
248282
};
249283

250284
/// An aggregate value slot.

clang/lib/CIR/Dialect/IR/CIRDialect.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ struct CIROpAsmDialectInterface : public OpAsmDialectInterface {
6565
os << (boolAttr.getValue() ? "true" : "false");
6666
return AliasResult::FinalAlias;
6767
}
68+
if (auto bitfield = mlir::dyn_cast<cir::BitfieldInfoAttr>(attr)) {
69+
os << "bfi_" << bitfield.getName().str();
70+
return AliasResult::FinalAlias;
71+
}
6872
return AliasResult::NoAlias;
6973
}
7074
};

0 commit comments

Comments
 (0)