Skip to content

[CIR] Upstream func args alloca handling #129167

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Feb 28, 2025
Merged

Conversation

andykaylor
Copy link
Contributor

This change adds support for collecting function arguments and storing them in alloca memory slots.

This change adds support for collecting function arguments and storing
them in alloca memory slots.
@llvmbot llvmbot added clang Clang issues not falling into any other category ClangIR Anything related to the ClangIR project labels Feb 28, 2025
@llvmbot
Copy link
Member

llvmbot commented Feb 28, 2025

@llvm/pr-subscribers-clang

Author: Andy Kaylor (andykaylor)

Changes

This change adds support for collecting function arguments and storing them in alloca memory slots.


Full diff: https://github.com/llvm/llvm-project/pull/129167.diff

10 Files Affected:

  • (modified) clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h (+5)
  • (modified) clang/include/clang/CIR/Dialect/IR/CIROps.td (+39)
  • (modified) clang/include/clang/CIR/MissingFeatures.h (+1)
  • (modified) clang/lib/CIR/CodeGen/Address.h (+8)
  • (added) clang/lib/CIR/CodeGen/CIRGenCall.h (+28)
  • (modified) clang/lib/CIR/CodeGen/CIRGenDecl.cpp (+1-1)
  • (modified) clang/lib/CIR/CodeGen/CIRGenFunction.cpp (+69-7)
  • (modified) clang/lib/CIR/CodeGen/CIRGenFunction.h (+8-3)
  • (modified) clang/lib/CIR/Dialect/IR/CIRMemorySlot.cpp (+33)
  • (modified) clang/test/CIR/CodeGen/basic.cpp (+20)
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>

@llvmbot
Copy link
Member

llvmbot commented Feb 28, 2025

@llvm/pr-subscribers-clangir

Author: Andy Kaylor (andykaylor)

Changes

This change adds support for collecting function arguments and storing them in alloca memory slots.


Full diff: https://github.com/llvm/llvm-project/pull/129167.diff

10 Files Affected:

  • (modified) clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h (+5)
  • (modified) clang/include/clang/CIR/Dialect/IR/CIROps.td (+39)
  • (modified) clang/include/clang/CIR/MissingFeatures.h (+1)
  • (modified) clang/lib/CIR/CodeGen/Address.h (+8)
  • (added) clang/lib/CIR/CodeGen/CIRGenCall.h (+28)
  • (modified) clang/lib/CIR/CodeGen/CIRGenDecl.cpp (+1-1)
  • (modified) clang/lib/CIR/CodeGen/CIRGenFunction.cpp (+69-7)
  • (modified) clang/lib/CIR/CodeGen/CIRGenFunction.h (+8-3)
  • (modified) clang/lib/CIR/Dialect/IR/CIRMemorySlot.cpp (+33)
  • (modified) clang/test/CIR/CodeGen/basic.cpp (+20)
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>

// 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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change auto for real type? Same for lines below?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, 'auto' is only allowed when the type is either: 1- so many type aliases deep that readability is helped, or 2- the type is 'named' directly/clear from what the RHS says.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I know this. I don't know how I missed these. I'll probably do this a few more times before it completely sinks in.

// 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);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, 'auto' is only allowed when the type is either: 1- so many type aliases deep that readability is helped, or 2- the type is 'named' directly/clear from what the RHS says.

Copy link
Collaborator

@erichkeane erichkeane left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM once the 'auto' problems are fixed.

@andykaylor andykaylor merged commit 56cc929 into llvm:main Feb 28, 2025
11 checks passed
@andykaylor andykaylor deleted the cir-fn-args branch March 4, 2025 22:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang Clang issues not falling into any other category ClangIR Anything related to the ClangIR project
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants