Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
Original file line number Diff line number Diff line change
Expand Up @@ -521,4 +521,28 @@ def GlobalCtorAttr : CIR_Attr<"GlobalCtor", "globalCtor"> {
];
let skipDefaultBuilders = 1;
}

def BitfieldInfoAttr : CIR_Attr<"BitfieldInfo", "bitfield_info"> {
let parameters = (ins "StringAttr":$name,
"Type":$elt_type,
"uint64_t":$storage_size,
"uint64_t":$size,
"uint64_t":$offset,
"bool":$is_signed);

let assemblyFormat = "`<` struct($name, $elt_type, $storage_size, $size, $offset, $is_signed) `>`";

let builders = [
AttrBuilder<(ins "StringRef":$name,
"Type":$elt_type,
"uint64_t":$storage_size,
"uint64_t":$size,
"uint64_t":$offset,
"bool":$is_signed
), [{
return $_get($_ctxt, StringAttr::get($_ctxt, name), elt_type, storage_size, size, offset, is_signed);
}]>
];
}

#endif // MLIR_CIR_DIALECT_CIR_ATTRS
107 changes: 107 additions & 0 deletions clang/include/clang/CIR/Dialect/IR/CIROps.td
Original file line number Diff line number Diff line change
Expand Up @@ -1415,6 +1415,113 @@ def VTableAddrPointOp : CIR_Op<"vtable.address_point",
let hasVerifier = 1;
}

//===----------------------------------------------------------------------===//
// SetBitfieldOp
//===----------------------------------------------------------------------===//

def SetBitfieldOp : CIR_Op<"set_bitfield"> {
let summary = "Set a bitfield";
let description = [{
The `cir.set_bitfield` operation provides a store-like access to
a bit field of a record.

It expects an address of a storage where to store, a value being stored,
a type of a bitfield, a name of a bit field, a pointer to a storage in the
base record, a size of the storage, a size the bit field, an offset
of the bit field and a sign. Returns a value being stored.

Example:
```mlir
// Suppose we have a struct with multiple bitfields stored in
// different storages. The `cir.set_bitfield` operation sets the value
// of the bitfield

// Sets the bitfield with name a1
%3 = cir.set_bitfield %1, %2(<name = "a1", elt_type = !u16i, storage_size = 16, size = 15, offset = 0, is_signed = true>) : (!s32i, !cir.ptr<!u16i>) -> !s32i
```
}];

let arguments = (ins
AnyType:$dst,
AnyType:$src,
BitfieldInfoAttr:$BitfieldInfo
);

let results = (outs CIR_IntType:$result);

let assemblyFormat = [{ $src `,` $dst`(`$BitfieldInfo`)` attr-dict `:`
`(` type($src) `,` type($dst) `)` `->` type($result) }];

let builders = [
OpBuilder<(ins "Type":$type,
"Value":$dst,
"Type":$eltType,
"Value":$src,
"StringRef":$name,
"unsigned":$storageSize,
"unsigned":$size,
"unsigned":$offset,
"bool":$isSigned
),
[{
BitfieldInfoAttr info = BitfieldInfoAttr::get($_builder.getContext(), name, eltType, storageSize, size, offset, isSigned);
build($_builder, $_state, type, dst, src, info);
}]>
];
}

//===----------------------------------------------------------------------===//
// GetBitfieldOp
//===----------------------------------------------------------------------===//

def GetBitfieldOp : CIR_Op<"get_bitfield"> {
let summary = "Get a bitfield";
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 size of the storage, a size the bit field, an offset
of the bit field and a sign.

Example:
```mlir
// Suppose we have a struct with multiple bitfields stored in
// different storages. The `cir.get_bitfield` operation gets the value
// of the bitfield

// Get the address of the bitfield with name a1.
%6 = "cir.get_bitfield"(%5) <{BitfieldInfo = #cir.bitfieldInfo<(name = "a1") (size 4) (storage !u64i 64) (offset 0) (signed = true)>}> : (!cir.ptr<!u64i>) -> !s32i
```
}];

let arguments = (ins
AnyType:$addr,
BitfieldInfoAttr:$BitfieldInfo
);

let results = (outs CIR_IntType:$result);

let assemblyFormat = [{ $addr`(`$BitfieldInfo`)` attr-dict `:`
type($addr) `->` type($result) }];

let builders = [
OpBuilder<(ins "Type":$type,
"Value":$addr,
"Type":$eltType,
"StringRef":$name,
"unsigned":$storageSize,
"unsigned":$size,
"unsigned":$offset,
"bool":$isSigned
),
[{
BitfieldInfoAttr info = BitfieldInfoAttr::get($_builder.getContext(), name, eltType, storageSize, size, offset, isSigned);
build($_builder, $_state, type, addr, info);
}]>
];
}

//===----------------------------------------------------------------------===//
// GetMemberOp
//===----------------------------------------------------------------------===//
Expand Down
26 changes: 25 additions & 1 deletion clang/lib/CIR/CodeGen/CIRGenBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#define LLVM_CLANG_LIB_CIR_CIRGENBUILDER_H

#include "Address.h"
#include "CIRGenRecordLayout.h"
#include "CIRGenTypeCache.h"
#include "UnimplementedFeatureGuarding.h"

Expand Down Expand Up @@ -635,6 +636,30 @@ class CIRGenBuilderTy : public CIRBaseBuilderTy {
global.getLoc(), getPointerTo(global.getSymType()), global.getName());
}

mlir::Value createGetBitfield(mlir::Location loc, mlir::Type resultType,
mlir::Value addr, mlir::Type eltType,
const CIRGenBitFieldInfo &info,
bool useVolatile) {
auto offset = useVolatile ? info.VolatileOffset : info.Offset;
auto storageSize =
useVolatile ? info.VolatileStorageSize : info.StorageSize;
return create<mlir::cir::GetBitfieldOp>(loc, resultType, addr, eltType,
info.Name, storageSize, info.Size,
offset, info.IsSigned);
}

mlir::Value createSetBitfield(mlir::Location loc, mlir::Type resultType,
mlir::Value dstAddr, mlir::Type eltType,
mlir::Value src, const CIRGenBitFieldInfo &info,
bool useVolatile) {
auto storageSize =
useVolatile ? info.VolatileStorageSize : info.StorageSize;
auto offset = useVolatile ? info.VolatileOffset : info.Offset;
return create<mlir::cir::SetBitfieldOp>(
loc, resultType, dstAddr, eltType, src, info.Name, storageSize,
info.Size, offset, info.IsSigned);
}

/// Create a pointer to a record member.
mlir::Value createGetMember(mlir::Location loc, mlir::Type result,
mlir::Value base, llvm::StringRef name,
Expand Down Expand Up @@ -689,7 +714,6 @@ class CIRGenBuilderTy : public CIRBaseBuilderTy {
auto flag = getBool(val, loc);
return create<mlir::cir::StoreOp>(loc, flag, dst);
}

};

} // namespace cir
Expand Down
147 changes: 36 additions & 111 deletions clang/lib/CIR/CodeGen/CIRGenExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -220,14 +220,14 @@ static bool isAAPCS(const TargetInfo &TargetInfo) {
return TargetInfo.getABI().startswith("aapcs");
}

Address CIRGenFunction::getAddrOfBitFieldStorage(LValue base,
Address CIRGenFunction::getAddrOfBitFieldStorage(LValue base,
const FieldDecl *field,
unsigned index,
unsigned size) {
if (index == 0)
return base.getAddress();

auto loc = getLoc(field->getLocation());
auto loc = getLoc(field->getLocation());
auto fieldType = builder.getUIntNTy(size);

auto fieldPtr =
Expand Down Expand Up @@ -266,7 +266,6 @@ LValue CIRGenFunction::buildLValueForBitField(LValue base,

const unsigned SS = useVolatile ? info.VolatileStorageSize : info.StorageSize;
Address Addr = getAddrOfBitFieldStorage(base, field, Idx, SS);

// Get the access type.
mlir::Type FieldIntTy = builder.getUIntNTy(SS);

Expand All @@ -276,7 +275,6 @@ LValue CIRGenFunction::buildLValueForBitField(LValue base,

QualType fieldType =
field->getType().withCVRQualifiers(base.getVRQualifiers());

assert(!UnimplementedFeature::tbaa() && "NYI TBAA for bit fields");
LValueBaseInfo FieldBaseInfo(BaseInfo.getAlignmentSource());
return LValue::MakeBitfield(Addr, info, fieldType, FieldBaseInfo);
Expand Down Expand Up @@ -398,7 +396,7 @@ LValue CIRGenFunction::buildLValueForFieldInitialization(

auto& layout = CGM.getTypes().getCIRGenRecordLayout(Field->getParent());
unsigned FieldIndex = layout.getCIRFieldNo(Field);

Address V = buildAddrOfFieldStorage(*this, Base.getAddress(), Field,
FieldName, FieldIndex);

Expand Down Expand Up @@ -607,42 +605,20 @@ RValue CIRGenFunction::buildLoadOfLValue(LValue LV, SourceLocation Loc) {

RValue CIRGenFunction::buildLoadOfBitfieldLValue(LValue LV,
SourceLocation Loc) {
const CIRGenBitFieldInfo &Info = LV.getBitFieldInfo();
const CIRGenBitFieldInfo &info = LV.getBitFieldInfo();

// Get the output type.
mlir::Type ResLTy = convertType(LV.getType());
Address Ptr = LV.getBitFieldAddress();
mlir::Value Val = builder.createLoad(getLoc(Loc), Ptr);
auto ValWidth = Val.getType().cast<IntType>().getWidth();

bool UseVolatile = LV.isVolatileQualified() &&
Info.VolatileStorageSize != 0 && isAAPCS(CGM.getTarget());
const unsigned Offset = UseVolatile ? Info.VolatileOffset : Info.Offset;
const unsigned StorageSize =
UseVolatile ? Info.VolatileStorageSize : Info.StorageSize;

if (Info.IsSigned) {
assert(static_cast<unsigned>(Offset + Info.Size) <= StorageSize);

mlir::Type typ = builder.getSIntNTy(ValWidth);
Val = builder.createIntCast(Val, typ);

unsigned HighBits = StorageSize - Offset - Info.Size;
if (HighBits)
Val = builder.createShiftLeft(Val, HighBits);
if (Offset + HighBits)
Val = builder.createShiftRight(Val, Offset + HighBits);
} else {
if (Offset)
Val = builder.createShiftRight(Val, Offset);
mlir::Type resLTy = convertType(LV.getType());
Address ptr = LV.getBitFieldAddress();

if (static_cast<unsigned>(Offset) + Info.Size < StorageSize)
Val = builder.createAnd(Val,
llvm::APInt::getLowBitsSet(ValWidth, Info.Size));
}
Val = builder.createIntCast(Val, ResLTy);
assert(!UnimplementedFeature::emitScalarRangeCheck() && "NYI");
return RValue::get(Val);
bool useVolatile = LV.isVolatileQualified() &&
info.VolatileStorageSize != 0 && isAAPCS(CGM.getTarget());

auto field =
builder.createGetBitfield(getLoc(Loc), resLTy, ptr.getPointer(),
ptr.getElementType(), info, useVolatile);
assert(!UnimplementedFeature::emitScalarRangeCheck() && "NYI");
return RValue::get(field);
}

void CIRGenFunction::buildStoreThroughLValue(RValue Src, LValue Dst) {
Expand All @@ -667,79 +643,28 @@ void CIRGenFunction::buildStoreThroughLValue(RValue Src, LValue Dst) {

void CIRGenFunction::buildStoreThroughBitfieldLValue(RValue Src, LValue Dst,
mlir::Value &Result) {
const CIRGenBitFieldInfo &Info = Dst.getBitFieldInfo();
mlir::Type ResLTy = getTypes().convertTypeForMem(Dst.getType());
Address Ptr = Dst.getBitFieldAddress();

// Get the source value, truncated to the width of the bit-field.
mlir::Value SrcVal = Src.getScalarVal();

// Cast the source to the storage type and shift it into place.
SrcVal = builder.createIntCast(SrcVal, Ptr.getElementType());
auto SrcWidth = SrcVal.getType().cast<IntType>().getWidth();
mlir::Value MaskedVal = SrcVal;

const bool UseVolatile =
// According to the AACPS:
// When a volatile bit-field is written, and its container does not overlap
// with any non-bit-field member, its container must be read exactly once
// and written exactly once using the access width appropriate to the type
// of the container. The two accesses are not atomic.
if (Dst.isVolatileQualified() && isAAPCS(CGM.getTarget()) &&
CGM.getCodeGenOpts().ForceAAPCSBitfieldLoad)
llvm_unreachable("volatile bit-field is not implemented for the AACPS");

const CIRGenBitFieldInfo &info = Dst.getBitFieldInfo();
mlir::Type resLTy = getTypes().convertTypeForMem(Dst.getType());
Address ptr = Dst.getBitFieldAddress();

const bool useVolatile =
CGM.getCodeGenOpts().AAPCSBitfieldWidth && Dst.isVolatileQualified() &&
Info.VolatileStorageSize != 0 && isAAPCS(CGM.getTarget());
const unsigned StorageSize =
UseVolatile ? Info.VolatileStorageSize : Info.StorageSize;
const unsigned Offset = UseVolatile ? Info.VolatileOffset : Info.Offset;
// See if there are other bits in the bitfield's storage we'll need to load
// and mask together with source before storing.
if (StorageSize != Info.Size) {
assert(StorageSize > Info.Size && "Invalid bitfield size.");

mlir::Value Val = buildLoadOfScalar(Dst, Dst.getPointer().getLoc());

// Mask the source value as needed.
if (!hasBooleanRepresentation(Dst.getType()))
SrcVal = builder.createAnd(
SrcVal, llvm::APInt::getLowBitsSet(SrcWidth, Info.Size));

MaskedVal = SrcVal;
if (Offset)
SrcVal = builder.createShiftLeft(SrcVal, Offset);

// Mask out the original value.
Val = builder.createAnd(
Val, ~llvm::APInt::getBitsSet(SrcWidth, Offset, Offset + Info.Size));
info.VolatileStorageSize != 0 && isAAPCS(CGM.getTarget());

// Or together the unchanged values and the source value.
SrcVal = builder.createOr(Val, SrcVal);
mlir::Value dstAddr = Dst.getAddress().getPointer();

} else {
// According to the AACPS:
// When a volatile bit-field is written, and its container does not overlap
// with any non-bit-field member, its container must be read exactly once
// and written exactly once using the access width appropriate to the type
// of the container. The two accesses are not atomic.
if (Dst.isVolatileQualified() && isAAPCS(CGM.getTarget()) &&
CGM.getCodeGenOpts().ForceAAPCSBitfieldLoad)
llvm_unreachable("volatile bit-field is not implemented for the AACPS");
}

// Write the new value back out.
// TODO: constant matrix type, volatile, no init, non temporal, TBAA
buildStoreOfScalar(SrcVal, Ptr, Dst.isVolatileQualified(), Dst.getType(),
Dst.getBaseInfo(), false, false);

// Return the new value of the bit-field.
mlir::Value ResultVal = MaskedVal;
ResultVal = builder.createIntCast(ResultVal, ResLTy);

// Sign extend the value if needed.
if (Info.IsSigned) {
assert(Info.Size <= StorageSize);
unsigned HighBits = StorageSize - Info.Size;

if (HighBits) {
ResultVal = builder.createShiftLeft(ResultVal, HighBits);
ResultVal = builder.createShiftRight(ResultVal, HighBits);
}
}

Result = buildFromMemory(ResultVal, Dst.getType());
Result = builder.createSetBitfield(dstAddr.getLoc(), resLTy, dstAddr,
ptr.getElementType(), Src.getScalarVal(),
info, useVolatile);
}

static LValue buildGlobalVarDeclLValue(CIRGenFunction &CGF, const Expr *E,
Expand Down Expand Up @@ -2455,9 +2380,9 @@ mlir::Value CIRGenFunction::buildLoadOfScalar(Address Addr, bool Volatile,
if (isNontemporal) {
llvm_unreachable("NYI");
}
assert(!UnimplementedFeature::tbaa() && "NYI");
assert(!UnimplementedFeature::emitScalarRangeCheck() && "NYI");

assert(!UnimplementedFeature::tbaa() && "NYI");
assert(!UnimplementedFeature::emitScalarRangeCheck() && "NYI");

return buildFromMemory(Load, Ty);
}
Expand Down
Loading