-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[CIR] Upstream get_bitfield operation to load bit-field members from structs #145971
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
d2e19a9
a94f579
1f3479e
24d6703
fccad21
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -375,4 +375,67 @@ def CIR_VisibilityAttr : CIR_EnumAttr<CIR_VisibilityKind, "visibility"> { | |
}]; | ||
} | ||
|
||
//===----------------------------------------------------------------------===// | ||
// BitfieldInfoAttr | ||
//===----------------------------------------------------------------------===// | ||
|
||
def BitfieldInfoAttr : CIR_Attr<"BitfieldInfo", "bitfield_info"> { | ||
let summary = "Represents a bit field info"; | ||
let description = [{ | ||
Holds the following information about bitfields: name, storage type, size | ||
and position in the storage, and signedness. | ||
Example: | ||
Given the following struct with bitfields: | ||
```c++ | ||
typedef struct { | ||
int a : 4; | ||
int b : 27; | ||
int c : 17; | ||
int d : 2; | ||
int e : 15; | ||
} S; | ||
``` | ||
|
||
The CIR representation of the struct `S` might look like: | ||
```mlir | ||
!rec_S = !cir.record<struct "S" packed padded {!u64i, !u16i, | ||
!cir.array<!u8i x 2>}> | ||
``` | ||
And the bitfield info attribute for member `a` would be: | ||
```mlir | ||
#bfi_a = #cir.bitfield_info<name = "a", storage_type = !u64i, | ||
size = 4, offset = 0, is_signed = true> | ||
``` | ||
|
||
This metadata describes that field `a` is stored in a 64-bit integer, | ||
is 4 bits wide, starts at offset 0, and is signed. | ||
}]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you add an example? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
let parameters = (ins "mlir::StringAttr":$name, | ||
"mlir::Type":$storageType, | ||
"uint64_t":$size, | ||
"uint64_t":$offset, | ||
"bool":$isSigned); | ||
|
||
let assemblyFormat = [{`<` struct($name, | ||
$storageType, | ||
$size, | ||
$offset, | ||
$isSigned) | ||
`>` | ||
}]; | ||
|
||
let builders = [ | ||
AttrBuilder<(ins "llvm::StringRef":$name, | ||
"mlir::Type":$storageType, | ||
"uint64_t":$size, | ||
"uint64_t":$offset, | ||
"bool":$isSigned | ||
), [{ | ||
return $_get($_ctxt, mlir::StringAttr::get($_ctxt, name), storageType, | ||
size, offset, isSigned); | ||
}]> | ||
]; | ||
} | ||
|
||
|
||
#endif // LLVM_CLANG_CIR_DIALECT_IR_CIRATTRS_TD |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -1669,6 +1669,85 @@ def GetGlobalOp : CIR_Op<"get_global", | |||||
}]; | ||||||
} | ||||||
|
||||||
//===----------------------------------------------------------------------===// | ||||||
// GetBitfieldOp | ||||||
//===----------------------------------------------------------------------===// | ||||||
|
||||||
def GetBitfieldOp : CIR_Op<"get_bitfield"> { | ||||||
let summary = "Get a bitfield"; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
let description = [{ | ||||||
The `cir.get_bitfield` operation provides a load-like access to | ||||||
a bit field of a record. | ||||||
|
||||||
It expects a name if a bit field, a pointer to a storage in the | ||||||
base record, a type of the storage, a name of the bitfield, | ||||||
a size the bit field, an offset of the bit field and a sign. | ||||||
|
||||||
A unit attribute `volatile` can be used to indicate a volatile load of the | ||||||
bitfield. | ||||||
|
||||||
Example: | ||||||
Suppose we have a struct with multiple bitfields stored in | ||||||
different storages. The `cir.get_bitfield` operation gets the value | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
of the bitfield | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
```C++ | ||||||
typedef struct { | ||||||
int a : 4; | ||||||
int b : 27; | ||||||
int c : 17; | ||||||
int d : 2; | ||||||
int e : 15; | ||||||
} S; | ||||||
|
||||||
int load_bitfield(S& s) { | ||||||
return s.e; | ||||||
} | ||||||
``` | ||||||
|
||||||
```mlir | ||||||
// 'e' is in the storage with the index 1 | ||||||
!cir.record<struct "S" packed padded {!u64i, !u16i, !cir.array<!u8i x 2>}> | ||||||
#bfi_e = #cir.bitfield_info<name = "e", storage_type = !u16i, size = 15, | ||||||
offset = 0, is_signed = true> | ||||||
|
||||||
%2 = cir.load %0 : !cir.ptr<!cir.ptr<!record_type>>, !cir.ptr<!record_type> | ||||||
%3 = cir.get_member %2[1] {name = "e"} : !cir.ptr<!record_type> | ||||||
-> !cir.ptr<!u16i> | ||||||
%4 = cir.get_bitfield(#bfi_e, %3 : !cir.ptr<!u16i>) -> !s32i | ||||||
``` | ||||||
}]; | ||||||
|
||||||
let arguments = (ins | ||||||
Arg<CIR_PointerType, "the address to load from", [MemRead]>:$addr, | ||||||
BitfieldInfoAttr:$bitfield_info, | ||||||
UnitAttr:$is_volatile | ||||||
); | ||||||
|
||||||
let results = (outs CIR_IntType:$result); | ||||||
|
||||||
let assemblyFormat = [{ `(`$bitfield_info `,` $addr attr-dict `:` | ||||||
qualified(type($addr)) `)` `->` type($result) }]; | ||||||
|
||||||
let builders = [ | ||||||
OpBuilder<(ins "mlir::Type":$type, | ||||||
"mlir::Value":$addr, | ||||||
"mlir::Type":$storage_type, | ||||||
"llvm::StringRef":$name, | ||||||
"unsigned":$size, | ||||||
"unsigned":$offset, | ||||||
"bool":$is_signed, | ||||||
"bool":$is_volatile | ||||||
), | ||||||
[{ | ||||||
BitfieldInfoAttr info = | ||||||
BitfieldInfoAttr::get($_builder.getContext(), | ||||||
name, storage_type, | ||||||
size, offset, is_signed); | ||||||
build($_builder, $_state, type, addr, info, is_volatile); | ||||||
}]> | ||||||
]; | ||||||
} | ||||||
|
||||||
//===----------------------------------------------------------------------===// | ||||||
// GetMemberOp | ||||||
//===----------------------------------------------------------------------===// | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -326,13 +326,62 @@ mlir::Value CIRGenFunction::emitStoreThroughBitfieldLValue(RValue src, | |
return {}; | ||
} | ||
|
||
RValue CIRGenFunction::emitLoadOfBitfieldLValue(LValue lv, SourceLocation loc) { | ||
const CIRGenBitFieldInfo &info = lv.getBitFieldInfo(); | ||
|
||
// Get the output type. | ||
mlir::Type resLTy = convertType(lv.getType()); | ||
Address ptr = lv.getBitFieldAddress(); | ||
|
||
assert(!cir::MissingFeatures::armComputeVolatileBitfields()); | ||
|
||
mlir::Value field = builder.createGetBitfield(getLoc(loc), resLTy, ptr.getPointer(), | ||
ptr.getElementType(), info, | ||
lv.isVolatile(), false); | ||
assert(!cir::MissingFeatures::opLoadEmitScalarRangeCheck() && "NYI"); | ||
return RValue::get(field); | ||
} | ||
|
||
Address CIRGenFunction::getAddrOfBitFieldStorage(LValue base, | ||
const FieldDecl *field, | ||
mlir::Type fieldType, | ||
unsigned index) { | ||
mlir::Location loc = getLoc(field->getLocation()); | ||
cir::PointerType fieldPtr = cir::PointerType::get(fieldType); | ||
cir::GetMemberOp sea = getBuilder().createGetMember( | ||
loc, fieldPtr, base.getPointer(), field->getName(), index); | ||
return Address(sea, CharUnits::One()); | ||
} | ||
|
||
LValue CIRGenFunction::emitLValueForBitField(LValue base, | ||
const FieldDecl *field) { | ||
LValueBaseInfo baseInfo = base.getBaseInfo(); | ||
const CIRGenRecordLayout &layout = | ||
cgm.getTypes().getCIRGenRecordLayout(field->getParent()); | ||
const CIRGenBitFieldInfo &info = layout.getBitFieldInfo(field); | ||
assert(!cir::MissingFeatures::armComputeVolatileBitfields()); | ||
assert(!cir::MissingFeatures::preservedAccessIndexRegion()); | ||
unsigned idx = layout.getCIRFieldNo(field); | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you add a missing feature marker here for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
Address addr = getAddrOfBitFieldStorage(base, field, info.storageType, idx); | ||
|
||
mlir::Location loc = getLoc(field->getLocation()); | ||
if (addr.getElementType() != info.storageType) | ||
addr = builder.createElementBitCast(loc, addr, info.storageType); | ||
|
||
QualType fieldType = | ||
field->getType().withCVRQualifiers(base.getVRQualifiers()); | ||
// TODO(cir): Support TBAA for bit fields. | ||
assert(!cir::MissingFeatures::opTBAA()); | ||
LValueBaseInfo fieldBaseInfo(baseInfo.getAlignmentSource()); | ||
return LValue::makeBitfield(addr, info, fieldType, fieldBaseInfo); | ||
} | ||
|
||
LValue CIRGenFunction::emitLValueForField(LValue base, const FieldDecl *field) { | ||
LValueBaseInfo baseInfo = base.getBaseInfo(); | ||
|
||
if (field->isBitField()) { | ||
cgm.errorNYI(field->getSourceRange(), "emitLValueForField: bitfield"); | ||
return LValue(); | ||
} | ||
if (field->isBitField()) | ||
return emitLValueForBitField(base, field); | ||
|
||
QualType fieldType = field->getType(); | ||
const RecordDecl *rec = field->getParent(); | ||
|
@@ -460,6 +509,9 @@ RValue CIRGenFunction::emitLoadOfLValue(LValue lv, SourceLocation loc) { | |
assert(!lv.getType()->isFunctionType()); | ||
assert(!(lv.getType()->isConstantMatrixType()) && "not implemented"); | ||
|
||
if (lv.isBitField()) | ||
return emitLoadOfBitfieldLValue(lv, loc); | ||
|
||
if (lv.isSimple()) | ||
return RValue::get(emitLoadOfScalar(lv, loc)); | ||
|
||
|
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
|
@@ -65,6 +65,10 @@ struct CIROpAsmDialectInterface : public OpAsmDialectInterface { | |||
os << (boolAttr.getValue() ? "true" : "false"); | ||||
return AliasResult::FinalAlias; | ||||
} | ||||
if (auto bitfield = mlir::dyn_cast<cir::BitfieldInfoAttr>(attr)) { | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you add an IR test to make sure this gets parsed correctly? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was trying to add an IR test for, but I ran into the following parse error when trying to define a struct:
Without the struct definition, I can't meaningfully call Should I consider it sufficient to check that the input runs without errors for now? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I see the problem with parsing complete records. You're right that you can't add your test until that is upstreamed. I think you can just proceed without the test for now. We can add it as a separate PR later. |
||||
os << "bfi_" << bitfield.getName().str(); | ||||
return AliasResult::FinalAlias; | ||||
} | ||||
return AliasResult::NoAlias; | ||||
} | ||||
}; | ||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.