diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index febd4a46fcc3..cde057dce4d1 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -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:
@@ -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 >
+
+ // Perform a volatile load from address in %0.
+ %4 = cir.load volatile %0 : !cir.ptr, i32
```
}];
let arguments = (ins Arg:$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.
@@ -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
+
+ // Perform a volatile store into memory location at the address in %0.
+ cir.store volatile %arg0, %0 : i32, !cir.ptr
```
}];
let arguments = (ins CIR_AnyType:$value,
Arg:$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.
}
@@ -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
@@ -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);
@@ -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);
}]>
];
}
@@ -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
@@ -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);
@@ -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);
}]>
];
}
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
index e3f15227fb21..ae189f2f26cd 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h
+++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
@@ -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(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(loc, resultType, dstAddr,
- storageType, src, info.Name,
- info.Size, offset, info.IsSigned);
+ return create(
+ loc, resultType, dstAddr, storageType, src, info.Name, info.Size,
+ offset, info.IsSigned, isLvalueVolatile);
}
/// Create a pointer to a record member.
diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index eadab7aa751e..faf106326715 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -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,
@@ -570,7 +570,8 @@ void CIRGenFunction::buildStoreOfScalar(mlir::Value Value, Address Addr,
}
assert(currSrcLoc && "must pass in source location");
- builder.create(*currSrcLoc, Value, Addr.getPointer());
+ builder.create(*currSrcLoc, Value, Addr.getPointer(),
+ Volatile);
if (isNontemporal) {
llvm_unreachable("NYI");
@@ -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);
}
@@ -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,
@@ -2404,7 +2405,8 @@ mlir::Value CIRGenFunction::buildLoadOfScalar(Address Addr, bool Volatile,
}
mlir::cir::LoadOp Load = builder.create(
- Loc, Addr.getElementType(), Addr.getPointer());
+ Loc, Addr.getElementType(), Addr.getPointer(), /* deref */ false,
+ Volatile);
if (isNontemporal) {
llvm_unreachable("NYI");
diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
index 611a35eacc2a..8b592ca1f254 100644
--- a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
@@ -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(loc, storageType, op.getAddr());
+ mlir::Value val = builder.create(
+ loc, storageType, op.getAddr(), /* deref */ false, op.getIsVolatile());
auto valWidth = val.getType().cast().getWidth();
if (info.getIsSigned()) {
@@ -384,7 +384,7 @@ void LoweringPreparePass::lowerSetBitfieldOp(SetBitfieldOp op) {
srcVal = builder.createOr(val, srcVal);
}
- builder.create(loc, srcVal, addr);
+ builder.create(loc, srcVal, addr, op.getIsVolatile());
if (!op->getUses().empty()) {
mlir::Value resultVal = maskedVal;
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index f057bcdee302..4a6e6f8af3e6 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -890,8 +890,9 @@ class CIRLoadLowering : public mlir::OpConversionPattern {
mlir::ConversionPatternRewriter &rewriter) const override {
const auto llvmTy =
getTypeConverter()->convertType(op.getResult().getType());
- rewriter.replaceOpWithNewOp(op, llvmTy,
- adaptor.getAddr());
+ rewriter.replaceOpWithNewOp(
+ op, llvmTy, adaptor.getAddr(), /* alignment */ 0,
+ /* volatile */ op.getIsVolatile());
return mlir::LogicalResult::success();
}
};
@@ -903,8 +904,9 @@ class CIRStoreLowering : public mlir::OpConversionPattern {
mlir::LogicalResult
matchAndRewrite(mlir::cir::StoreOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const override {
- rewriter.replaceOpWithNewOp(op, adaptor.getValue(),
- adaptor.getAddr());
+ rewriter.replaceOpWithNewOp(
+ op, adaptor.getValue(), adaptor.getAddr(),
+ /* alignment */ 0, /* volatile */ op.getIsVolatile());
return mlir::LogicalResult::success();
}
};
diff --git a/clang/test/CIR/CodeGen/volatile.cpp b/clang/test/CIR/CodeGen/volatile.cpp
new file mode 100644
index 000000000000..85466c93271f
--- /dev/null
+++ b/clang/test/CIR/CodeGen/volatile.cpp
@@ -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]]
diff --git a/clang/test/CIR/Lowering/loadstorealloca.cir b/clang/test/CIR/Lowering/loadstorealloca.cir
index 833e2dbb469f..fc3f333db56d 100644
--- a/clang/test/CIR/Lowering/loadstorealloca.cir
+++ b/clang/test/CIR/Lowering/loadstorealloca.cir
@@ -10,6 +10,14 @@ module {
%2 = cir.load %0 : cir.ptr , !u32i
cir.return %2 : !u32i
}
+
+ cir.func @test_volatile() -> !u32i {
+ %0 = cir.alloca !u32i, cir.ptr , ["x", init] {alignment = 4 : i64}
+ %1 = cir.const(#cir.int<1> : !u32i) : !u32i
+ cir.store volatile %1, %0 : !u32i, cir.ptr
+ %2 = cir.load volatile %0 : cir.ptr , !u32i
+ cir.return %2 : !u32i
+ }
}
// MLIR: module {
@@ -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