[clang] [CIR] Upstream func args alloca handling (PR #129167)
Andy Kaylor via cfe-commits
cfe-commits at lists.llvm.org
Fri Feb 28 10:40:16 PST 2025
https://github.com/andykaylor updated https://github.com/llvm/llvm-project/pull/129167
>From d1fa2629b5786befa8ca8f839cf948df4644d615 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Tue, 25 Feb 2025 16:52:18 -0800
Subject: [PATCH 1/2] [CIR] Upstream func args alloca handling
This change adds support for collecting function arguments and storing
them in alloca memory slots.
---
.../CIR/Dialect/Builder/CIRBaseBuilder.h | 5 ++
clang/include/clang/CIR/Dialect/IR/CIROps.td | 39 ++++++++++
clang/include/clang/CIR/MissingFeatures.h | 1 +
clang/lib/CIR/CodeGen/Address.h | 8 ++
clang/lib/CIR/CodeGen/CIRGenCall.h | 28 +++++++
clang/lib/CIR/CodeGen/CIRGenDecl.cpp | 2 +-
clang/lib/CIR/CodeGen/CIRGenFunction.cpp | 76 +++++++++++++++++--
clang/lib/CIR/CodeGen/CIRGenFunction.h | 11 ++-
clang/lib/CIR/Dialect/IR/CIRMemorySlot.cpp | 33 ++++++++
clang/test/CIR/CodeGen/basic.cpp | 20 +++++
10 files changed, 212 insertions(+), 11 deletions(-)
create mode 100644 clang/lib/CIR/CodeGen/CIRGenCall.h
diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
index 14afdfc2758ea..b65797e40d5f9 100644
--- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
+++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
@@ -69,6 +69,11 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
return create<cir::LoadOp>(loc, ptr);
}
+ cir::StoreOp createStore(mlir::Location loc, mlir::Value val,
+ mlir::Value dst) {
+ return create<cir::StoreOp>(loc, val, dst);
+ }
+
//
// Block handling helpers
// ----------------------
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 083cf46a93ae6..48178b0ff247d 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -228,6 +228,45 @@ def LoadOp : CIR_Op<"load", [
// FIXME: add verifier.
}
+//===----------------------------------------------------------------------===//
+// StoreOp
+//===----------------------------------------------------------------------===//
+
+def StoreOp : CIR_Op<"store", [
+ TypesMatchWith<"type of 'value' matches pointee type of 'addr'",
+ "addr", "value",
+ "cast<PointerType>($_self).getPointee()">,
+ DeclareOpInterfaceMethods<PromotableMemOpInterface>]> {
+
+ 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. A unit attribute `volatile` can be used to indicate
+ 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
+ default, which is computed from `result`'s type ABI data layout.
+
+ Example:
+
+ ```mlir
+ // Store a function argument to local storage, address in %0.
+ cir.store %arg0, %0 : i32, !cir.ptr<i32>
+ ```
+ }];
+
+ let arguments = (ins CIR_AnyType:$value,
+ Arg<CIR_PointerType, "the address to store the value",
+ [MemWrite]>:$addr);
+
+ let assemblyFormat = [{
+ $value `,` $addr attr-dict `:` type($value) `,` qualified(type($addr))
+ }];
+
+ // FIXME: add verifier.
+}
+
//===----------------------------------------------------------------------===//
// ReturnOp
//===----------------------------------------------------------------------===//
diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index 5c7e10d018809..9b416ef61055e 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -59,6 +59,7 @@ struct MissingFeatures {
// Misc
static bool scalarConversionOpts() { return false; }
static bool tryEmitAsConstant() { return false; }
+ static bool constructABIArgDirectExtend() { return false; }
};
} // namespace cir
diff --git a/clang/lib/CIR/CodeGen/Address.h b/clang/lib/CIR/CodeGen/Address.h
index 72e7e1dcf1560..fba1ffd90877b 100644
--- a/clang/lib/CIR/CodeGen/Address.h
+++ b/clang/lib/CIR/CodeGen/Address.h
@@ -52,6 +52,14 @@ class Address {
elementType);
}
+ Address(mlir::Value pointer, clang::CharUnits alignment)
+ : Address(pointer,
+ mlir::cast<cir::PointerType>(pointer.getType()).getPointee(),
+ alignment) {
+ assert((!alignment.isZero() || pointer == nullptr) &&
+ "creating valid address with invalid alignment");
+ }
+
static Address invalid() { return Address(nullptr); }
bool isValid() const {
return pointerAndKnownNonNull.getPointer() != nullptr;
diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.h b/clang/lib/CIR/CodeGen/CIRGenCall.h
new file mode 100644
index 0000000000000..0996167feeef6
--- /dev/null
+++ b/clang/lib/CIR/CodeGen/CIRGenCall.h
@@ -0,0 +1,28 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// These classes wrap the information about a call or function
+// definition used to handle ABI compliancy.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef CLANG_LIB_CODEGEN_CIRGENCALL_H
+#define CLANG_LIB_CODEGEN_CIRGENCALL_H
+
+#include "clang/AST/GlobalDecl.h"
+#include "llvm/ADT/SmallVector.h"
+
+namespace clang::CIRGen {
+
+/// Type for representing both the decl and type of parameters to a function.
+/// The decl must be either a ParmVarDecl or ImplicitParamDecl.
+class FunctionArgList : public llvm::SmallVector<const clang::VarDecl *, 16> {};
+
+} // namespace clang::CIRGen
+
+#endif // CLANG_LIB_CODEGEN_CIRGENCALL_H
diff --git a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
index e44cad559d509..c34d42eff6966 100644
--- a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
@@ -44,7 +44,7 @@ void CIRGenFunction::emitAutoVarAlloca(const VarDecl &d) {
mlir::Type allocaTy = convertTypeForMem(ty);
// Create the temp alloca and declare variable using it.
address = createTempAlloca(allocaTy, alignment, loc, d.getName());
- declare(address, &d, ty, getLoc(d.getSourceRange()), alignment);
+ declare(address.getPointer(), &d, ty, getLoc(d.getSourceRange()), alignment);
setAddrOfLocalVar(&d, address);
}
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
index 86986b5847e98..76912d412fd06 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -12,6 +12,8 @@
#include "CIRGenFunction.h"
+#include "CIRGenCall.h"
+#include "mlir/IR/Location.h"
#include "clang/AST/GlobalDecl.h"
#include "clang/CIR/MissingFeatures.h"
@@ -132,15 +134,17 @@ mlir::Location CIRGenFunction::getLoc(mlir::Location lhs, mlir::Location rhs) {
return mlir::FusedLoc::get(locs, metadata, &getMLIRContext());
}
-mlir::LogicalResult CIRGenFunction::declare(Address addr, const Decl *var,
- QualType ty, mlir::Location loc,
- CharUnits alignment) {
+mlir::LogicalResult CIRGenFunction::declare(mlir::Value addrVal,
+ const Decl *var, QualType ty,
+ mlir::Location loc,
+ CharUnits alignment, bool isParam) {
const auto *namedVar = dyn_cast_or_null<NamedDecl>(var);
assert(namedVar && "Needs a named decl");
assert(!cir::MissingFeatures::cgfSymbolTable());
- mlir::Value addrVal = addr.getPointer();
auto allocaOp = cast<cir::AllocaOp>(addrVal.getDefiningOp());
+ if (isParam)
+ allocaOp.setInitAttr(mlir::UnitAttr::get(&getMLIRContext()));
if (ty->isReferenceType() || ty.isConstQualified())
allocaOp.setConstantAttr(mlir::UnitAttr::get(&getMLIRContext()));
@@ -149,7 +153,7 @@ mlir::LogicalResult CIRGenFunction::declare(Address addr, const Decl *var,
void CIRGenFunction::startFunction(GlobalDecl gd, QualType returnType,
cir::FuncOp fn, cir::FuncType funcType,
- SourceLocation loc,
+ FunctionArgList args, SourceLocation loc,
SourceLocation startLoc) {
assert(!curFn &&
"CIRGenFunction can only be used for one function at a time");
@@ -157,8 +161,41 @@ void CIRGenFunction::startFunction(GlobalDecl gd, QualType returnType,
fnRetTy = returnType;
curFn = fn;
+ const auto *fd = dyn_cast_or_null<FunctionDecl>(gd.getDecl());
+
mlir::Block *entryBB = &fn.getBlocks().front();
builder.setInsertionPointToStart(entryBB);
+
+ // TODO(cir): this should live in `emitFunctionProlog
+ // Declare all the function arguments in the symbol table.
+ for (const auto nameValue : llvm::zip(args, entryBB->getArguments())) {
+ auto *paramVar = std::get<0>(nameValue);
+ mlir::Value paramVal = std::get<1>(nameValue);
+ auto alignment = getContext().getDeclAlign(paramVar);
+ auto paramLoc = getLoc(paramVar->getSourceRange());
+ paramVal.setLoc(paramLoc);
+
+ mlir::Value addrVal =
+ emitAlloca(cast<NamedDecl>(paramVar)->getName(),
+ convertType(paramVar->getType()), paramLoc, alignment);
+
+ declare(addrVal, paramVar, paramVar->getType(), paramLoc, alignment,
+ /*isParam=*/true);
+
+ setAddrOfLocalVar(paramVar, Address(addrVal, alignment));
+
+ bool isPromoted = isa<ParmVarDecl>(paramVar) &&
+ cast<ParmVarDecl>(paramVar)->isKNRPromoted();
+ assert(!cir::MissingFeatures::constructABIArgDirectExtend());
+ if (isPromoted)
+ cgm.errorNYI(fd->getSourceRange(), "Function argument demotion");
+
+ // Location of the store to the param storage tracked as beginning of
+ // the function body.
+ mlir::Location fnBodyBegin = getLoc(fd->getBody()->getBeginLoc());
+ builder.CIRBaseBuilderTy::createStore(fnBodyBegin, paramVal, addrVal);
+ }
+ assert(builder.getInsertionBlock() && "Should be valid");
}
void CIRGenFunction::finishFunction(SourceLocation endLoc) {}
@@ -187,8 +224,10 @@ cir::FuncOp CIRGenFunction::generateCode(clang::GlobalDecl gd, cir::FuncOp fn,
// This will be used once more code is upstreamed.
[[maybe_unused]] mlir::Block *entryBB = fn.addEntryBlock();
- startFunction(gd, funcDecl->getReturnType(), fn, funcType, loc,
- bodyRange.getBegin());
+ FunctionArgList args;
+ QualType retTy = buildFunctionArgList(gd, args);
+
+ startFunction(gd, retTy, fn, funcType, args, loc, bodyRange.getBegin());
if (isa<CXXDestructorDecl>(funcDecl))
getCIRGenModule().errorNYI(bodyRange, "C++ destructor definition");
@@ -234,6 +273,29 @@ cir::FuncOp CIRGenFunction::generateCode(clang::GlobalDecl gd, cir::FuncOp fn,
return fn;
}
+clang::QualType CIRGenFunction::buildFunctionArgList(clang::GlobalDecl gd,
+ FunctionArgList &args) {
+ const auto *fd = cast<FunctionDecl>(gd.getDecl());
+ QualType retTy = fd->getReturnType();
+
+ const auto *md = dyn_cast<CXXMethodDecl>(fd);
+ if (md && md->isInstance())
+ cgm.errorNYI(fd->getSourceRange(), "buildFunctionArgList: CXXMethodDecl");
+
+ if (isa<CXXConstructorDecl>(fd))
+ cgm.errorNYI(fd->getSourceRange(),
+ "buildFunctionArgList: CXXConstructorDecl");
+
+ for (auto *param : fd->parameters())
+ args.push_back(param);
+
+ if (md && (isa<CXXConstructorDecl>(md) || isa<CXXDestructorDecl>(md)))
+ cgm.errorNYI(fd->getSourceRange(),
+ "buildFunctionArgList: implicit structor params");
+
+ return retTy;
+}
+
/// Emit code to compute a designator that specifies the location
/// of the expression.
/// FIXME: document this function better.
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index e0888acdc3dce..6b383378ae764 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -14,6 +14,7 @@
#define CLANG_LIB_CIR_CODEGEN_CIRGENFUNCTION_H
#include "CIRGenBuilder.h"
+#include "CIRGenCall.h"
#include "CIRGenModule.h"
#include "CIRGenTypeCache.h"
#include "CIRGenValue.h"
@@ -96,9 +97,9 @@ class CIRGenFunction : public CIRGenTypeCache {
private:
/// Declare a variable in the current scope, return success if the variable
/// wasn't declared yet.
- mlir::LogicalResult declare(Address addr, const clang::Decl *var,
+ mlir::LogicalResult declare(mlir::Value addrVal, const clang::Decl *var,
clang::QualType ty, mlir::Location loc,
- clang::CharUnits alignment);
+ clang::CharUnits alignment, bool isParam = false);
public:
mlir::Value emitAlloca(llvm::StringRef name, mlir::Type ty,
@@ -196,12 +197,16 @@ class CIRGenFunction : public CIRGenTypeCache {
cir::FuncOp generateCode(clang::GlobalDecl gd, cir::FuncOp fn,
cir::FuncType funcType);
+ clang::QualType buildFunctionArgList(clang::GlobalDecl gd,
+ FunctionArgList &args);
+
/// Emit code for the start of a function.
/// \param loc The location to be associated with the function.
/// \param startLoc The location of the function body.
void startFunction(clang::GlobalDecl gd, clang::QualType retTy,
cir::FuncOp fn, cir::FuncType funcType,
- clang::SourceLocation loc, clang::SourceLocation startLoc);
+ FunctionArgList args, clang::SourceLocation loc,
+ clang::SourceLocation startLoc);
Address createTempAlloca(mlir::Type ty, CharUnits align, mlir::Location loc,
const Twine &name = "tmp");
diff --git a/clang/lib/CIR/Dialect/IR/CIRMemorySlot.cpp b/clang/lib/CIR/Dialect/IR/CIRMemorySlot.cpp
index af6b5e4fbd9f6..5e44837979af3 100644
--- a/clang/lib/CIR/Dialect/IR/CIRMemorySlot.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRMemorySlot.cpp
@@ -75,3 +75,36 @@ DeletionKind cir::LoadOp::removeBlockingUses(
getResult().replaceAllUsesWith(reachingDefinition);
return DeletionKind::Delete;
}
+
+//===----------------------------------------------------------------------===//
+// Interfaces for StoreOp
+//===----------------------------------------------------------------------===//
+
+bool cir::StoreOp::loadsFrom(const MemorySlot &slot) { return false; }
+
+bool cir::StoreOp::storesTo(const MemorySlot &slot) {
+ return getAddr() == slot.ptr;
+}
+
+Value cir::StoreOp::getStored(const MemorySlot &slot, OpBuilder &builder,
+ Value reachingDef, const DataLayout &dataLayout) {
+ return getValue();
+}
+
+bool cir::StoreOp::canUsesBeRemoved(
+ const MemorySlot &slot, const SmallPtrSetImpl<OpOperand *> &blockingUses,
+ SmallVectorImpl<OpOperand *> &newBlockingUses,
+ const DataLayout &dataLayout) {
+ if (blockingUses.size() != 1)
+ return false;
+ Value blockingUse = (*blockingUses.begin())->get();
+ return blockingUse == slot.ptr && getAddr() == slot.ptr &&
+ getValue() != slot.ptr && slot.elemType == getValue().getType();
+}
+
+DeletionKind cir::StoreOp::removeBlockingUses(
+ const MemorySlot &slot, const SmallPtrSetImpl<OpOperand *> &blockingUses,
+ OpBuilder &builder, Value reachingDefinition,
+ const DataLayout &dataLayout) {
+ return DeletionKind::Delete;
+}
diff --git a/clang/test/CIR/CodeGen/basic.cpp b/clang/test/CIR/CodeGen/basic.cpp
index 210afcd541159..6a2faa725a34d 100644
--- a/clang/test/CIR/CodeGen/basic.cpp
+++ b/clang/test/CIR/CodeGen/basic.cpp
@@ -25,3 +25,23 @@ int f2() {
// CHECK: %[[I_PTR:.*]] = cir.alloca !cir.int<s, 32>, !cir.ptr<!cir.int<s, 32>>, ["i", const] {alignment = 4 : i64}
// CHECK: %[[I:.*]] = cir.load %[[I_PTR]] : !cir.ptr<!cir.int<s, 32>>, !cir.int<s, 32>
// CHECK: cir.return %[[I]] : !cir.int<s, 32>
+
+int f3(int i) {
+ return i;
+ }
+
+// CHECK: cir.func @f3(%[[ARG:.*]]: !cir.int<s, 32> loc({{.*}})) -> !cir.int<s, 32>
+// CHECK: %[[ARG_ALLOCA:.*]] = cir.alloca !cir.int<s, 32>, !cir.ptr<!cir.int<s, 32>>, ["i", init] {alignment = 4 : i64}
+// CHECK: cir.store %[[ARG]], %[[ARG_ALLOCA]] : !cir.int<s, 32>, !cir.ptr<!cir.int<s, 32>>
+// CHECK: %[[ARG_VAL:.*]] = cir.load %[[ARG_ALLOCA]] : !cir.ptr<!cir.int<s, 32>>, !cir.int<s, 32>
+// CHECK: cir.return %[[ARG_VAL]] : !cir.int<s, 32>
+
+int f4(const int i) {
+ return i;
+}
+
+// CHECK: cir.func @f4(%[[ARG:.*]]: !cir.int<s, 32> loc({{.*}})) -> !cir.int<s, 32>
+// CHECK: %[[ARG_ALLOCA:.*]] = cir.alloca !cir.int<s, 32>, !cir.ptr<!cir.int<s, 32>>, ["i", init, const] {alignment = 4 : i64}
+// CHECK: cir.store %[[ARG]], %[[ARG_ALLOCA]] : !cir.int<s, 32>, !cir.ptr<!cir.int<s, 32>>
+// CHECK: %[[ARG_VAL:.*]] = cir.load %[[ARG_ALLOCA]] : !cir.ptr<!cir.int<s, 32>>, !cir.int<s, 32>
+// CHECK: cir.return %[[ARG_VAL]] : !cir.int<s, 32>
>From 9f548a1492a3da5d9f1d4358916f88531e0dc9e7 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Fri, 28 Feb 2025 10:39:09 -0800
Subject: [PATCH 2/2] Replace auto with correct types
---
clang/lib/CIR/CodeGen/CIRGenFunction.cpp | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
index 76912d412fd06..7861a48c93244 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -169,10 +169,10 @@ void CIRGenFunction::startFunction(GlobalDecl gd, QualType returnType,
// TODO(cir): this should live in `emitFunctionProlog
// Declare all the function arguments in the symbol table.
for (const auto nameValue : llvm::zip(args, entryBB->getArguments())) {
- auto *paramVar = std::get<0>(nameValue);
+ const VarDecl *paramVar = std::get<0>(nameValue);
mlir::Value paramVal = std::get<1>(nameValue);
- auto alignment = getContext().getDeclAlign(paramVar);
- auto paramLoc = getLoc(paramVar->getSourceRange());
+ CharUnits alignment = getContext().getDeclAlign(paramVar);
+ mlir::Location paramLoc = getLoc(paramVar->getSourceRange());
paramVal.setLoc(paramLoc);
mlir::Value addrVal =
More information about the cfe-commits
mailing list