[clang] [CIR] Add side effect attribute to call operations (PR #144201)
Sirui Mu via cfe-commits
cfe-commits at lists.llvm.org
Tue Jun 17 07:37:05 PDT 2025
https://github.com/Lancern updated https://github.com/llvm/llvm-project/pull/144201
>From 77d093f1a9c6c6fc742e788f8b05aa9c601480de Mon Sep 17 00:00:00 2001
From: Sirui Mu <msrlancern at gmail.com>
Date: Tue, 17 Jun 2025 22:35:50 +0800
Subject: [PATCH] [CIR] Add support for __builtin_assume
This patch adds support for the __builtin_assume builtin function.
---
.../CIR/Dialect/Builder/CIRBaseBuilder.h | 16 +++--
.../include/clang/CIR/Dialect/IR/CIRAttrs.td | 5 ++
clang/include/clang/CIR/Dialect/IR/CIROps.td | 43 +++++++++++-
.../clang/CIR/Interfaces/CIROpInterfaces.td | 2 +
clang/include/clang/CIR/MissingFeatures.h | 1 -
clang/lib/CIR/CodeGen/CIRGenCall.cpp | 46 +++++++++++--
clang/lib/CIR/CodeGen/CIRGenCall.h | 6 ++
clang/lib/CIR/CodeGen/CIRGenModule.h | 10 +++
clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 66 ++++++++++++++++++-
.../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 51 ++++++++++++--
.../CIR/Lowering/DirectToLLVM/LowerToLLVM.h | 5 ++
clang/test/CIR/CodeGen/call.c | 26 ++++++++
clang/test/CIR/IR/call.cir | 4 ++
13 files changed, 257 insertions(+), 24 deletions(-)
diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
index a3754f4de66b0..b6ae9965161d4 100644
--- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
+++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
@@ -213,22 +213,26 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
//===--------------------------------------------------------------------===//
cir::CallOp createCallOp(mlir::Location loc, mlir::SymbolRefAttr callee,
- mlir::Type returnType, mlir::ValueRange operands) {
- return create<cir::CallOp>(loc, callee, returnType, operands);
+ mlir::Type returnType, mlir::ValueRange operands,
+ cir::SideEffect sideEffect = cir::SideEffect::All) {
+ return create<cir::CallOp>(loc, callee, returnType, operands, sideEffect);
}
cir::CallOp createCallOp(mlir::Location loc, cir::FuncOp callee,
- mlir::ValueRange operands) {
+ mlir::ValueRange operands,
+ cir::SideEffect sideEffect = cir::SideEffect::All) {
return createCallOp(loc, mlir::SymbolRefAttr::get(callee),
- callee.getFunctionType().getReturnType(), operands);
+ callee.getFunctionType().getReturnType(), operands,
+ sideEffect);
}
cir::CallOp createIndirectCallOp(mlir::Location loc,
mlir::Value indirectTarget,
cir::FuncType funcType,
- mlir::ValueRange operands) {
+ mlir::ValueRange operands,
+ cir::SideEffect sideEffect) {
return create<cir::CallOp>(loc, indirectTarget, funcType.getReturnType(),
- operands);
+ operands, sideEffect);
}
//===--------------------------------------------------------------------===//
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
index d22d265e82425..d02189ff42d2e 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
@@ -42,6 +42,11 @@ class CIR_TypedAttr<string name, string attrMnemonic, list<Trait> traits = []>
let assemblyFormat = [{}];
}
+class CIR_I32EnumAttr<string name, string summary, list<I32EnumAttrCase> cases>
+ : I32EnumAttr<name, summary, cases> {
+ let cppNamespace = "::cir";
+}
+
class CIRUnitAttr<string name, string attrMnemonic, list<Trait> traits = []>
: CIR_Attr<name, attrMnemonic, traits> {
let returnType = "bool";
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 194153caa9271..b953d7b2fb338 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -1858,6 +1858,36 @@ def FuncOp : CIR_Op<"func", [
// CallOp
//===----------------------------------------------------------------------===//
+def CIR_SideEffect : CIR_I32EnumAttr<
+ "SideEffect", "allowed side effects of a function", [
+ I32EnumAttrCase<"All", 1, "all">,
+ I32EnumAttrCase<"Pure", 2, "pure">,
+ I32EnumAttrCase<"Const", 3, "const">
+ ]> {
+ let description = [{
+ The side effect attribute specifies the possible side effects of the callee
+ of a call operation. This is an enumeration attribute and all possible
+ enumerators are:
+
+ - all: The callee can have any side effects. This is the default if no side
+ effects are explicitly listed.
+ - pure: The callee may read data from memory, but it cannot write data to
+ memory. This has the same effect as the GNU C/C++ attribute
+ `__attribute__((pure))`.
+ - const: The callee may not read or write data from memory. This has the
+ same effect as the GNU C/C++ attribute `__attribute__((const))`.
+
+ Examples:
+
+ ```mlir
+ %2 = cir.call @add(%0, %1) : (!s32i, !s32i) -> !s32i
+ %2 = cir.call @add(%0, %1) : (!s32i, !s32i) -> !s32i side_effect(pure)
+ %2 = cir.call @add(%0, %1) : (!s32i, !s32i) -> !s32i side_effect(const)
+ ```
+ }];
+ let cppNamespace = "::cir";
+}
+
class CIR_CallOpBase<string mnemonic, list<Trait> extra_traits = []>
: Op<CIR_Dialect, mnemonic,
!listconcat(extra_traits,
@@ -1911,7 +1941,8 @@ class CIR_CallOpBase<string mnemonic, list<Trait> extra_traits = []>
// will add in the future.
dag commonArgs = (ins OptionalAttr<FlatSymbolRefAttr>:$callee,
- Variadic<CIR_AnyType>:$args);
+ Variadic<CIR_AnyType>:$args,
+ DefaultValuedAttr<CIR_SideEffect, "SideEffect::All">:$side_effect);
}
def CallOp : CIR_CallOpBase<"call", [NoRegionArguments]> {
@@ -1942,20 +1973,26 @@ def CallOp : CIR_CallOpBase<"call", [NoRegionArguments]> {
let builders = [
// Build a call op for a direct call
OpBuilder<(ins "mlir::SymbolRefAttr":$callee, "mlir::Type":$resType,
- "mlir::ValueRange":$operands), [{
+ "mlir::ValueRange":$operands,
+ CArg<"SideEffect", "SideEffect::All">:$sideEffect), [{
assert(callee && "callee attribute is required for direct call");
$_state.addOperands(operands);
$_state.addAttribute("callee", callee);
+ $_state.addAttribute("side_effect",
+ SideEffectAttr::get($_builder.getContext(), sideEffect));
if (resType && !isa<VoidType>(resType))
$_state.addTypes(resType);
}]>,
// Build a call op for an indirect call
OpBuilder<(ins "mlir::Value":$calleePtr, "mlir::Type":$resType,
- "mlir::ValueRange":$operands), [{
+ "mlir::ValueRange":$operands,
+ CArg<"SideEffect", "SideEffect::All">:$sideEffect), [{
$_state.addOperands(calleePtr);
$_state.addOperands(operands);
if (resType && !isa<VoidType>(resType))
$_state.addTypes(resType);
+ $_state.addAttribute("side_effect",
+ SideEffectAttr::get($_builder.getContext(), sideEffect));
}]>,
];
}
diff --git a/clang/include/clang/CIR/Interfaces/CIROpInterfaces.td b/clang/include/clang/CIR/Interfaces/CIROpInterfaces.td
index 80d78b11c2ba4..203e42f7c575e 100644
--- a/clang/include/clang/CIR/Interfaces/CIROpInterfaces.td
+++ b/clang/include/clang/CIR/Interfaces/CIROpInterfaces.td
@@ -34,6 +34,8 @@ let cppNamespace = "::cir" in {
"Return the number of operands, accounts for indirect call or "
"exception info",
"unsigned", "getNumArgOperands", (ins)>,
+ InterfaceMethod<"Return the side effects of the call operation",
+ "cir::SideEffect", "getSideEffect", (ins)>,
];
}
diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index 225e9ec89a827..d5e1442d562b8 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -95,7 +95,6 @@ struct MissingFeatures {
static bool opCallReturn() { return false; }
static bool opCallArgEvaluationOrder() { return false; }
static bool opCallCallConv() { return false; }
- static bool opCallSideEffect() { return false; }
static bool opCallNoPrototypeFunc() { return false; }
static bool opCallMustTail() { return false; }
static bool opCallVirtual() { return false; }
diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.cpp b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
index 0d9064425fa95..b37f98ac06af6 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCall.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCall.cpp
@@ -77,6 +77,35 @@ void CIRGenFunction::emitAggregateStore(mlir::Value value, Address dest) {
builder.createStore(*currSrcLoc, value, dest);
}
+/// Construct the CIR attribute list of a function or call.
+void CIRGenModule::constructAttributeList(CIRGenCalleeInfo calleeInfo,
+ cir::SideEffect &sideEffect) {
+ assert(!cir::MissingFeatures::opCallCallConv());
+ sideEffect = cir::SideEffect::All;
+
+ assert(!cir::MissingFeatures::opCallAttrs());
+
+ const Decl *targetDecl = calleeInfo.getCalleeDecl().getDecl();
+
+ if (targetDecl) {
+ assert(!cir::MissingFeatures::opCallAttrs());
+
+ // 'const', 'pure' and 'noalias' attributed functions are also nounwind.
+ if (targetDecl->hasAttr<ConstAttr>()) {
+ // gcc specifies that 'const' functions have greater restrictions than
+ // 'pure' functions, so they also cannot have infinite loops.
+ sideEffect = cir::SideEffect::Const;
+ } else if (targetDecl->hasAttr<PureAttr>()) {
+ // gcc specifies that 'pure' functions cannot have infinite loops.
+ sideEffect = cir::SideEffect::Pure;
+ }
+
+ assert(!cir::MissingFeatures::opCallAttrs());
+ }
+
+ assert(!cir::MissingFeatures::opCallAttrs());
+}
+
/// Returns the canonical formal type of the given C++ method.
static CanQual<FunctionProtoType> getFormalType(const CXXMethodDecl *md) {
return md->getType()
@@ -386,7 +415,8 @@ static cir::CIRCallOpInterface
emitCallLikeOp(CIRGenFunction &cgf, mlir::Location callLoc,
cir::FuncType indirectFuncTy, mlir::Value indirectFuncVal,
cir::FuncOp directFuncOp,
- const SmallVectorImpl<mlir::Value> &cirCallArgs) {
+ const SmallVectorImpl<mlir::Value> &cirCallArgs,
+ cir::SideEffect sideEffect) {
CIRGenBuilderTy &builder = cgf.getBuilder();
assert(!cir::MissingFeatures::opCallSurroundingTry());
@@ -397,11 +427,11 @@ emitCallLikeOp(CIRGenFunction &cgf, mlir::Location callLoc,
if (indirectFuncTy) {
// TODO(cir): Set calling convention for indirect calls.
assert(!cir::MissingFeatures::opCallCallConv());
- return builder.createIndirectCallOp(callLoc, indirectFuncVal,
- indirectFuncTy, cirCallArgs);
+ return builder.createIndirectCallOp(
+ callLoc, indirectFuncVal, indirectFuncTy, cirCallArgs, sideEffect);
}
- return builder.createCallOp(callLoc, directFuncOp, cirCallArgs);
+ return builder.createCallOp(callLoc, directFuncOp, cirCallArgs, sideEffect);
}
const CIRGenFunctionInfo &
@@ -513,8 +543,9 @@ RValue CIRGenFunction::emitCall(const CIRGenFunctionInfo &funcInfo,
funcName = calleeFuncOp.getName();
assert(!cir::MissingFeatures::opCallCallConv());
- assert(!cir::MissingFeatures::opCallSideEffect());
assert(!cir::MissingFeatures::opCallAttrs());
+ cir::SideEffect sideEffect;
+ cgm.constructAttributeList(callee.getAbstractInfo(), sideEffect);
assert(!cir::MissingFeatures::invokeOp());
@@ -538,8 +569,9 @@ RValue CIRGenFunction::emitCall(const CIRGenFunctionInfo &funcInfo,
assert(!cir::MissingFeatures::opCallAttrs());
mlir::Location callLoc = loc;
- cir::CIRCallOpInterface theCall = emitCallLikeOp(
- *this, loc, indirectFuncTy, indirectFuncVal, directFuncOp, cirCallArgs);
+ cir::CIRCallOpInterface theCall =
+ emitCallLikeOp(*this, loc, indirectFuncTy, indirectFuncVal, directFuncOp,
+ cirCallArgs, sideEffect);
if (callOp)
*callOp = theCall;
diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.h b/clang/lib/CIR/CodeGen/CIRGenCall.h
index 0353848f3ec0d..56c76c51a46d8 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCall.h
+++ b/clang/lib/CIR/CodeGen/CIRGenCall.h
@@ -105,6 +105,12 @@ class CIRGenCallee {
/// callee
CIRGenCallee prepareConcreteCallee(CIRGenFunction &cgf) const;
+ CIRGenCalleeInfo getAbstractInfo() const {
+ assert(!cir::MissingFeatures::opCallVirtual());
+ assert(isOrdinary());
+ return abstractInfo;
+ }
+
mlir::Operation *getFunctionPointer() const {
assert(isOrdinary());
return reinterpret_cast<mlir::Operation *>(kindOrFunctionPtr);
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h
index f76fd8e733642..a3703c90395f6 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.h
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.h
@@ -14,6 +14,7 @@
#define LLVM_CLANG_LIB_CIR_CODEGEN_CIRGENMODULE_H
#include "CIRGenBuilder.h"
+#include "CIRGenCall.h"
#include "CIRGenTypeCache.h"
#include "CIRGenTypes.h"
#include "CIRGenValue.h"
@@ -145,6 +146,15 @@ class CIRGenModule : public CIRGenTypeCache {
const CXXRecordDecl *derivedClass,
llvm::iterator_range<CastExpr::path_const_iterator> path);
+ /// Get the CIR attributes and calling convention to use for a particular
+ /// function type.
+ ///
+ /// \param calleeInfo - The callee information these attributes are being
+ /// constructed for. If valid, the attributes applied to this decl may
+ /// contribute to the function attributes and calling convention.
+ void constructAttributeList(CIRGenCalleeInfo calleeInfo,
+ cir::SideEffect &sideEffect);
+
/// Return a constant array for the given string.
mlir::Attribute getConstantArrayFromStringLiteral(const StringLiteral *e);
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index 8ed0ee92574dc..82bec96edc5ff 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -92,6 +92,46 @@ Operation *cir::CIRDialect::materializeConstant(mlir::OpBuilder &builder,
// Helpers
//===----------------------------------------------------------------------===//
+// Parses one of the keywords provided in the list `keywords` and returns the
+// position of the parsed keyword in the list. If none of the keywords from the
+// list is parsed, returns -1.
+static int parseOptionalKeywordAlternative(AsmParser &parser,
+ ArrayRef<llvm::StringRef> keywords) {
+ for (auto en : llvm::enumerate(keywords)) {
+ if (succeeded(parser.parseOptionalKeyword(en.value())))
+ return en.index();
+ }
+ return -1;
+}
+
+namespace {
+template <typename Ty> struct EnumTraits {};
+
+#define REGISTER_ENUM_TYPE(Ty) \
+ template <> struct EnumTraits<cir::Ty> { \
+ static llvm::StringRef stringify(cir::Ty value) { \
+ return stringify##Ty(value); \
+ } \
+ static unsigned getMaxEnumVal() { return cir::getMaxEnumValFor##Ty(); } \
+ }
+
+REGISTER_ENUM_TYPE(SideEffect);
+} // namespace
+
+/// Parse an enum from the keyword, return failure if the keyword is not found.
+template <typename EnumTy, typename RetTy = EnumTy>
+static ParseResult parseCIRKeyword(AsmParser &parser, RetTy &result) {
+ llvm::SmallVector<llvm::StringRef, 10> names;
+ for (unsigned i = 0, e = EnumTraits<EnumTy>::getMaxEnumVal(); i <= e; ++i)
+ names.push_back(EnumTraits<EnumTy>::stringify(static_cast<EnumTy>(i)));
+
+ int index = parseOptionalKeywordAlternative(parser, names);
+ if (index == -1)
+ return failure();
+ result = static_cast<RetTy>(index);
+ return success();
+}
+
// Check if a region's termination omission is valid and, if so, creates and
// inserts the omitted terminator into the region.
static LogicalResult ensureRegionTerm(OpAsmParser &parser, Region ®ion,
@@ -534,6 +574,18 @@ static mlir::ParseResult parseCallCommon(mlir::OpAsmParser &parser,
if (parser.parseRParen())
return mlir::failure();
+ if (parser.parseOptionalKeyword("side_effect").succeeded()) {
+ if (parser.parseLParen().failed())
+ return failure();
+ cir::SideEffect sideEffect;
+ if (parseCIRKeyword<cir::SideEffect>(parser, sideEffect).failed())
+ return failure();
+ if (parser.parseRParen().failed())
+ return failure();
+ auto attr = cir::SideEffectAttr::get(parser.getContext(), sideEffect);
+ result.addAttribute("side_effect", attr);
+ }
+
if (parser.parseOptionalAttrDict(result.attributes))
return ::mlir::failure();
@@ -556,7 +608,8 @@ static mlir::ParseResult parseCallCommon(mlir::OpAsmParser &parser,
static void printCallCommon(mlir::Operation *op,
mlir::FlatSymbolRefAttr calleeSym,
mlir::Value indirectCallee,
- mlir::OpAsmPrinter &printer) {
+ mlir::OpAsmPrinter &printer,
+ cir::SideEffect sideEffect) {
printer << ' ';
auto callLikeOp = mlir::cast<cir::CIRCallOpInterface>(op);
@@ -572,7 +625,13 @@ static void printCallCommon(mlir::Operation *op,
}
printer << "(" << ops << ")";
- printer.printOptionalAttrDict(op->getAttrs(), {"callee"});
+ if (sideEffect != cir::SideEffect::All) {
+ printer << " side_effect(";
+ printer << stringifySideEffect(sideEffect);
+ printer << ")";
+ }
+
+ printer.printOptionalAttrDict(op->getAttrs(), {"callee", "side_effect"});
printer << " : ";
printer.printFunctionalType(op->getOperands().getTypes(),
@@ -586,7 +645,8 @@ mlir::ParseResult cir::CallOp::parse(mlir::OpAsmParser &parser,
void cir::CallOp::print(mlir::OpAsmPrinter &p) {
mlir::Value indirectCallee = isIndirect() ? getIndirectCall() : nullptr;
- printCallCommon(*this, getCalleeAttr(), indirectCallee, p);
+ cir::SideEffect sideEffect = getSideEffect();
+ printCallCommon(*this, getCalleeAttr(), indirectCallee, p, sideEffect);
}
static LogicalResult
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 619e113202c9a..cb6c89f395f89 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -220,6 +220,39 @@ mlir::Value lowerCirAttrAsValue(mlir::Operation *parentOp,
return value;
}
+void convertSideEffectForCall(mlir::Operation *callOp,
+ cir::SideEffect sideEffect,
+ mlir::LLVM::MemoryEffectsAttr &memoryEffect,
+ bool &noUnwind, bool &willReturn) {
+ using mlir::LLVM::ModRefInfo;
+
+ switch (sideEffect) {
+ case cir::SideEffect::All:
+ memoryEffect = {};
+ noUnwind = false;
+ willReturn = false;
+ break;
+
+ case cir::SideEffect::Pure:
+ memoryEffect = mlir::LLVM::MemoryEffectsAttr::get(
+ callOp->getContext(), /*other=*/ModRefInfo::Ref,
+ /*argMem=*/ModRefInfo::Ref,
+ /*inaccessibleMem=*/ModRefInfo::Ref);
+ noUnwind = true;
+ willReturn = true;
+ break;
+
+ case cir::SideEffect::Const:
+ memoryEffect = mlir::LLVM::MemoryEffectsAttr::get(
+ callOp->getContext(), /*other=*/ModRefInfo::NoModRef,
+ /*argMem=*/ModRefInfo::NoModRef,
+ /*inaccessibleMem=*/ModRefInfo::NoModRef);
+ noUnwind = true;
+ willReturn = true;
+ break;
+ }
+}
+
/// IntAttr visitor.
mlir::Value CIRAttrToValue::visitCirAttr(cir::IntAttr intAttr) {
mlir::Location loc = parentOp->getLoc();
@@ -737,12 +770,18 @@ rewriteCallOrInvoke(mlir::Operation *op, mlir::ValueRange callOperands,
mlir::FlatSymbolRefAttr calleeAttr) {
llvm::SmallVector<mlir::Type, 8> llvmResults;
mlir::ValueTypeRange<mlir::ResultRange> cirResults = op->getResultTypes();
+ auto call = cast<cir::CIRCallOpInterface>(op);
if (converter->convertTypes(cirResults, llvmResults).failed())
return mlir::failure();
assert(!cir::MissingFeatures::opCallCallConv());
- assert(!cir::MissingFeatures::opCallSideEffect());
+
+ mlir::LLVM::MemoryEffectsAttr memoryEffects;
+ bool noUnwind = false;
+ bool willReturn = false;
+ convertSideEffectForCall(op, call.getSideEffect(), memoryEffects, noUnwind,
+ willReturn);
mlir::LLVM::LLVMFunctionType llvmFnTy;
if (calleeAttr) { // direct call
@@ -767,10 +806,14 @@ rewriteCallOrInvoke(mlir::Operation *op, mlir::ValueRange callOperands,
assert(!cir::MissingFeatures::opCallLandingPad());
assert(!cir::MissingFeatures::opCallContinueBlock());
assert(!cir::MissingFeatures::opCallCallConv());
- assert(!cir::MissingFeatures::opCallSideEffect());
- rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>(op, llvmFnTy, calleeAttr,
- callOperands);
+ auto newOp = rewriter.replaceOpWithNewOp<mlir::LLVM::CallOp>(
+ op, llvmFnTy, calleeAttr, callOperands);
+ if (memoryEffects)
+ newOp.setMemoryEffectsAttr(memoryEffects);
+ newOp.setNoUnwind(noUnwind);
+ newOp.setWillReturn(willReturn);
+
return mlir::success();
}
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
index 2eda568c84bdb..bf353c34b46c9 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
@@ -29,6 +29,11 @@ mlir::Value lowerCirAttrAsValue(mlir::Operation *parentOp, mlir::Attribute attr,
mlir::LLVM::Linkage convertLinkage(cir::GlobalLinkageKind linkage);
+void convertSideEffectForCall(mlir::Operation *callOp,
+ cir::SideEffect sideEffect,
+ mlir::LLVM::MemoryEffectsAttr &memoryEffect,
+ bool &noUnwind, bool &willReturn);
+
class CIRToLLVMBrCondOpLowering
: public mlir::OpConversionPattern<cir::BrCondOp> {
public:
diff --git a/clang/test/CIR/CodeGen/call.c b/clang/test/CIR/CodeGen/call.c
index 13f3c5a21ceb0..f6aa41df7439e 100644
--- a/clang/test/CIR/CodeGen/call.c
+++ b/clang/test/CIR/CodeGen/call.c
@@ -109,3 +109,29 @@ void f9() {
// OGCG-NEXT: store i64 %[[RET]], ptr %[[SLOT]], align 4
// OGCG-NEXT: %[[ARG:.+]] = load i64, ptr %[[SLOT]], align 4
// OGCG-NEXT: call void @f1(i64 %[[ARG]])
+
+__attribute__((pure)) int f10(int);
+__attribute__((const)) int f11(int);
+int f12(void) {
+ return f10(1) + f11(2);
+}
+
+// CIR-LABEL: cir.func @f12() -> !s32i
+// CIR: %[[A:.+]] = cir.const #cir.int<1> : !s32i
+// CIR-NEXT: %{{.+}} = cir.call @f10(%[[A]]) side_effect(pure) : (!s32i) -> !s32i
+// CIR-NEXT: %[[B:.+]] = cir.const #cir.int<2> : !s32i
+// CIR-NEXT: %{{.+}} = cir.call @f11(%[[B]]) side_effect(const) : (!s32i) -> !s32i
+
+// LLVM-LABEL: define i32 @f12()
+// LLVM: %{{.+}} = call i32 @f10(i32 1) #[[ATTR0:.+]]
+// LLVM-NEXT: %{{.+}} = call i32 @f11(i32 2) #[[ATTR1:.+]]
+
+// OGCG-LABEL: define dso_local i32 @f12()
+// OGCG: %{{.+}} = call i32 @f10(i32 noundef 1) #[[ATTR0:.+]]
+// OGCG-NEXT: %{{.+}} = call i32 @f11(i32 noundef 2) #[[ATTR1:.+]]
+
+// LLVM: attributes #[[ATTR0]] = { nounwind willreturn memory(read, errnomem: none) }
+// LLVM: attributes #[[ATTR1]] = { nounwind willreturn memory(none) }
+
+// OGCG: attributes #[[ATTR0]] = { nounwind willreturn memory(read) }
+// OGCG: attributes #[[ATTR1]] = { nounwind willreturn memory(none) }
diff --git a/clang/test/CIR/IR/call.cir b/clang/test/CIR/IR/call.cir
index e35c201b6ed48..5f0916775479e 100644
--- a/clang/test/CIR/IR/call.cir
+++ b/clang/test/CIR/IR/call.cir
@@ -8,11 +8,15 @@ cir.func @f1()
cir.func @f2() {
cir.call @f1() : () -> ()
+ cir.call @f1() side_effect(pure) : () -> ()
+ cir.call @f1() side_effect(const) : () -> ()
cir.return
}
// CHECK: cir.func @f2() {
// CHECK-NEXT: cir.call @f1() : () -> ()
+// CHECK-NEXT: cir.call @f1() side_effect(pure) : () -> ()
+// CHECK-NEXT: cir.call @f1() side_effect(const) : () -> ()
// CHECK-NEXT: cir.return
// CHECK-NEXT: }
More information about the cfe-commits
mailing list