Skip to content

Commit

Permalink
[CIR] Add a new volatile flag to distinguish volatile accesses (#402)
Browse files Browse the repository at this point in the history
This patch adds a new `volatile` tag to the following operations to
distinguish volatile loads and stores from normal loads and stores:

- `cir.load`
- `cir.store`
- `cir.get_bitfield`
- `cir.set_bitfield`

Besides, this patch also updates the CodeGen and LLVMIR lowering code to
start emitting CIR and LLVMIR operations with volatile flag.
  • Loading branch information
Lancern authored Jan 31, 2024
1 parent aa157fe commit afb7c68
Show file tree
Hide file tree
Showing 7 changed files with 151 additions and 38 deletions.
50 changes: 36 additions & 14 deletions clang/include/clang/CIR/Dialect/IR/CIROps.td
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,8 @@ def LoadOp : CIR_Op<"load", [
`cir.load` reads a value (lvalue to rvalue conversion) given an address
backed up by a `cir.ptr` type. A unit attribute `deref` can be used to
mark the resulting value as used by another operation to dereference
a pointer.
a pointer. A unit attribute `volatile` can be used to indicate a volatile
loading.

Example:

Expand All @@ -385,18 +386,22 @@ def LoadOp : CIR_Op<"load", [
// Load address from memory at address %0. %3 is used by at least one
// operation that dereferences a pointer.
%3 = cir.load deref %0 : cir.ptr <!cir.ptr<i32>>

// Perform a volatile load from address in %0.
%4 = cir.load volatile %0 : !cir.ptr<i32>, i32
```
}];

let arguments = (ins Arg<CIR_PointerType, "the address to load from",
[MemRead]>:$addr, UnitAttr:$isDeref);
[MemRead]>:$addr, UnitAttr:$isDeref,
UnitAttr:$is_volatile);
let results = (outs CIR_AnyType:$result);

// FIXME: we should not be printing `cir.ptr` below, that should come
// from the pointer type directly.
let assemblyFormat = [{
(`deref` $isDeref^)? $addr `:` `cir.ptr` type($addr) `,`
type($result) attr-dict
(`deref` $isDeref^)? (`volatile` $is_volatile^)?
$addr `:` `cir.ptr` type($addr) `,` type($result) attr-dict
}];

// FIXME: add verifier.
Expand All @@ -414,24 +419,31 @@ def StoreOp : CIR_Op<"store", [
let summary = "Store value to memory address";
let description = [{
`cir.store` stores a value (first operand) to the memory address specified
in the second operand.
in the second operand. A unit attribute `volatile` can be used to indicate
a volatile store.

Example:

```mlir
// Store a function argument to local storage, address in %0.
cir.store %arg0, %0 : i32, !cir.ptr<i32>

// Perform a volatile store into memory location at the address in %0.
cir.store volatile %arg0, %0 : i32, !cir.ptr<i32>
```
}];

let arguments = (ins CIR_AnyType:$value,
Arg<CIR_PointerType, "the address to store the value",
[MemWrite]>:$addr);
[MemWrite]>:$addr,
UnitAttr:$is_volatile);

// FIXME: we should not be printing `cir.ptr` below, that should come
// from the pointer type directly.
let assemblyFormat =
"$value `,` $addr attr-dict `:` type($value) `,` `cir.ptr` type($addr)";
let assemblyFormat = [{
(`volatile` $is_volatile^)?
$value `,` $addr attr-dict `:` type($value) `,` `cir.ptr` type($addr)
}];

// FIXME: add verifier.
}
Expand Down Expand Up @@ -1554,6 +1566,9 @@ def SetBitfieldOp : CIR_Op<"set_bitfield"> {
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.

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.set_bitfield` operation sets the value
Expand Down Expand Up @@ -1587,7 +1602,8 @@ def SetBitfieldOp : CIR_Op<"set_bitfield"> {
let arguments = (ins
CIR_PointerType:$dst,
CIR_AnyType:$src,
BitfieldInfoAttr:$bitfield_info
BitfieldInfoAttr:$bitfield_info,
UnitAttr:$is_volatile
);

let results = (outs CIR_IntType:$result);
Expand All @@ -1603,14 +1619,15 @@ def SetBitfieldOp : CIR_Op<"set_bitfield"> {
"StringRef":$name,
"unsigned":$size,
"unsigned":$offset,
"bool":$is_signed
"bool":$is_signed,
"bool":$is_volatile
),
[{
BitfieldInfoAttr info =
BitfieldInfoAttr::get($_builder.getContext(),
name, storage_type,
size, offset, is_signed);
build($_builder, $_state, type, dst, src, info);
build($_builder, $_state, type, dst, src, info, is_volatile);
}]>
];
}
Expand All @@ -1629,6 +1646,9 @@ def GetBitfieldOp : CIR_Op<"get_bitfield"> {
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
Expand Down Expand Up @@ -1660,7 +1680,8 @@ def GetBitfieldOp : CIR_Op<"get_bitfield"> {

let arguments = (ins
CIR_PointerType:$addr,
BitfieldInfoAttr:$bitfield_info
BitfieldInfoAttr:$bitfield_info,
UnitAttr:$is_volatile
);

let results = (outs CIR_IntType:$result);
Expand All @@ -1675,14 +1696,15 @@ def GetBitfieldOp : CIR_Op<"get_bitfield"> {
"StringRef":$name,
"unsigned":$size,
"unsigned":$offset,
"bool":$is_signed
"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);
build($_builder, $_state, type, addr, info, is_volatile);
}]>
];
}
Expand Down
12 changes: 6 additions & 6 deletions clang/lib/CIR/CodeGen/CIRGenBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -712,21 +712,21 @@ class CIRGenBuilderTy : public CIRBaseBuilderTy {
mlir::Value createGetBitfield(mlir::Location loc, mlir::Type resultType,
mlir::Value addr, mlir::Type storageType,
const CIRGenBitFieldInfo &info,
bool useVolatile) {
bool isLvalueVolatile, bool useVolatile) {
auto offset = useVolatile ? info.VolatileOffset : info.Offset;
return create<mlir::cir::GetBitfieldOp>(loc, resultType, addr, storageType,
info.Name, info.Size, offset,
info.IsSigned);
info.IsSigned, isLvalueVolatile);
}

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

/// Create a pointer to a record member.
Expand Down
24 changes: 13 additions & 11 deletions clang/lib/CIR/CodeGen/CIRGenExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -537,9 +537,9 @@ mlir::Value CIRGenFunction::buildToMemory(mlir::Value Value, QualType Ty) {
}

void CIRGenFunction::buildStoreOfScalar(mlir::Value value, LValue lvalue) {
// TODO: constant matrix type, volatile, no init, non temporal, TBAA
buildStoreOfScalar(value, lvalue.getAddress(), false, lvalue.getType(),
lvalue.getBaseInfo(), false, false);
// TODO: constant matrix type, no init, non temporal, TBAA
buildStoreOfScalar(value, lvalue.getAddress(), lvalue.isVolatile(),
lvalue.getType(), lvalue.getBaseInfo(), false, false);
}

void CIRGenFunction::buildStoreOfScalar(mlir::Value Value, Address Addr,
Expand Down Expand Up @@ -570,7 +570,8 @@ void CIRGenFunction::buildStoreOfScalar(mlir::Value Value, Address Addr,
}

assert(currSrcLoc && "must pass in source location");
builder.create<mlir::cir::StoreOp>(*currSrcLoc, Value, Addr.getPointer());
builder.create<mlir::cir::StoreOp>(*currSrcLoc, Value, Addr.getPointer(),
Volatile);

if (isNontemporal) {
llvm_unreachable("NYI");
Expand Down Expand Up @@ -617,9 +618,9 @@ RValue CIRGenFunction::buildLoadOfBitfieldLValue(LValue LV,
bool useVolatile = LV.isVolatileQualified() &&
info.VolatileStorageSize != 0 && isAAPCS(CGM.getTarget());

auto field =
builder.createGetBitfield(getLoc(Loc), resLTy, ptr.getPointer(),
ptr.getElementType(), info, useVolatile);
auto field = builder.createGetBitfield(getLoc(Loc), resLTy, ptr.getPointer(),
ptr.getElementType(), info,
LV.isVolatile(), useVolatile);
assert(!UnimplementedFeature::emitScalarRangeCheck() && "NYI");
return RValue::get(field);
}
Expand Down Expand Up @@ -677,9 +678,9 @@ void CIRGenFunction::buildStoreThroughBitfieldLValue(RValue Src, LValue Dst,

mlir::Value dstAddr = Dst.getAddress().getPointer();

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

static LValue buildGlobalVarDeclLValue(CIRGenFunction &CGF, const Expr *E,
Expand Down Expand Up @@ -2403,7 +2404,8 @@ mlir::Value CIRGenFunction::buildLoadOfScalar(Address Addr, bool Volatile,
}

mlir::cir::LoadOp Load = builder.create<mlir::cir::LoadOp>(
Loc, Addr.getElementType(), Addr.getPointer());
Loc, Addr.getElementType(), Addr.getPointer(), /* deref */ false,
Volatile);

if (isNontemporal) {
llvm_unreachable("NYI");
Expand Down
6 changes: 3 additions & 3 deletions clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -315,8 +315,8 @@ void LoweringPreparePass::lowerGetBitfieldOp(GetBitfieldOp op) {
auto resultTy = op.getType();
auto addr = op.getAddr();
auto loc = addr.getLoc();
mlir::Value val =
builder.create<mlir::cir::LoadOp>(loc, storageType, op.getAddr());
mlir::Value val = builder.create<mlir::cir::LoadOp>(
loc, storageType, op.getAddr(), /* deref */ false, op.getIsVolatile());
auto valWidth = val.getType().cast<IntType>().getWidth();

if (info.getIsSigned()) {
Expand Down Expand Up @@ -384,7 +384,7 @@ void LoweringPreparePass::lowerSetBitfieldOp(SetBitfieldOp op) {
srcVal = builder.createOr(val, srcVal);
}

builder.create<mlir::cir::StoreOp>(loc, srcVal, addr);
builder.create<mlir::cir::StoreOp>(loc, srcVal, addr, op.getIsVolatile());

if (!op->getUses().empty()) {
mlir::Value resultVal = maskedVal;
Expand Down
10 changes: 6 additions & 4 deletions clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -890,8 +890,9 @@ class CIRLoadLowering : public mlir::OpConversionPattern<mlir::cir::LoadOp> {
mlir::ConversionPatternRewriter &rewriter) const override {
const auto llvmTy =
getTypeConverter()->convertType(op.getResult().getType());
rewriter.replaceOpWithNewOp<mlir::LLVM::LoadOp>(op, llvmTy,
adaptor.getAddr());
rewriter.replaceOpWithNewOp<mlir::LLVM::LoadOp>(
op, llvmTy, adaptor.getAddr(), /* alignment */ 0,
/* volatile */ op.getIsVolatile());
return mlir::LogicalResult::success();
}
};
Expand All @@ -903,8 +904,9 @@ class CIRStoreLowering : public mlir::OpConversionPattern<mlir::cir::StoreOp> {
mlir::LogicalResult
matchAndRewrite(mlir::cir::StoreOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const override {
rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>(op, adaptor.getValue(),
adaptor.getAddr());
rewriter.replaceOpWithNewOp<mlir::LLVM::StoreOp>(
op, adaptor.getValue(), adaptor.getAddr(),
/* alignment */ 0, /* volatile */ op.getIsVolatile());
return mlir::LogicalResult::success();
}
};
Expand Down
70 changes: 70 additions & 0 deletions clang/test/CIR/CodeGen/volatile.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir-enable -emit-cir %s -o %t.cir
// RUN: FileCheck --input-file=%t.cir %s

int test_load(volatile int *ptr) {
return *ptr;
}

// CHECK: cir.func @_Z9test_loadPVi
// CHECK: %{{.+}} = cir.load volatile

void test_store(volatile int *ptr) {
*ptr = 42;
}

// CHECK: cir.func @_Z10test_storePVi
// CHECK: cir.store volatile

struct Foo {
int x;
volatile int y;
volatile int z: 4;
};

int test_load_field1(volatile Foo *ptr) {
return ptr->x;
}

// CHECK: cir.func @_Z16test_load_field1PV3Foo
// CHECK: %[[MemberAddr:.*]] = cir.get_member
// CHECK: %{{.+}} = cir.load volatile %[[MemberAddr]]

int test_load_field2(Foo *ptr) {
return ptr->y;
}

// CHECK: cir.func @_Z16test_load_field2P3Foo
// CHECK: %[[MemberAddr:.+]] = cir.get_member
// CHECK: %{{.+}} = cir.load volatile %[[MemberAddr]]

int test_load_field3(Foo *ptr) {
return ptr->z;
}

// CHECK: cir.func @_Z16test_load_field3P3Foo
// CHECK: %[[MemberAddr:.+]] = cir.get_member
// CHECK: %{{.+}} = cir.load volatile %[[MemberAddr]]

void test_store_field1(volatile Foo *ptr) {
ptr->x = 42;
}

// CHECK: cir.func @_Z17test_store_field1PV3Foo
// CHECK: %[[MemberAddr:.+]] = cir.get_member
// CHECK: cir.store volatile %{{.+}}, %[[MemberAddr]]

void test_store_field2(Foo *ptr) {
ptr->y = 42;
}

// CHECK: cir.func @_Z17test_store_field2P3Foo
// CHECK: %[[MemberAddr:.+]] = cir.get_member
// CHECK: cir.store volatile %{{.+}}, %[[MemberAddr]]

void test_store_field3(Foo *ptr) {
ptr->z = 4;
}

// CHECK: cir.func @_Z17test_store_field3P3Foo
// CHECK: %[[MemberAddr:.+]] = cir.get_member
// CHECK: cir.store volatile %{{.+}}, %[[MemberAddr]]
17 changes: 17 additions & 0 deletions clang/test/CIR/Lowering/loadstorealloca.cir
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ module {
%2 = cir.load %0 : cir.ptr <!u32i>, !u32i
cir.return %2 : !u32i
}

cir.func @test_volatile() -> !u32i {
%0 = cir.alloca !u32i, cir.ptr <!u32i>, ["x", init] {alignment = 4 : i64}
%1 = cir.const(#cir.int<1> : !u32i) : !u32i
cir.store volatile %1, %0 : !u32i, cir.ptr <!u32i>
%2 = cir.load volatile %0 : cir.ptr <!u32i>, !u32i
cir.return %2 : !u32i
}
}

// MLIR: module {
Expand All @@ -20,3 +28,12 @@ module {
// MLIR-NEXT: llvm.store %2, %1 : i32, !llvm.ptr
// MLIR-NEXT: %3 = llvm.load %1 : !llvm.ptr -> i32
// MLIR-NEXT: return %3 : i32


// MLIR: func @test_volatile() -> i32
// MLIR-NEXT: %0 = llvm.mlir.constant(1 : index) : i64
// MLIR-NEXT: %1 = llvm.alloca %0 x i32 {alignment = 4 : i64} : (i64) -> !llvm.ptr
// MLIR-NEXT: %2 = llvm.mlir.constant(1 : i32) : i32
// MLIR-NEXT: llvm.store volatile %2, %1 : i32, !llvm.ptr
// MLIR-NEXT: %3 = llvm.load volatile %1 : !llvm.ptr -> i32
// MLIR-NEXT: return %3 : i32

0 comments on commit afb7c68

Please sign in to comment.