Skip to content

Commit bbf37b0

Browse files
gitoleglanza
authored andcommitted
[CIR][CodeGen] Bitfield operations (#279)
As we discussed in #233, there is a desire to have CIR operations for bit fields set/get access and do all the stuff in the `LoweringPrepare`. There is one thing I want to discuss, that's why the PR is marked as a draft now. Looks like I have to introduce some redundant helpers for all these `or` and `shift` operations: while we were in the `CodeGen` area, we used `CIRGenBuilder` and we could easily extend it. I bet we don't want to depend from `CodeGen` in the `LoweringPrepare`. Once it's true. what is a good place for all this common things? As an idea, we could introduce one more layer for builder, with no state involved - just helpers and nothing else. But again, what is a good place for it from your point of view?
1 parent 41f55da commit bbf37b0

File tree

9 files changed

+407
-129
lines changed

9 files changed

+407
-129
lines changed

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

+27
Original file line numberDiff line numberDiff line change
@@ -521,4 +521,31 @@ def GlobalCtorAttr : CIR_Attr<"GlobalCtor", "globalCtor"> {
521521
];
522522
let skipDefaultBuilders = 1;
523523
}
524+
525+
def BitfieldInfoAttr : CIR_Attr<"BitfieldInfo", "bitfield_info"> {
526+
let summary = "Represents a bit field info";
527+
let description = [{
528+
Holds the next information about bitfields: name, storage type, a bitfield size
529+
and position in the storage, if the bitfield is signed or not.
530+
}];
531+
let parameters = (ins "StringAttr":$name,
532+
"Type":$storage_type,
533+
"uint64_t":$size,
534+
"uint64_t":$offset,
535+
"bool":$is_signed);
536+
537+
let assemblyFormat = "`<` struct($name, $storage_type, $size, $offset, $is_signed) `>`";
538+
539+
let builders = [
540+
AttrBuilder<(ins "StringRef":$name,
541+
"Type":$storage_type,
542+
"uint64_t":$size,
543+
"uint64_t":$offset,
544+
"bool":$is_signed
545+
), [{
546+
return $_get($_ctxt, StringAttr::get($_ctxt, name), storage_type, size, offset, is_signed);
547+
}]>
548+
];
549+
}
550+
524551
#endif // MLIR_CIR_DIALECT_CIR_ATTRS

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

+148
Original file line numberDiff line numberDiff line change
@@ -1415,6 +1415,154 @@ def VTableAddrPointOp : CIR_Op<"vtable.address_point",
14151415
let hasVerifier = 1;
14161416
}
14171417

1418+
//===----------------------------------------------------------------------===//
1419+
// SetBitfieldOp
1420+
//===----------------------------------------------------------------------===//
1421+
1422+
def SetBitfieldOp : CIR_Op<"set_bitfield"> {
1423+
let summary = "Set a bitfield";
1424+
let description = [{
1425+
The `cir.set_bitfield` operation provides a store-like access to
1426+
a bit field of a record.
1427+
1428+
It expects an address of a storage where to store, a type of the storage,
1429+
a value being stored, a name of a bit field, a pointer to the storage in the
1430+
base record, a size of the storage, a size the bit field, an offset
1431+
of the bit field and a sign. Returns a value being stored.
1432+
1433+
Example.
1434+
Suppose we have a struct with multiple bitfields stored in
1435+
different storages. The `cir.set_bitfield` operation sets the value
1436+
of the bitfield.
1437+
```C++
1438+
typedef struct {
1439+
int a : 4;
1440+
int b : 27;
1441+
int c : 17;
1442+
int d : 2;
1443+
int e : 15;
1444+
} S;
1445+
1446+
void store_bitfield(S& s) {
1447+
s.d = 3;
1448+
}
1449+
```
1450+
1451+
```mlir
1452+
// 'd' is in the storage with the index 1
1453+
!struct_type = !cir.struct<struct "S" {!cir.int<u, 32>, !cir.int<u, 32>, !cir.int<u, 16>} #cir.record.decl.ast>
1454+
#bfi_d = #cir.bitfield_info<name = "d", storage_type = !u32i, size = 2, offset = 17, is_signed = true>
1455+
1456+
%1 = cir.const(#cir.int<3> : !s32i) : !s32i
1457+
%2 = cir.load %0 : cir.ptr <!cir.ptr<!struct_type>>, !cir.ptr<!struct_type>
1458+
%3 = cir.get_member %2[1] {name = "d"} : !cir.ptr<!struct_type> -> !cir.ptr<!u32i>
1459+
%4 = cir.set_bitfield(#bfi_d, %3 : !cir.ptr<!u32i>, %1 : !s32i) -> !s32i
1460+
```
1461+
}];
1462+
1463+
let arguments = (ins
1464+
AnyType:$dst,
1465+
AnyType:$src,
1466+
BitfieldInfoAttr:$bitfield_info
1467+
);
1468+
1469+
let results = (outs CIR_IntType:$result);
1470+
1471+
let assemblyFormat = [{ `(`$bitfield_info`,` $dst`:`type($dst)`,`
1472+
$src`:`type($src) `)` attr-dict `->` type($result) }];
1473+
1474+
let builders = [
1475+
OpBuilder<(ins "Type":$type,
1476+
"Value":$dst,
1477+
"Type":$storage_type,
1478+
"Value":$src,
1479+
"StringRef":$name,
1480+
"unsigned":$size,
1481+
"unsigned":$offset,
1482+
"bool":$is_signed
1483+
),
1484+
[{
1485+
BitfieldInfoAttr info =
1486+
BitfieldInfoAttr::get($_builder.getContext(),
1487+
name, storage_type,
1488+
size, offset, is_signed);
1489+
build($_builder, $_state, type, dst, src, info);
1490+
}]>
1491+
];
1492+
}
1493+
1494+
//===----------------------------------------------------------------------===//
1495+
// GetBitfieldOp
1496+
//===----------------------------------------------------------------------===//
1497+
1498+
def GetBitfieldOp : CIR_Op<"get_bitfield"> {
1499+
let summary = "Get a bitfield";
1500+
let description = [{
1501+
The `cir.get_bitfield` operation provides a load-like access to
1502+
a bit field of a record.
1503+
1504+
It expects a name if a bit field, a pointer to a storage in the
1505+
base record, a type of the storage, a name of the bitfield,
1506+
a size the bit field, an offset of the bit field and a sign.
1507+
1508+
Example:
1509+
Suppose we have a struct with multiple bitfields stored in
1510+
different storages. The `cir.get_bitfield` operation gets the value
1511+
of the bitfield
1512+
```C++
1513+
typedef struct {
1514+
int a : 4;
1515+
int b : 27;
1516+
int c : 17;
1517+
int d : 2;
1518+
int e : 15;
1519+
} S;
1520+
1521+
int load_bitfield(S& s) {
1522+
return s.d;
1523+
}
1524+
```
1525+
1526+
```mlir
1527+
// 'd' is in the storage with the index 1
1528+
!struct_type = !cir.struct<struct "S" {!cir.int<u, 32>, !cir.int<u, 32>, !cir.int<u, 16>} #cir.record.decl.ast>
1529+
#bfi_d = #cir.bitfield_info<name = "d", storage_type = !u32i, size = 2, offset = 17, is_signed = true>
1530+
1531+
%2 = cir.load %0 : cir.ptr <!cir.ptr<!struct_type>>, !cir.ptr<!struct_type>
1532+
%3 = cir.get_member %2[1] {name = "d"} : !cir.ptr<!struct_type> -> !cir.ptr<!u32i>
1533+
%4 = cir.get_bitfield(#bfi_d, %3 : !cir.ptr<!u32i>) -> !s32i
1534+
```
1535+
}];
1536+
1537+
let arguments = (ins
1538+
AnyType:$addr,
1539+
BitfieldInfoAttr:$bitfield_info
1540+
);
1541+
1542+
let results = (outs CIR_IntType:$result);
1543+
1544+
let assemblyFormat = [{ `(`$bitfield_info `,` $addr attr-dict `:`
1545+
type($addr) `)` `->` type($result) }];
1546+
1547+
let builders = [
1548+
OpBuilder<(ins "Type":$type,
1549+
"Value":$addr,
1550+
"Type":$storage_type,
1551+
"StringRef":$name,
1552+
"unsigned":$size,
1553+
"unsigned":$offset,
1554+
"bool":$is_signed
1555+
),
1556+
[{
1557+
BitfieldInfoAttr info =
1558+
BitfieldInfoAttr::get($_builder.getContext(),
1559+
name, storage_type,
1560+
size, offset, is_signed);
1561+
build($_builder, $_state, type, addr, info);
1562+
}]>
1563+
];
1564+
}
1565+
14181566
//===----------------------------------------------------------------------===//
14191567
// GetMemberOp
14201568
//===----------------------------------------------------------------------===//

clang/lib/CIR/CodeGen/CIRGenBuilder.h

+21
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#define LLVM_CLANG_LIB_CIR_CIRGENBUILDER_H
1111

1212
#include "Address.h"
13+
#include "CIRGenRecordLayout.h"
1314
#include "CIRDataLayout.h"
1415
#include "CIRGenTypeCache.h"
1516
#include "UnimplementedFeatureGuarding.h"
@@ -661,6 +662,26 @@ class CIRGenBuilderTy : public CIRBaseBuilderTy {
661662
global.getLoc(), getPointerTo(global.getSymType()), global.getName());
662663
}
663664

665+
mlir::Value createGetBitfield(mlir::Location loc, mlir::Type resultType,
666+
mlir::Value addr, mlir::Type storageType,
667+
const CIRGenBitFieldInfo &info,
668+
bool useVolatile) {
669+
auto offset = useVolatile ? info.VolatileOffset : info.Offset;
670+
return create<mlir::cir::GetBitfieldOp>(loc, resultType, addr, storageType,
671+
info.Name, info.Size,
672+
offset, info.IsSigned);
673+
}
674+
675+
mlir::Value createSetBitfield(mlir::Location loc, mlir::Type resultType,
676+
mlir::Value dstAddr, mlir::Type storageType,
677+
mlir::Value src, const CIRGenBitFieldInfo &info,
678+
bool useVolatile) {
679+
auto offset = useVolatile ? info.VolatileOffset : info.Offset;
680+
return create<mlir::cir::SetBitfieldOp>(
681+
loc, resultType, dstAddr, storageType, src, info.Name,
682+
info.Size, offset, info.IsSigned);
683+
}
684+
664685
/// Create a pointer to a record member.
665686
mlir::Value createGetMember(mlir::Location loc, mlir::Type result,
666687
mlir::Value base, llvm::StringRef name,

0 commit comments

Comments
 (0)