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