https://github.com/andykaylor created https://github.com/llvm/llvm-project/pull/156124
This fills in the missing pieces to handle volatile loads and stores in CIR. This addresses https://github.com/llvm/llvm-project/issues/153280 >From 99df40768de131846d6b58ac876cafd6dcddc2c0 Mon Sep 17 00:00:00 2001 From: Andy Kaylor <akay...@nvidia.com> Date: Fri, 29 Aug 2025 14:22:41 -0700 Subject: [PATCH] [CIR] Add handling for volatile loads and stores This fills in the missing pieces to handle volatile loads and stores in CIR. This addresses https://github.com/llvm/llvm-project/issues/153280 --- .../CIR/Dialect/Builder/CIRBaseBuilder.h | 12 +- clang/include/clang/CIR/Dialect/IR/CIROps.td | 24 ++- clang/include/clang/CIR/MissingFeatures.h | 3 +- clang/lib/CIR/CodeGen/CIRGenBuilder.h | 2 +- clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 46 +++++- clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp | 6 +- clang/lib/CIR/CodeGen/CIRGenFunction.h | 2 + .../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 9 +- clang/test/CIR/CodeGen/volatile.cpp | 146 ++++++++++++++++++ 9 files changed, 226 insertions(+), 24 deletions(-) create mode 100644 clang/test/CIR/CodeGen/volatile.cpp diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h index d29e5687d2544..fb4cce6e1531e 100644 --- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h +++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h @@ -161,16 +161,15 @@ class CIRBaseBuilderTy : public mlir::OpBuilder { } cir::LoadOp createLoad(mlir::Location loc, mlir::Value ptr, - uint64_t alignment = 0) { + bool isVolatile = false, uint64_t alignment = 0) { mlir::IntegerAttr alignmentAttr = getAlignmentAttr(alignment); - assert(!cir::MissingFeatures::opLoadStoreVolatile()); return cir::LoadOp::create(*this, loc, ptr, /*isDeref=*/false, - alignmentAttr, cir::MemOrderAttr{}); + alignmentAttr, isVolatile, cir::MemOrderAttr{}); } mlir::Value createAlignedLoad(mlir::Location loc, mlir::Value ptr, uint64_t alignment) { - return createLoad(loc, ptr, alignment); + return createLoad(loc, ptr, /*isVolatile=*/false, alignment); } mlir::Value createNot(mlir::Value value) { @@ -251,7 +250,7 @@ class CIRBaseBuilderTy : public mlir::OpBuilder { bool isVolatile = false, mlir::IntegerAttr align = {}, cir::MemOrderAttr order = {}) { - return cir::StoreOp::create(*this, loc, val, dst, align, order); + return cir::StoreOp::create(*this, loc, val, dst, align, isVolatile, order); } [[nodiscard]] cir::GlobalOp createGlobal(mlir::ModuleOp mlirModule, @@ -275,7 +274,8 @@ class CIRBaseBuilderTy : public mlir::OpBuilder { mlir::IntegerAttr alignmentAttr = getAlignmentAttr(alignment); auto addr = createAlloca(loc, getPointerTo(type), type, {}, alignmentAttr); return cir::LoadOp::create(*this, loc, addr, /*isDeref=*/false, - alignmentAttr, /*mem_order=*/{}); + alignmentAttr, /*isVolatile=*/false, + /*mem_order=*/{}); } cir::PtrStrideOp createPtrStride(mlir::Location loc, mlir::Value base, diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 2b7a709b80c26..848cb3c38bc33 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -404,7 +404,11 @@ def CIR_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. Load can be marked atomic by using `atomic(<mem_order>)`. + + `alignment` can be used to specify an alignment that's different from the + default, which is computed from `result`'s type ABI data layout. Example: @@ -416,6 +420,12 @@ def CIR_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 + + // Others + %x = cir.load align(16) atomic(seq_cst) %0 : !cir.ptr<i32>, i32 ``` }]; @@ -423,11 +433,13 @@ def CIR_LoadOp : CIR_Op<"load", [ [MemRead]>:$addr, UnitAttr:$isDeref, OptionalAttr<I64Attr>:$alignment, + UnitAttr:$is_volatile, OptionalAttr<CIR_MemOrder>:$mem_order); let results = (outs CIR_AnyType:$result); let assemblyFormat = [{ (`deref` $isDeref^)? + (`volatile` $is_volatile^)? (`align` `(` $alignment^ `)`)? (`atomic` `(` $mem_order^ `)`)? $addr `:` qualified(type($addr)) `,` type($result) attr-dict @@ -452,7 +464,7 @@ def CIR_StoreOp : CIR_Op<"store", [ a volatile store. Store's can be marked atomic by using `atomic(<mem_order>)`. - `align` can be used to specify an alignment that's different from the + `alignment` can be used to specify an alignment that's different from the default, which is computed from `result`'s type ABI data layout. Example: @@ -460,6 +472,12 @@ def CIR_StoreOp : CIR_Op<"store", [ ```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> + + // Others + cir.store align(16) atomic(seq_cst) %x, %addr : i32, !cir.ptr<i32> ``` }]; @@ -467,9 +485,11 @@ def CIR_StoreOp : CIR_Op<"store", [ Arg<CIR_PointerType, "the address to store the value", [MemWrite]>:$addr, OptionalAttr<I64Attr>:$alignment, + UnitAttr:$is_volatile, OptionalAttr<CIR_MemOrder>:$mem_order); let assemblyFormat = [{ + (`volatile` $is_volatile^)? (`align` `(` $alignment^ `)`)? (`atomic` `(` $mem_order^ `)`)? $value `,` $addr attr-dict `:` type($value) `,` qualified(type($addr)) diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index a8be2a2374d6e..d534c53a82a47 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -47,9 +47,8 @@ struct MissingFeatures { // Load/store attributes static bool opLoadStoreThreadLocal() { return false; } static bool opLoadEmitScalarRangeCheck() { return false; } - static bool opLoadBooleanRepresentation() { return false; } + static bool opLoadStoreNontemporal() { return false; } static bool opLoadStoreTbaa() { return false; } - static bool opLoadStoreVolatile() { return false; } static bool opLoadStoreAtomic() { return false; } static bool opLoadStoreObjC() { return false; } diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h index d5cb6d416bc9b..a7d679e67dc44 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h +++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h @@ -375,7 +375,7 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy { bool isVolatile = false) { mlir::IntegerAttr align = getAlignmentAttr(addr.getAlignment()); return cir::LoadOp::create(*this, loc, addr.getPointer(), /*isDeref=*/false, - /*alignment=*/align, + /*alignment=*/align, isVolatile, /*mem_order=*/cir::MemOrderAttr{}); } diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp index 469879371eb1d..aec60d01fc238 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -325,6 +325,7 @@ void CIRGenFunction::emitStoreOfScalar(mlir::Value value, Address addr, const auto vecTy = cast<cir::VectorType>(elementType); // TODO(CIR): Use `ABIInfo::getOptimalVectorMemoryType` once it upstreamed + assert(!cir::MissingFeatures::cirgenABIInfo()); if (vecTy.getSize() == 3 && !getLangOpts().PreserveVec3Type) cgm.errorNYI(addr.getPointer().getLoc(), "emitStoreOfScalar Vec3 & PreserveVec3Type disabled"); @@ -345,7 +346,7 @@ void CIRGenFunction::emitStoreOfScalar(mlir::Value value, Address addr, } assert(currSrcLoc && "must pass in source location"); - builder.createStore(*currSrcLoc, value, addr /*, isVolatile*/); + builder.createStore(*currSrcLoc, value, addr, isVolatile); if (isNontemporal) { cgm.errorNYI(addr.getPointer().getLoc(), "emitStoreOfScalar nontemporal"); @@ -543,23 +544,52 @@ void CIRGenFunction::emitStoreOfScalar(mlir::Value value, LValue lvalue, lvalue.getType(), isInit, /*isNontemporal=*/false); } -mlir::Value CIRGenFunction::emitLoadOfScalar(LValue lvalue, - SourceLocation loc) { +mlir::Value CIRGenFunction::emitLoadOfScalar(Address addr, bool isVolatile, + QualType ty, SourceLocation loc, + LValueBaseInfo baseInfo) { assert(!cir::MissingFeatures::opLoadStoreThreadLocal()); - assert(!cir::MissingFeatures::opLoadEmitScalarRangeCheck()); - assert(!cir::MissingFeatures::opLoadBooleanRepresentation()); - - Address addr = lvalue.getAddress(); mlir::Type eltTy = addr.getElementType(); + if (const auto *clangVecTy = ty->getAs<clang::VectorType>()) { + if (clangVecTy->isExtVectorBoolType()) { + cgm.errorNYI(loc, "emitLoadOfScalar: ExtVectorBoolType"); + return nullptr; + } + + const auto vecTy = cast<cir::VectorType>(eltTy); + + // Handle vectors of size 3 like size 4 for better performance. + assert(!cir::MissingFeatures::cirgenABIInfo()); + if (vecTy.getSize() == 3 && !getLangOpts().PreserveVec3Type) + cgm.errorNYI(addr.getPointer().getLoc(), + "emitLoadOfScalar Vec3 & PreserveVec3Type disabled"); + } + + assert(!cir::MissingFeatures::opLoadStoreTbaa()); + LValue atomicLValue = LValue::makeAddr(addr, ty, baseInfo); + if (ty->isAtomicType() || isLValueSuitableForInlineAtomic(atomicLValue)) + cgm.errorNYI("emitLoadOfScalar: load atomic"); + if (mlir::isa<cir::VoidType>(eltTy)) cgm.errorNYI(loc, "emitLoadOfScalar: void type"); - mlir::Value loadOp = builder.createLoad(getLoc(loc), addr); + assert(!cir::MissingFeatures::opLoadEmitScalarRangeCheck()); + + mlir::Value loadOp = builder.createLoad(getLoc(loc), addr, isVolatile); + if (!ty->isBooleanType() && ty->hasBooleanRepresentation()) + cgm.errorNYI("emitLoadOfScalar: boolean type with boolean representation"); return loadOp; } +mlir::Value CIRGenFunction::emitLoadOfScalar(LValue lvalue, + SourceLocation loc) { + assert(!cir::MissingFeatures::opLoadStoreNontemporal()); + assert(!cir::MissingFeatures::opLoadStoreTbaa()); + return emitLoadOfScalar(lvalue.getAddress(), lvalue.isVolatile(), + lvalue.getType(), loc, lvalue.getBaseInfo()); +} + /// Given an expression that represents a value lvalue, this /// method emits the address of the lvalue, then loads the result as an rvalue, /// returning the rvalue. diff --git a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp index 113f9961d2b48..05320a0fd576d 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp @@ -101,7 +101,11 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> { void VisitCastExpr(CastExpr *e) { switch (e->getCastKind()) { case CK_LValueToRValue: - assert(!cir::MissingFeatures::aggValueSlotVolatile()); + // If we're loading from a volatile type, force the destination + // into existence. + if (e->getSubExpr()->getType().isVolatileQualified()) + cgf.cgm.errorNYI(e->getSourceRange(), + "AggExprEmitter: volatile lvalue-to-rvalue cast"); [[fallthrough]]; case CK_NoOp: case CK_UserDefinedConversion: diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index c799ecdc27538..5556b8607727f 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -1290,6 +1290,8 @@ class CIRGenFunction : public CIRGenTypeCache { /// the LLVM value representation. The l-value must be a simple /// l-value. mlir::Value emitLoadOfScalar(LValue lvalue, SourceLocation loc); + mlir::Value emitLoadOfScalar(Address addr, bool isVolatile, QualType ty, + SourceLocation loc, LValueBaseInfo baseInfo); /// Emit code to compute a designator that specifies the location /// of the expression. diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index 03955dc737828..b06cddcdf490d 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -1214,10 +1214,10 @@ mlir::LogicalResult CIRToLLVMLoadOpLowering::matchAndRewrite( assert(!cir::MissingFeatures::lowerModeOptLevel()); // TODO: nontemporal, syncscope. - assert(!cir::MissingFeatures::opLoadStoreVolatile()); + assert(!cir::MissingFeatures::opLoadStoreNontemporal()); mlir::LLVM::LoadOp newLoad = mlir::LLVM::LoadOp::create( rewriter, op->getLoc(), llvmTy, adaptor.getAddr(), alignment, - /*isVolatile=*/false, /*isNonTemporal=*/false, + op.getIsVolatile(), /*isNonTemporal=*/false, /*isInvariant=*/false, /*isInvariantGroup=*/false, ordering); // Convert adapted result to its original type if needed. @@ -1244,10 +1244,11 @@ mlir::LogicalResult CIRToLLVMStoreOpLowering::matchAndRewrite( mlir::Value value = emitToMemory(rewriter, dataLayout, op.getValue().getType(), adaptor.getValue()); // TODO: nontemporal, syncscope. - assert(!cir::MissingFeatures::opLoadStoreVolatile()); + assert(!cir::MissingFeatures::opLoadStoreNontemporal()); + assert(!cir::MissingFeatures::opLoadStoreTbaa()); mlir::LLVM::StoreOp storeOp = mlir::LLVM::StoreOp::create( rewriter, op->getLoc(), value, adaptor.getAddr(), alignment, - /*isVolatile=*/false, + op.getIsVolatile(), /*isNonTemporal=*/false, /*isInvariantGroup=*/false, memorder); rewriter.replaceOp(op, storeOp); assert(!cir::MissingFeatures::opLoadStoreTbaa()); diff --git a/clang/test/CIR/CodeGen/volatile.cpp b/clang/test/CIR/CodeGen/volatile.cpp new file mode 100644 index 0000000000000..f81d18b0de7dd --- /dev/null +++ b/clang/test/CIR/CodeGen/volatile.cpp @@ -0,0 +1,146 @@ +// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --check-prefix=CIR %s < %t.cir +// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll +// RUN: FileCheck --check-prefix=LLVM %s < %t-cir.ll +// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll +// RUN: FileCheck --check-prefix=OGCG %s < %t.ll + +int test_load(volatile int *ptr) { + return *ptr; +} + +// CIR: cir.func dso_local @_Z9test_loadPVi +// CIR: cir.load volatile + +// LLVM: define {{.*}} i32 @_Z9test_loadPVi +// LLVM: load volatile i32, ptr %{{.*}} + +// OGCG: define {{.*}} i32 @_Z9test_loadPVi +// OGCG: load volatile i32, ptr %{{.*}} + +void test_store(volatile int *ptr) { + *ptr = 42; +} + +// CIR: cir.func dso_local @_Z10test_storePVi +// CIR: cir.store volatile + +// LLVM: define {{.*}} void @_Z10test_storePVi +// LLVM: store volatile i32 42, ptr %{{.*}} + +// OGCG: define {{.*}} void @_Z10test_storePVi +// OGCG: store volatile i32 42, ptr %{{.*}} + +struct Foo { + int x; + volatile int y; + volatile int z: 4; +}; + +int test_load_field1(volatile Foo *ptr) { + return ptr->x; +} + +// CIR: cir.func dso_local @_Z16test_load_field1PV3Foo +// CIR: %[[MEMBER_ADDR:.*]] = cir.get_member +// CIR: %{{.+}} = cir.load volatile{{.*}} %[[MEMBER_ADDR]] + +// LLVM: define {{.*}} i32 @_Z16test_load_field1PV3Foo +// LLVM: %[[MEMBER_ADDR:.*]] = getelementptr %struct.Foo, ptr %{{.*}}, i32 0, i32 0 +// LLVM: %{{.*}} = load volatile i32, ptr %[[MEMBER_ADDR]] + +// OGCG: define {{.*}} i32 @_Z16test_load_field1PV3Foo +// OGCG: %[[MEMBER_ADDR:.*]] = getelementptr inbounds nuw %struct.Foo, ptr %{{.*}}, i32 0, i32 0 +// OGCG: %{{.*}} = load volatile i32, ptr %[[MEMBER_ADDR]] + +int test_load_field2(Foo *ptr) { + return ptr->y; +} + +// CIR: cir.func dso_local @_Z16test_load_field2P3Foo +// CIR: %[[MEMBER_ADDR:.*]] = cir.get_member +// CIR: %{{.+}} = cir.load volatile{{.*}} %[[MEMBER_ADDR]] + +// LLVM: define {{.*}} i32 @_Z16test_load_field2P3Foo +// LLVM: %[[MEMBER_ADDR:.*]] = getelementptr %struct.Foo, ptr %{{.*}}, i32 0, i32 1 +// LLVM: %{{.*}} = load volatile i32, ptr %[[MEMBER_ADDR]] + +// OGCG: define {{.*}} i32 @_Z16test_load_field2P3Foo +// OGCG: %[[MEMBER_ADDR:.*]] = getelementptr inbounds nuw %struct.Foo, ptr %{{.*}}, i32 0, i32 1 +// OGCG: %{{.*}} = load volatile i32, ptr %[[MEMBER_ADDR]] + +int test_load_field3(Foo *ptr) { + return ptr->z; +} + +// CIR: cir.func dso_local @_Z16test_load_field3P3Foo +// CIR: %[[MEMBER_ADDR:.*]] = cir.get_member +// CIR: %{{.*}} = cir.get_bitfield align(4) (#bfi_z, %[[MEMBER_ADDR:.+]] {is_volatile} : !cir.ptr<!u8i>) -> !s32i + +// LLVM: define {{.*}} i32 @_Z16test_load_field3P3Foo +// LLVM: %[[MEMBER_ADDR:.*]] = getelementptr %struct.Foo, ptr %{{.*}}, i32 0, i32 2 +// LLVM: %[[TMP1:.*]] = load volatile i8, ptr %[[MEMBER_ADDR]] +// LLVM: %[[TMP2:.*]] = shl i8 %[[TMP1]], 4 +// LLVM: %[[TMP3:.*]] = ashr i8 %[[TMP2]], 4 +// LLVM: %{{.*}} = sext i8 %[[TMP3]] to i32 + +// OGCG: define {{.*}} i32 @_Z16test_load_field3P3Foo +// OGCG: %[[MEMBER_ADDR:.*]] = getelementptr inbounds nuw %struct.Foo, ptr %{{.*}}, i32 0, i32 2 +// OGCG: %[[TMP1:.*]] = load volatile i8, ptr %[[MEMBER_ADDR]] +// OGCG: %[[TMP2:.*]] = shl i8 %[[TMP1]], 4 +// OGCG: %[[TMP3:.*]] = ashr i8 %[[TMP2]], 4 +// OGCG: %{{.*}} = sext i8 %[[TMP3]] to i32 + +void test_store_field1(volatile Foo *ptr) { + ptr->x = 42; +} + +// CIR: cir.func dso_local @_Z17test_store_field1PV3Foo +// CIR: %[[MEMBER_ADDR:.*]] = cir.get_member +// CIR: cir.store volatile{{.*}} %{{.+}}, %[[MEMBER_ADDR]] + +// LLVM: define {{.*}} void @_Z17test_store_field1PV3Foo +// LLVM: %[[MEMBER_ADDR:.*]] = getelementptr %struct.Foo, ptr %{{.*}}, i32 0, i32 0 +// LLVM: store volatile i32 42, ptr %[[MEMBER_ADDR]] + +// OGCG: define {{.*}} void @_Z17test_store_field1PV3Foo +// OGCG: %[[MEMBER_ADDR:.*]] = getelementptr inbounds nuw %struct.Foo, ptr %{{.*}}, i32 0, i32 0 +// OGCG: store volatile i32 42, ptr %[[MEMBER_ADDR]] + +void test_store_field2(Foo *ptr) { + ptr->y = 42; +} + +// CIR: cir.func dso_local @_Z17test_store_field2P3Foo +// CIR: %[[MEMBER_ADDR:.*]] = cir.get_member +// CIR: cir.store volatile{{.*}} %{{.+}}, %[[MEMBER_ADDR]] + +// LLVM: define {{.*}} void @_Z17test_store_field2P3Foo +// LLVM: %[[MEMBER_ADDR:.*]] = getelementptr %struct.Foo, ptr %{{.*}}, i32 0, i32 1 +// LLVM: store volatile i32 42, ptr %[[MEMBER_ADDR]] + +// OGCG: define {{.*}} void @_Z17test_store_field2P3Foo +// OGCG: %[[MEMBER_ADDR:.*]] = getelementptr inbounds nuw %struct.Foo, ptr %{{.*}}, i32 0, i32 1 +// OGCG: store volatile i32 42, ptr %[[MEMBER_ADDR]] + +void test_store_field3(Foo *ptr) { + ptr->z = 4; +} + +// CIR: cir.func dso_local @_Z17test_store_field3P3Foo +// CIR: %[[MEMBER_ADDR:.*]] = cir.get_member +// CIR: cir.set_bitfield align(4) (#bfi_z, %[[MEMBER_ADDR:.+]] : !cir.ptr<!u8i>, %1 : !s32i) {is_volatile} + +// LLVM: define {{.*}} void @_Z17test_store_field3P3Foo +// LLVM: %[[MEMBER_ADDR:.*]] = getelementptr %struct.Foo, ptr %{{.*}}, i32 0, i32 2 +// LLVM: %[[TMP1:.*]] = load volatile i8, ptr %[[MEMBER_ADDR]] +// LLVM: %[[TMP2:.*]] = and i8 %[[TMP1]], -16 +// LLVM: %[[TMP3:.*]] = or i8 %[[TMP2]], 4 +// LLVM: store volatile i8 %[[TMP3]], ptr %[[MEMBER_ADDR]] + +// OGCG: define {{.*}} void @_Z17test_store_field3P3Foo +// OGCG: %[[MEMBER_ADDR:.*]] = getelementptr inbounds nuw %struct.Foo, ptr %{{.*}}, i32 0, i32 2 +// OGCG: %[[TMP1:.*]] = load volatile i8, ptr %[[MEMBER_ADDR]] +// OGCG: %[[TMP2:.*]] = and i8 %[[TMP1]], -16 +// OGCG: %[[TMP3:.*]] = or i8 %[[TMP2]], 4 +// OGCG: store volatile i8 %[[TMP3]], ptr %[[MEMBER_ADDR]] _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits