[clang] 3989b78 - [CIR] Upstream basic alloca and load support (#128792)

via cfe-commits cfe-commits at lists.llvm.org
Thu Feb 27 14:22:31 PST 2025


Author: Andy Kaylor
Date: 2025-02-27T14:22:26-08:00
New Revision: 3989b78fa96f6c93da0fa23c7aa29a313b56831d

URL: https://github.com/llvm/llvm-project/commit/3989b78fa96f6c93da0fa23c7aa29a313b56831d
DIFF: https://github.com/llvm/llvm-project/commit/3989b78fa96f6c93da0fa23c7aa29a313b56831d.diff

LOG: [CIR] Upstream basic alloca and load support (#128792)

This change implements basic support in ClangIR for local variables
using the cir.alloca and cir.load operations.

Added: 
    clang/lib/CIR/CodeGen/Address.h
    clang/lib/CIR/CodeGen/CIRGenDecl.cpp
    clang/lib/CIR/CodeGen/CIRGenExpr.cpp
    clang/lib/CIR/CodeGen/CIRGenValue.h
    clang/lib/CIR/Dialect/IR/CIRMemorySlot.cpp
    clang/test/CIR/CodeGen/basic.cpp

Modified: 
    clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
    clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
    clang/include/clang/CIR/Dialect/IR/CIROps.td
    clang/include/clang/CIR/MissingFeatures.h
    clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
    clang/lib/CIR/CodeGen/CIRGenFunction.cpp
    clang/lib/CIR/CodeGen/CIRGenFunction.h
    clang/lib/CIR/CodeGen/CIRGenModule.h
    clang/lib/CIR/CodeGen/CIRGenStmt.cpp
    clang/lib/CIR/CodeGen/CMakeLists.txt
    clang/lib/CIR/Dialect/IR/CIRDialect.cpp
    clang/lib/CIR/Dialect/IR/CMakeLists.txt

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
index f03241a875845..14afdfc2758ea 100644
--- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
+++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
@@ -9,6 +9,7 @@
 #ifndef LLVM_CLANG_CIR_DIALECT_BUILDER_CIRBASEBUILDER_H
 #define LLVM_CLANG_CIR_DIALECT_BUILDER_CIRBASEBUILDER_H
 
+#include "clang/AST/CharUnits.h"
 #include "clang/CIR/Dialect/IR/CIRAttrs.h"
 #include "clang/CIR/Dialect/IR/CIRDialect.h"
 #include "clang/CIR/Dialect/IR/CIRTypes.h"
@@ -51,6 +52,47 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
     return cir::ConstPtrAttr::get(
         getContext(), mlir::cast<cir::PointerType>(type), valueAttr);
   }
+
+  mlir::Value createAlloca(mlir::Location loc, cir::PointerType addrType,
+                           mlir::Type type, llvm::StringRef name,
+                           mlir::IntegerAttr alignment) {
+    return create<cir::AllocaOp>(loc, addrType, type, name, alignment);
+  }
+
+  cir::LoadOp createLoad(mlir::Location loc, mlir::Value ptr,
+                         bool isVolatile = false, uint64_t alignment = 0) {
+    mlir::IntegerAttr intAttr;
+    if (alignment)
+      intAttr = mlir::IntegerAttr::get(
+          mlir::IntegerType::get(ptr.getContext(), 64), alignment);
+
+    return create<cir::LoadOp>(loc, ptr);
+  }
+
+  //
+  // Block handling helpers
+  // ----------------------
+  //
+  static OpBuilder::InsertPoint getBestAllocaInsertPoint(mlir::Block *block) {
+    auto last =
+        std::find_if(block->rbegin(), block->rend(), [](mlir::Operation &op) {
+          // TODO: Add LabelOp missing feature here
+          return mlir::isa<cir::AllocaOp>(&op);
+        });
+
+    if (last != block->rend())
+      return OpBuilder::InsertPoint(block, ++mlir::Block::iterator(&*last));
+    return OpBuilder::InsertPoint(block, block->begin());
+  };
+
+  mlir::IntegerAttr getSizeFromCharUnits(mlir::MLIRContext *ctx,
+                                         clang::CharUnits size) {
+    // Note that mlir::IntegerType is used instead of cir::IntType here
+    // because we don't need sign information for this to be useful, so keep
+    // it simple.
+    return mlir::IntegerAttr::get(mlir::IntegerType::get(ctx, 64),
+                                  size.getQuantity());
+  }
 };
 
 } // namespace cir

diff  --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
index 097616ba06749..ece04c225e322 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
@@ -54,6 +54,21 @@ def CIR_BoolAttr : CIR_Attr<"Bool", "bool", [TypedAttrInterface]> {
   }];
 }
 
+//===----------------------------------------------------------------------===//
+// UndefAttr
+//===----------------------------------------------------------------------===//
+
+def UndefAttr : CIR_Attr<"Undef", "undef", [TypedAttrInterface]> {
+  let summary = "Represent an undef constant";
+  let description = [{
+    The UndefAttr represents an undef constant, corresponding to LLVM's notion
+    of undef.
+  }];
+
+  let parameters = (ins AttributeSelfTypeParameter<"">:$type);
+  let assemblyFormat = [{}];
+}
+
 //===----------------------------------------------------------------------===//
 // IntegerAttr
 //===----------------------------------------------------------------------===//

diff  --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index f9ce38588e436..083cf46a93ae6 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -115,6 +115,119 @@ def ConstantOp : CIR_Op<"const",
   let hasFolder = 1;
 }
 
+//===----------------------------------------------------------------------===//
+// AllocaOp
+//===----------------------------------------------------------------------===//
+
+class AllocaTypesMatchWith<string summary, string lhsArg, string rhsArg,
+                     string transform, string comparator = "std::equal_to<>()">
+  : PredOpTrait<summary, CPred<
+      comparator # "(" #
+      !subst("$_self", "$" # lhsArg # ".getType()", transform) #
+             ", $" # rhsArg # ")">> {
+  string lhs = lhsArg;
+  string rhs = rhsArg;
+  string transformer = transform;
+}
+
+def AllocaOp : CIR_Op<"alloca", [
+  AllocaTypesMatchWith<"'allocaType' matches pointee type of 'addr'",
+                 "addr", "allocaType",
+                 "cast<PointerType>($_self).getPointee()">,
+                 DeclareOpInterfaceMethods<PromotableAllocationOpInterface>]> {
+  let summary = "Defines a scope-local variable";
+  let description = [{
+    The `cir.alloca` operation defines a scope-local variable.
+
+    The presence of the `const` attribute indicates that the local variable is
+    declared with C/C++ `const` keyword.
+
+    The result type is a pointer to the input's type.
+
+    Example:
+
+    ```mlir
+    // int count;
+    %0 = cir.alloca i32, !cir.ptr<i32>, ["count"] {alignment = 4 : i64}
+
+    // int *ptr;
+    %1 = cir.alloca !cir.ptr<i32>, !cir.ptr<!cir.ptr<i32>>, ["ptr"] {alignment = 8 : i64}
+    ...
+    ```
+  }];
+
+  let arguments = (ins
+    TypeAttr:$allocaType,
+    StrAttr:$name,
+    UnitAttr:$init,
+    UnitAttr:$constant,
+    ConfinedAttr<OptionalAttr<I64Attr>, [IntMinValue<0>]>:$alignment,
+    OptionalAttr<ArrayAttr>:$annotations
+  );
+
+  let results = (outs Res<CIR_PointerType, "",
+                      [MemAlloc<AutomaticAllocationScopeResource>]>:$addr);
+
+  let skipDefaultBuilders = 1;
+  let builders = [
+    OpBuilder<(ins "mlir::Type":$addr,
+                   "mlir::Type":$allocaType,
+                   "llvm::StringRef":$name,
+                   "mlir::IntegerAttr":$alignment)>
+  ];
+
+  let extraClassDeclaration = [{
+    // Whether the alloca input type is a pointer.
+    bool isPointerType() { return ::mlir::isa<::cir::PointerType>(getAllocaType()); }
+  }];
+
+  let assemblyFormat = [{
+    $allocaType `,` qualified(type($addr)) `,`
+    `[` $name
+       (`,` `init` $init^)?
+       (`,` `const` $constant^)?
+    `]`
+    ($annotations^)? attr-dict
+  }];
+
+  let hasVerifier = 0;
+}
+
+//===----------------------------------------------------------------------===//
+// LoadOp
+//===----------------------------------------------------------------------===//
+
+def LoadOp : CIR_Op<"load", [
+  TypesMatchWith<"type of 'result' matches pointee type of 'addr'",
+                 "addr", "result",
+                 "cast<PointerType>($_self).getPointee()">,
+                 DeclareOpInterfaceMethods<PromotableMemOpInterface>]> {
+
+  let summary = "Load value from memory adddress";
+  let description = [{
+    `cir.load` reads a value (lvalue to rvalue conversion) given an address
+    backed up by a `cir.ptr` type.
+
+    Example:
+
+    ```mlir
+
+    // Read from local variable, address in %0.
+    %1 = cir.load %0 : !cir.ptr<i32>, i32
+    ```
+  }];
+
+  let arguments = (ins Arg<CIR_PointerType, "the address to load from",
+                           [MemRead]>:$addr);
+  let results = (outs CIR_AnyType:$result);
+
+  let assemblyFormat = [{
+    $addr `:` qualified(type($addr)) `,` type($result) attr-dict
+  }];
+
+  // FIXME: add verifier.
+}
+
 //===----------------------------------------------------------------------===//
 // ReturnOp
 //===----------------------------------------------------------------------===//

diff  --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index d4fcd52e7e6e3..5c7e10d018809 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -30,12 +30,35 @@ struct MissingFeatures {
   // This isn't needed until we add support for bools.
   static bool convertTypeForMemory() { return false; }
 
+  // CIRGenFunction implementation details
+  static bool cgfSymbolTable() { return false; }
+
   // Unhandled global/linkage information.
   static bool opGlobalDSOLocal() { return false; }
   static bool opGlobalThreadLocal() { return false; }
   static bool opGlobalConstant() { return false; }
   static bool opGlobalAlignment() { return false; }
   static bool opGlobalLinkage() { return false; }
+
+  // Load attributes
+  static bool opLoadThreadLocal() { return false; }
+  static bool opLoadEmitScalarRangeCheck() { return false; }
+  static bool opLoadBooleanRepresentation() { return false; }
+
+  // AllocaOp handling
+  static bool opAllocaVarDeclContext() { return false; }
+  static bool opAllocaStaticLocal() { return false; }
+  static bool opAllocaNonGC() { return false; }
+  static bool opAllocaImpreciseLifetime() { return false; }
+  static bool opAllocaPreciseLifetime() { return false; }
+  static bool opAllocaTLS() { return false; }
+  static bool opAllocaOpenMPThreadPrivate() { return false; }
+  static bool opAllocaEscapeByReference() { return false; }
+  static bool opAllocaReference() { return false; }
+
+  // Misc
+  static bool scalarConversionOpts() { return false; }
+  static bool tryEmitAsConstant() { return false; }
 };
 
 } // namespace cir

diff  --git a/clang/lib/CIR/CodeGen/Address.h b/clang/lib/CIR/CodeGen/Address.h
new file mode 100644
index 0000000000000..72e7e1dcf1560
--- /dev/null
+++ b/clang/lib/CIR/CodeGen/Address.h
@@ -0,0 +1,76 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This class provides a simple wrapper for a pair of a pointer and an
+// alignment.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef CLANG_LIB_CIR_ADDRESS_H
+#define CLANG_LIB_CIR_ADDRESS_H
+
+#include "mlir/IR/Value.h"
+#include "clang/AST/CharUnits.h"
+#include "clang/CIR/Dialect/IR/CIRTypes.h"
+#include "llvm/ADT/PointerIntPair.h"
+
+namespace clang::CIRGen {
+
+class Address {
+
+  // The boolean flag indicates whether the pointer is known to be non-null.
+  llvm::PointerIntPair<mlir::Value, 1, bool> pointerAndKnownNonNull;
+
+  /// The expected CIR type of the pointer. Carrying accurate element type
+  /// information in Address makes it more convenient to work with Address
+  /// values and allows frontend assertions to catch simple mistakes.
+  mlir::Type elementType;
+
+  clang::CharUnits alignment;
+
+protected:
+  Address(std::nullptr_t) : elementType(nullptr) {}
+
+public:
+  Address(mlir::Value pointer, mlir::Type elementType,
+          clang::CharUnits alignment)
+      : pointerAndKnownNonNull(pointer, false), elementType(elementType),
+        alignment(alignment) {
+    assert(mlir::isa<cir::PointerType>(pointer.getType()) &&
+           "Expected cir.ptr type");
+
+    assert(pointer && "Pointer cannot be null");
+    assert(elementType && "Element type cannot be null");
+    assert(!alignment.isZero() && "Alignment cannot be zero");
+
+    assert(mlir::cast<cir::PointerType>(pointer.getType()).getPointee() ==
+           elementType);
+  }
+
+  static Address invalid() { return Address(nullptr); }
+  bool isValid() const {
+    return pointerAndKnownNonNull.getPointer() != nullptr;
+  }
+
+  mlir::Value getPointer() const {
+    assert(isValid());
+    return pointerAndKnownNonNull.getPointer();
+  }
+
+  mlir::Type getElementType() const {
+    assert(isValid());
+    assert(mlir::cast<cir::PointerType>(
+               pointerAndKnownNonNull.getPointer().getType())
+               .getPointee() == elementType);
+    return elementType;
+  }
+};
+
+} // namespace clang::CIRGen
+
+#endif // CLANG_LIB_CIR_ADDRESS_H

diff  --git a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
new file mode 100644
index 0000000000000..e44cad559d509
--- /dev/null
+++ b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
@@ -0,0 +1,113 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This contains code to emit Decl nodes as CIR code.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CIRGenFunction.h"
+#include "clang/AST/Attr.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/CIR/MissingFeatures.h"
+
+using namespace clang;
+using namespace clang::CIRGen;
+
+void CIRGenFunction::emitAutoVarAlloca(const VarDecl &d) {
+  QualType ty = d.getType();
+  if (ty.getAddressSpace() != LangAS::Default)
+    cgm.errorNYI(d.getSourceRange(), "emitAutoVarAlloca: address space");
+
+  auto loc = getLoc(d.getSourceRange());
+
+  if (d.isEscapingByref())
+    cgm.errorNYI(d.getSourceRange(),
+                 "emitAutoVarDecl: decl escaping by reference");
+
+  CharUnits alignment = getContext().getDeclAlign(&d);
+
+  // If the type is variably-modified, emit all the VLA sizes for it.
+  if (ty->isVariablyModifiedType())
+    cgm.errorNYI(d.getSourceRange(), "emitAutoVarDecl: variably modified type");
+
+  Address address = Address::invalid();
+  if (!ty->isConstantSizeType())
+    cgm.errorNYI(d.getSourceRange(), "emitAutoVarDecl: non-constant size type");
+
+  // A normal fixed sized variable becomes an alloca in the entry block,
+  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);
+
+  setAddrOfLocalVar(&d, address);
+}
+
+void CIRGenFunction::emitAutoVarInit(const clang::VarDecl &d) {
+  QualType type = d.getType();
+
+  // If this local has an initializer, emit it now.
+  const Expr *init = d.getInit();
+
+  if (init || !type.isPODType(getContext())) {
+    cgm.errorNYI(d.getSourceRange(), "emitAutoVarInit");
+  }
+}
+
+void CIRGenFunction::emitAutoVarCleanups(const clang::VarDecl &d) {
+  // Check the type for a cleanup.
+  if (QualType::DestructionKind dtorKind = d.needsDestruction(getContext()))
+    cgm.errorNYI(d.getSourceRange(), "emitAutoVarCleanups: type cleanup");
+
+  assert(!cir::MissingFeatures::opAllocaPreciseLifetime());
+
+  // Handle the cleanup attribute.
+  if (d.hasAttr<CleanupAttr>())
+    cgm.errorNYI(d.getSourceRange(), "emitAutoVarCleanups: CleanupAttr");
+}
+
+/// Emit code and set up symbol table for a variable declaration with auto,
+/// register, or no storage class specifier. These turn into simple stack
+/// objects, globals depending on target.
+void CIRGenFunction::emitAutoVarDecl(const VarDecl &d) {
+  emitAutoVarAlloca(d);
+  emitAutoVarInit(d);
+  emitAutoVarCleanups(d);
+}
+
+void CIRGenFunction::emitVarDecl(const VarDecl &d) {
+  // If the declaration has external storage, don't emit it now, allow it to be
+  // emitted lazily on its first use.
+  if (d.hasExternalStorage())
+    return;
+
+  if (d.getStorageDuration() != SD_Automatic)
+    cgm.errorNYI(d.getSourceRange(), "emitVarDecl automatic storage duration");
+  if (d.getType().getAddressSpace() == LangAS::opencl_local)
+    cgm.errorNYI(d.getSourceRange(), "emitVarDecl openCL address space");
+
+  assert(d.hasLocalStorage());
+
+  assert(!cir::MissingFeatures::opAllocaVarDeclContext());
+  return emitAutoVarDecl(d);
+}
+
+void CIRGenFunction::emitDecl(const Decl &d) {
+  switch (d.getKind()) {
+  case Decl::Var: {
+    const VarDecl &vd = cast<VarDecl>(d);
+    assert(vd.isLocalVarDecl() &&
+           "Should not see file-scope variables inside a function!");
+    emitVarDecl(vd);
+    return;
+  }
+  default:
+    cgm.errorNYI(d.getSourceRange(), "emitDecl: unhandled decl type");
+  }
+}

diff  --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
new file mode 100644
index 0000000000000..ccc3e20875263
--- /dev/null
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -0,0 +1,130 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This contains code to emit Expr nodes as CIR code.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Address.h"
+#include "CIRGenFunction.h"
+#include "CIRGenValue.h"
+#include "mlir/IR/BuiltinAttributes.h"
+#include "clang/AST/Attr.h"
+#include "clang/AST/CharUnits.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/CIR/Dialect/IR/CIRDialect.h"
+#include "clang/CIR/MissingFeatures.h"
+
+using namespace clang;
+using namespace clang::CIRGen;
+using namespace cir;
+
+mlir::Value CIRGenFunction::emitLoadOfScalar(LValue lvalue,
+                                             SourceLocation loc) {
+  assert(!cir::MissingFeatures::opLoadThreadLocal());
+  assert(!cir::MissingFeatures::opLoadEmitScalarRangeCheck());
+  assert(!cir::MissingFeatures::opLoadBooleanRepresentation());
+
+  Address addr = lvalue.getAddress();
+  mlir::Type eltTy = addr.getElementType();
+
+  mlir::Value ptr = addr.getPointer();
+  if (mlir::isa<cir::VoidType>(eltTy))
+    cgm.errorNYI(loc, "emitLoadOfScalar: void type");
+
+  mlir::Value loadOp = builder.CIRBaseBuilderTy::createLoad(
+      getLoc(loc), ptr, false /*isVolatile*/);
+
+  return loadOp;
+}
+
+/// 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.
+RValue CIRGenFunction::emitLoadOfLValue(LValue lv, SourceLocation loc) {
+  assert(!lv.getType()->isFunctionType());
+  assert(!(lv.getType()->isConstantMatrixType()) && "not implemented");
+
+  if (lv.isSimple())
+    return RValue::get(emitLoadOfScalar(lv, loc));
+
+  cgm.errorNYI(loc, "emitLoadOfLValue");
+  return RValue::get(nullptr);
+}
+
+LValue CIRGenFunction::emitDeclRefLValue(const DeclRefExpr *e) {
+  const NamedDecl *nd = e->getDecl();
+  QualType ty = e->getType();
+
+  assert(e->isNonOdrUse() != NOUR_Unevaluated &&
+         "should not emit an unevaluated operand");
+
+  if (const auto *vd = dyn_cast<VarDecl>(nd)) {
+    // Checks for omitted feature handling
+    assert(!cir::MissingFeatures::opAllocaStaticLocal());
+    assert(!cir::MissingFeatures::opAllocaNonGC());
+    assert(!cir::MissingFeatures::opAllocaImpreciseLifetime());
+    assert(!cir::MissingFeatures::opAllocaTLS());
+    assert(!cir::MissingFeatures::opAllocaOpenMPThreadPrivate());
+    assert(!cir::MissingFeatures::opAllocaEscapeByReference());
+
+    // Check if this is a global variable
+    if (vd->hasLinkage() || vd->isStaticDataMember())
+      cgm.errorNYI(vd->getSourceRange(), "emitDeclRefLValue: global variable");
+
+    Address addr = Address::invalid();
+
+    // The variable should generally be present in the local decl map.
+    auto iter = LocalDeclMap.find(vd);
+    if (iter != LocalDeclMap.end()) {
+      addr = iter->second;
+    } else {
+      // Otherwise, it might be static local we haven't emitted yet for some
+      // reason; most likely, because it's in an outer function.
+      cgm.errorNYI(vd->getSourceRange(), "emitDeclRefLValue: static local");
+    }
+
+    return LValue::makeAddr(addr, ty);
+  }
+
+  cgm.errorNYI(e->getSourceRange(), "emitDeclRefLValue: unhandled decl type");
+  return LValue();
+}
+
+mlir::Value CIRGenFunction::emitAlloca(StringRef name, mlir::Type ty,
+                                       mlir::Location loc,
+                                       CharUnits alignment) {
+  mlir::Block *entryBlock = getCurFunctionEntryBlock();
+
+  // CIR uses its own alloca address space rather than follow the target data
+  // layout like original CodeGen. The data layout awareness should be done in
+  // the lowering pass instead.
+  assert(!cir::MissingFeatures::addressSpace());
+  cir::PointerType localVarPtrTy = builder.getPointerTo(ty);
+  mlir::IntegerAttr alignIntAttr = cgm.getSize(alignment);
+
+  mlir::Value addr;
+  {
+    mlir::OpBuilder::InsertionGuard guard(builder);
+    builder.restoreInsertionPoint(builder.getBestAllocaInsertPoint(entryBlock));
+    addr = builder.createAlloca(loc, /*addr type*/ localVarPtrTy,
+                                /*var type*/ ty, name, alignIntAttr);
+    assert(!cir::MissingFeatures::opAllocaVarDeclContext());
+  }
+  return addr;
+}
+
+/// This creates an alloca and inserts it  at the current insertion point of the
+/// builder.
+Address CIRGenFunction::createTempAlloca(mlir::Type ty, CharUnits align,
+                                         mlir::Location loc,
+                                         const Twine &name) {
+  mlir::Value alloca = emitAlloca(name.str(), ty, loc, align);
+  return Address(alloca, ty, align);
+}

diff  --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
index 24a959108f73b..90a2fd2a5d806 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
@@ -11,9 +11,11 @@
 //===----------------------------------------------------------------------===//
 
 #include "CIRGenFunction.h"
+#include "CIRGenValue.h"
 
 #include "clang/AST/Expr.h"
 #include "clang/AST/StmtVisitor.h"
+#include "clang/CIR/MissingFeatures.h"
 
 #include "mlir/IR/Value.h"
 
@@ -52,6 +54,19 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
     return {};
   }
 
+  /// Emits the address of the l-value, then loads and returns the result.
+  mlir::Value emitLoadOfLValue(const Expr *e) {
+    LValue lv = cgf.emitLValue(e);
+    // FIXME: add some akin to EmitLValueAlignmentAssumption(E, V);
+    return cgf.emitLoadOfLValue(lv, e->getExprLoc()).getScalarVal();
+  }
+
+  // l-values
+  mlir::Value VisitDeclRefExpr(DeclRefExpr *e) {
+    assert(!cir::MissingFeatures::tryEmitAsConstant());
+    return emitLoadOfLValue(e);
+  }
+
   mlir::Value VisitIntegerLiteral(const IntegerLiteral *e) {
     mlir::Type type = cgf.convertType(e->getType());
     return builder.create<cir::ConstantOp>(
@@ -65,7 +80,27 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> {
         cgf.getLoc(e->getExprLoc()), type,
         builder.getCIRBoolAttr(e->getValue()));
   }
+
+  mlir::Value VisitCastExpr(CastExpr *E);
+
+  /// Emit a conversion from the specified type to the specified destination
+  /// type, both of which are CIR scalar types.
+  /// TODO: do we need ScalarConversionOpts here? Should be done in another
+  /// pass.
+  mlir::Value emitScalarConversion(mlir::Value src, QualType srcType,
+                                   QualType dstType, SourceLocation loc) {
+    // No sort of type conversion is implemented yet, but the path for implicit
+    // paths goes through here even if the type isn't being changed.
+    srcType = srcType.getCanonicalType();
+    dstType = dstType.getCanonicalType();
+    if (srcType == dstType)
+      return src;
+
+    cgf.getCIRGenModule().errorNYI(loc,
+                                   "emitScalarConversion for unequal types");
+  }
 };
+
 } // namespace
 
 /// Emit the computation of the specified expression of scalar type.
@@ -75,3 +110,31 @@ mlir::Value CIRGenFunction::emitScalarExpr(const Expr *e) {
 
   return ScalarExprEmitter(*this, builder).Visit(const_cast<Expr *>(e));
 }
+
+// Emit code for an explicit or implicit cast.  Implicit
+// casts have to handle a more broad range of conversions than explicit
+// casts, as they handle things like function to ptr-to-function decay
+// etc.
+mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr *ce) {
+  Expr *e = ce->getSubExpr();
+  QualType destTy = ce->getType();
+  CastKind kind = ce->getCastKind();
+
+  switch (kind) {
+  case CK_LValueToRValue:
+    assert(cgf.getContext().hasSameUnqualifiedType(e->getType(), destTy));
+    assert(e->isGLValue() && "lvalue-to-rvalue applied to r-value!");
+    return Visit(const_cast<Expr *>(e));
+
+  case CK_IntegralCast: {
+    assert(!cir::MissingFeatures::scalarConversionOpts());
+    return emitScalarConversion(Visit(e), e->getType(), destTy,
+                                ce->getExprLoc());
+  }
+
+  default:
+    cgf.getCIRGenModule().errorNYI(e->getSourceRange(),
+                                   "CastExpr: ", ce->getCastKindName());
+  }
+  return {};
+}

diff  --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
index bba2f71a87627..86986b5847e98 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -13,6 +13,7 @@
 #include "CIRGenFunction.h"
 
 #include "clang/AST/GlobalDecl.h"
+#include "clang/CIR/MissingFeatures.h"
 
 #include <cassert>
 
@@ -131,6 +132,21 @@ 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) {
+  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 (ty->isReferenceType() || ty.isConstQualified())
+    allocaOp.setConstantAttr(mlir::UnitAttr::get(&getMLIRContext()));
+
+  return mlir::success();
+}
+
 void CIRGenFunction::startFunction(GlobalDecl gd, QualType returnType,
                                    cir::FuncOp fn, cir::FuncType funcType,
                                    SourceLocation loc,
@@ -153,6 +169,7 @@ mlir::LogicalResult CIRGenFunction::emitFunctionBody(const clang::Stmt *body) {
     emitCompoundStmtWithoutScope(*block);
   else
     result = emitStmt(body, /*useCurrentScope=*/true);
+
   return result;
 }
 
@@ -217,4 +234,20 @@ cir::FuncOp CIRGenFunction::generateCode(clang::GlobalDecl gd, cir::FuncOp fn,
   return fn;
 }
 
+/// Emit code to compute a designator that specifies the location
+/// of the expression.
+/// FIXME: document this function better.
+LValue CIRGenFunction::emitLValue(const Expr *e) {
+  // FIXME: ApplyDebugLocation DL(*this, e);
+  switch (e->getStmtClass()) {
+  default:
+    getCIRGenModule().errorNYI(e->getSourceRange(),
+                               std::string("l-value not implemented for '") +
+                                   e->getStmtClassName() + "'");
+    break;
+  case Expr::DeclRefExprClass:
+    return emitDeclRefLValue(cast<DeclRefExpr>(e));
+  }
+}
+
 } // namespace clang::CIRGen

diff  --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 92fbea16d3aa1..e0888acdc3dce 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -16,8 +16,12 @@
 #include "CIRGenBuilder.h"
 #include "CIRGenModule.h"
 #include "CIRGenTypeCache.h"
+#include "CIRGenValue.h"
+
+#include "Address.h"
 
 #include "clang/AST/ASTContext.h"
+#include "clang/AST/CharUnits.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/Type.h"
 #include "clang/CIR/Dialect/IR/CIRDialect.h"
@@ -49,6 +53,11 @@ class CIRGenFunction : public CIRGenTypeCache {
   /// for.
   mlir::Operation *curFn = nullptr;
 
+  using DeclMapTy = llvm::DenseMap<const clang::Decl *, Address>;
+  /// This keeps track of the CIR allocas or globals for local C
+  /// declarations.
+  DeclMapTy LocalDeclMap;
+
   clang::ASTContext &getContext() const { return cgm.getASTContext(); }
 
   CIRGenBuilderTy &getBuilder() { return builder; }
@@ -56,6 +65,12 @@ class CIRGenFunction : public CIRGenTypeCache {
   CIRGenModule &getCIRGenModule() { return cgm; }
   const CIRGenModule &getCIRGenModule() const { return cgm; }
 
+  mlir::Block *getCurFunctionEntryBlock() {
+    auto fn = mlir::dyn_cast<cir::FuncOp>(curFn);
+    assert(fn && "other callables NYI");
+    return &fn.getRegion().front();
+  }
+
   mlir::Type convertTypeForMem(QualType T);
 
   mlir::Type convertType(clang::QualType T);
@@ -78,6 +93,17 @@ class CIRGenFunction : public CIRGenTypeCache {
 
   mlir::MLIRContext &getMLIRContext() { return cgm.getMLIRContext(); }
 
+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,
+                              clang::QualType ty, mlir::Location loc,
+                              clang::CharUnits alignment);
+
+public:
+  mlir::Value emitAlloca(llvm::StringRef name, mlir::Type ty,
+                         mlir::Location loc, clang::CharUnits alignment);
+
   /// Use to track source locations across nested visitor traversals.
   /// Always use a `SourceLocRAIIObject` to change currSrcLoc.
   std::optional<mlir::Location> currSrcLoc;
@@ -121,8 +147,50 @@ class CIRGenFunction : public CIRGenTypeCache {
 
   void emitCompoundStmtWithoutScope(const clang::CompoundStmt &s);
 
+  mlir::LogicalResult emitDeclStmt(const clang::DeclStmt &s);
+
   mlir::LogicalResult emitReturnStmt(const clang::ReturnStmt &s);
 
+  /// 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.
+  RValue emitLoadOfLValue(LValue lv, SourceLocation loc);
+
+  /// EmitLoadOfScalar - Load a scalar value from an address, taking
+  /// care to appropriately convert from the memory representation to
+  /// the LLVM value representation.  The l-value must be a simple
+  /// l-value.
+  mlir::Value emitLoadOfScalar(LValue lvalue, SourceLocation loc);
+
+  /// Emit code to compute a designator that specifies the location
+  /// of the expression.
+  /// FIXME: document this function better.
+  LValue emitLValue(const clang::Expr *e);
+
+  void emitDecl(const clang::Decl &d);
+
+  LValue emitDeclRefLValue(const clang::DeclRefExpr *e);
+
+  /// Emit code and set up symbol table for a variable declaration with auto,
+  /// register, or no storage class specifier. These turn into simple stack
+  /// objects, globals depending on target.
+  void emitAutoVarDecl(const clang::VarDecl &d);
+
+  void emitAutoVarAlloca(const clang::VarDecl &d);
+  void emitAutoVarInit(const clang::VarDecl &d);
+  void emitAutoVarCleanups(const clang::VarDecl &d);
+
+  /// This method handles emission of any variable declaration
+  /// inside a function, including static vars etc.
+  void emitVarDecl(const clang::VarDecl &d);
+
+  /// Set the address of a local variable.
+  void setAddrOfLocalVar(const clang::VarDecl *vd, Address addr) {
+    assert(!LocalDeclMap.count(vd) && "Decl already exists in LocalDeclMap!");
+    LocalDeclMap.insert({vd, addr});
+    // TODO: Add symbol table support
+  }
+
   /// Emit the computation of the specified expression of scalar type.
   mlir::Value emitScalarExpr(const clang::Expr *e);
   cir::FuncOp generateCode(clang::GlobalDecl gd, cir::FuncOp fn,
@@ -134,8 +202,10 @@ class CIRGenFunction : public CIRGenTypeCache {
   void startFunction(clang::GlobalDecl gd, clang::QualType retTy,
                      cir::FuncOp fn, cir::FuncType funcType,
                      clang::SourceLocation loc, clang::SourceLocation startLoc);
-};
 
+  Address createTempAlloca(mlir::Type ty, CharUnits align, mlir::Location loc,
+                           const Twine &name = "tmp");
+};
 } // namespace clang::CIRGen
 
 #endif

diff  --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h
index bf3a4d1130f15..71a37b8c9a2ea 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.h
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.h
@@ -17,6 +17,7 @@
 #include "CIRGenTypeCache.h"
 #include "CIRGenTypes.h"
 
+#include "clang/AST/CharUnits.h"
 #include "clang/CIR/Dialect/IR/CIRDialect.h"
 
 #include "mlir/IR/Builders.h"
@@ -116,6 +117,10 @@ class CIRGenModule : public CIRGenTypeCache {
                                 cir::FuncType funcType,
                                 const clang::FunctionDecl *funcDecl);
 
+  mlir::IntegerAttr getSize(CharUnits size) {
+    return builder.getSizeFromCharUnits(&getMLIRContext(), size);
+  }
+
   const llvm::Triple &getTriple() const { return target.getTriple(); }
 
   /// Helpers to emit "not yet implemented" error diagnostics

diff  --git a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
index f42f30cc5a433..ed5d87a39704a 100644
--- a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp
@@ -68,6 +68,8 @@ mlir::LogicalResult CIRGenFunction::emitSimpleStmt(const Stmt *s,
   default:
     // Only compound and return statements are supported right now.
     return mlir::failure();
+  case Stmt::DeclStmtClass:
+    return emitDeclStmt(cast<DeclStmt>(*s));
   case Stmt::CompoundStmtClass:
     if (useCurrentScope)
       emitCompoundStmtWithoutScope(cast<CompoundStmt>(*s));
@@ -81,6 +83,15 @@ mlir::LogicalResult CIRGenFunction::emitSimpleStmt(const Stmt *s,
   return mlir::success();
 }
 
+mlir::LogicalResult CIRGenFunction::emitDeclStmt(const DeclStmt &s) {
+  assert(builder.getInsertionBlock() && "expected valid insertion point");
+
+  for (const Decl *I : s.decls())
+    emitDecl(*I);
+
+  return mlir::success();
+}
+
 mlir::LogicalResult CIRGenFunction::emitReturnStmt(const ReturnStmt &s) {
   mlir::Location loc = getLoc(s.getSourceRange());
   const Expr *rv = s.getRetValue();

diff  --git a/clang/lib/CIR/CodeGen/CIRGenValue.h b/clang/lib/CIR/CodeGen/CIRGenValue.h
new file mode 100644
index 0000000000000..d29646983fd30
--- /dev/null
+++ b/clang/lib/CIR/CodeGen/CIRGenValue.h
@@ -0,0 +1,125 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 implement wrappers around mlir::Value in order to fully
+// represent the range of values for C L- and R- values.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef CLANG_LIB_CIR_CIRGENVALUE_H
+#define CLANG_LIB_CIR_CIRGENVALUE_H
+
+#include "Address.h"
+
+#include "clang/AST/CharUnits.h"
+#include "clang/AST/Type.h"
+
+#include "llvm/ADT/PointerIntPair.h"
+
+#include "mlir/IR/Value.h"
+
+namespace clang::CIRGen {
+
+/// This trivial value class is used to represent the result of an
+/// expression that is evaluated. It can be one of three things: either a
+/// simple MLIR SSA value, a pair of SSA values for complex numbers, or the
+/// address of an aggregate value in memory.
+class RValue {
+  enum Flavor { Scalar, Complex, Aggregate };
+
+  // Stores first value and flavor.
+  llvm::PointerIntPair<mlir::Value, 2, Flavor> v1;
+  // Stores second value and volatility.
+  llvm::PointerIntPair<llvm::PointerUnion<mlir::Value, int *>, 1, bool> v2;
+  // Stores element type for aggregate values.
+  mlir::Type elementType;
+
+public:
+  bool isScalar() const { return v1.getInt() == Scalar; }
+
+  /// Return the mlir::Value of this scalar value.
+  mlir::Value getScalarVal() const {
+    assert(isScalar() && "Not a scalar!");
+    return v1.getPointer();
+  }
+
+  static RValue get(mlir::Value v) {
+    RValue er;
+    er.v1.setPointer(v);
+    er.v1.setInt(Scalar);
+    er.v2.setInt(false);
+    return er;
+  }
+};
+
+/// The source of the alignment of an l-value; an expression of
+/// confidence in the alignment actually matching the estimate.
+enum class AlignmentSource {
+  /// The l-value was an access to a declared entity or something
+  /// equivalently strong, like the address of an array allocated by a
+  /// language runtime.
+  Decl,
+
+  /// The l-value was considered opaque, so the alignment was
+  /// determined from a type, but that type was an explicitly-aligned
+  /// typedef.
+  AttributedType,
+
+  /// The l-value was considered opaque, so the alignment was
+  /// determined from a type.
+  Type
+};
+
+class LValue {
+  enum {
+    Simple,       // This is a normal l-value, use getAddress().
+    VectorElt,    // This is a vector element l-value (V[i]), use getVector*
+    BitField,     // This is a bitfield l-value, use getBitfield*.
+    ExtVectorElt, // This is an extended vector subset, use getExtVectorComp
+    GlobalReg,    // This is a register l-value, use getGlobalReg()
+    MatrixElt     // This is a matrix element, use getVector*
+  } lvType;
+  clang::QualType type;
+
+  mlir::Value v;
+  mlir::Type elementType;
+
+  void initialize(clang::QualType type) { this->type = type; }
+
+public:
+  bool isSimple() const { return lvType == Simple; }
+
+  // TODO: Add support for volatile
+  bool isVolatile() const { return false; }
+
+  clang::QualType getType() const { return type; }
+
+  mlir::Value getPointer() const { return v; }
+
+  clang::CharUnits getAlignment() const {
+    // TODO: Handle alignment
+    return clang::CharUnits::One();
+  }
+
+  Address getAddress() const {
+    return Address(getPointer(), elementType, getAlignment());
+  }
+
+  static LValue makeAddr(Address address, clang::QualType t) {
+    LValue r;
+    r.lvType = Simple;
+    r.v = address.getPointer();
+    r.elementType = address.getElementType();
+    r.initialize(t);
+    return r;
+  }
+};
+
+} // namespace clang::CIRGen
+
+#endif // CLANG_LIB_CIR_CIRGENVALUE_H

diff  --git a/clang/lib/CIR/CodeGen/CMakeLists.txt b/clang/lib/CIR/CodeGen/CMakeLists.txt
index 5602efae1ba41..dbb6d9e7b3807 100644
--- a/clang/lib/CIR/CodeGen/CMakeLists.txt
+++ b/clang/lib/CIR/CodeGen/CMakeLists.txt
@@ -8,6 +8,8 @@ get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS)
 
 add_clang_library(clangCIR
   CIRGenerator.cpp
+  CIRGenDecl.cpp
+  CIRGenExpr.cpp
   CIRGenExprScalar.cpp
   CIRGenFunction.cpp
   CIRGenModule.cpp

diff  --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index 3f1be930d71e5..aa21edcb5e99d 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -117,6 +117,24 @@ static void printOmittedTerminatorRegion(mlir::OpAsmPrinter &printer,
                       /*printBlockTerminators=*/!omitRegionTerm(region));
 }
 
+//===----------------------------------------------------------------------===//
+// AllocaOp
+//===----------------------------------------------------------------------===//
+
+void cir::AllocaOp::build(mlir::OpBuilder &odsBuilder,
+                          mlir::OperationState &odsState, mlir::Type addr,
+                          mlir::Type allocaType, llvm::StringRef name,
+                          mlir::IntegerAttr alignment) {
+  odsState.addAttribute(getAllocaTypeAttrName(odsState.name),
+                        mlir::TypeAttr::get(allocaType));
+  odsState.addAttribute(getNameAttrName(odsState.name),
+                        odsBuilder.getStringAttr(name));
+  if (alignment) {
+    odsState.addAttribute(getAlignmentAttrName(odsState.name), alignment);
+  }
+  odsState.addTypes(addr);
+}
+
 //===----------------------------------------------------------------------===//
 // ConstantOp
 //===----------------------------------------------------------------------===//

diff  --git a/clang/lib/CIR/Dialect/IR/CIRMemorySlot.cpp b/clang/lib/CIR/Dialect/IR/CIRMemorySlot.cpp
new file mode 100644
index 0000000000000..af6b5e4fbd9f6
--- /dev/null
+++ b/clang/lib/CIR/Dialect/IR/CIRMemorySlot.cpp
@@ -0,0 +1,77 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements MemorySlot-related interfaces for CIR dialect
+// operations.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/CIR/Dialect/IR/CIRDialect.h"
+
+using namespace mlir;
+
+//===----------------------------------------------------------------------===//
+// Interfaces for AllocaOp
+//===----------------------------------------------------------------------===//
+
+llvm::SmallVector<MemorySlot> cir::AllocaOp::getPromotableSlots() {
+  return {MemorySlot{getResult(), getAllocaType()}};
+}
+
+Value cir::AllocaOp::getDefaultValue(const MemorySlot &slot,
+                                     OpBuilder &builder) {
+  return builder.create<cir::ConstantOp>(
+      getLoc(), slot.elemType, builder.getAttr<cir::UndefAttr>(slot.elemType));
+}
+
+void cir::AllocaOp::handleBlockArgument(const MemorySlot &slot,
+                                        BlockArgument argument,
+                                        OpBuilder &builder) {}
+
+std::optional<PromotableAllocationOpInterface>
+cir::AllocaOp::handlePromotionComplete(const MemorySlot &slot,
+                                       Value defaultValue, OpBuilder &builder) {
+  if (defaultValue && defaultValue.use_empty())
+    defaultValue.getDefiningOp()->erase();
+  this->erase();
+  return std::nullopt;
+}
+
+//===----------------------------------------------------------------------===//
+// Interfaces for LoadOp
+//===----------------------------------------------------------------------===//
+
+bool cir::LoadOp::loadsFrom(const MemorySlot &slot) {
+  return getAddr() == slot.ptr;
+}
+
+bool cir::LoadOp::storesTo(const MemorySlot &slot) { return false; }
+
+Value cir::LoadOp::getStored(const MemorySlot &slot, OpBuilder &builder,
+                             Value reachingDef, const DataLayout &dataLayout) {
+  llvm_unreachable("getStored should not be called on LoadOp");
+}
+
+bool cir::LoadOp::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 &&
+         getResult().getType() == slot.elemType;
+}
+
+DeletionKind cir::LoadOp::removeBlockingUses(
+    const MemorySlot &slot, const SmallPtrSetImpl<OpOperand *> &blockingUses,
+    OpBuilder &builder, Value reachingDefinition,
+    const DataLayout &dataLayout) {
+  getResult().replaceAllUsesWith(reachingDefinition);
+  return DeletionKind::Delete;
+}

diff  --git a/clang/lib/CIR/Dialect/IR/CMakeLists.txt b/clang/lib/CIR/Dialect/IR/CMakeLists.txt
index baf8bff185221..925af0d61c984 100644
--- a/clang/lib/CIR/Dialect/IR/CMakeLists.txt
+++ b/clang/lib/CIR/Dialect/IR/CMakeLists.txt
@@ -1,6 +1,7 @@
 add_clang_library(MLIRCIR
   CIRAttrs.cpp
   CIRDialect.cpp
+  CIRMemorySlot.cpp
   CIRTypes.cpp
 
   DEPENDS

diff  --git a/clang/test/CIR/CodeGen/basic.cpp b/clang/test/CIR/CodeGen/basic.cpp
new file mode 100644
index 0000000000000..210afcd541159
--- /dev/null
+++ b/clang/test/CIR/CodeGen/basic.cpp
@@ -0,0 +1,27 @@
+// RUN: not %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o - 2>&1 | FileCheck %s
+
+// This error is caused by the "const int i = 2" line in f2(). When
+// initaliziers are implemented, the checks there should be updated
+// and the "not" should be removed from the run line.
+// CHECK: error: ClangIR code gen Not Yet Implemented: emitAutoVarInit
+
+int f1() {
+  int i;
+  return i;
+}
+
+// CHECK: module
+// CHECK: cir.func @f1() -> !cir.int<s, 32>
+// CHECK:    %[[I_PTR:.*]] = cir.alloca !cir.int<s, 32>, !cir.ptr<!cir.int<s, 32>>, ["i"] {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 f2() {
+  const int i = 2;
+  return i;
+}
+
+// CHECK: cir.func @f2() -> !cir.int<s, 32>
+// 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>


        


More information about the cfe-commits mailing list