[clang] [CIR] Upstream basic alloca and load support (PR #128792)

Andy Kaylor via cfe-commits cfe-commits at lists.llvm.org
Tue Feb 25 16:13:43 PST 2025


https://github.com/andykaylor created https://github.com/llvm/llvm-project/pull/128792

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

>From 235ef9e23a314f8946b48143294da367b80f7d14 Mon Sep 17 00:00:00 2001
From: Andy Kaylor <akaylor at nvidia.com>
Date: Tue, 25 Feb 2025 12:31:08 -0800
Subject: [PATCH] [CIR] Upstream basic alloca and load support

This change implements basic support in ClangIR for local
variables using the cir.alloca and cir.load operations.
---
 .../CIR/Dialect/Builder/CIRBaseBuilder.h      |  44 ++++++
 .../include/clang/CIR/Dialect/IR/CIRAttrs.td  |  15 ++
 clang/include/clang/CIR/Dialect/IR/CIROps.td  | 143 ++++++++++++++++++
 clang/include/clang/CIR/MissingFeatures.h     |  18 +++
 clang/lib/CIR/CodeGen/Address.h               |  76 ++++++++++
 clang/lib/CIR/CodeGen/CIRGenDecl.cpp          |  82 ++++++++++
 clang/lib/CIR/CodeGen/CIRGenExpr.cpp          | 128 ++++++++++++++++
 clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp    |  66 ++++++++
 clang/lib/CIR/CodeGen/CIRGenFunction.cpp      |  17 +++
 clang/lib/CIR/CodeGen/CIRGenFunction.h        |  62 +++++++-
 clang/lib/CIR/CodeGen/CIRGenModule.h          |   5 +
 clang/lib/CIR/CodeGen/CIRGenStmt.cpp          |  12 ++
 clang/lib/CIR/CodeGen/CIRGenValue.h           | 125 +++++++++++++++
 clang/lib/CIR/CodeGen/CMakeLists.txt          |   2 +
 clang/lib/CIR/Dialect/IR/CIRDialect.cpp       |  18 +++
 clang/lib/CIR/Dialect/IR/CIRMemorySlot.cpp    |  77 ++++++++++
 clang/lib/CIR/Dialect/IR/CMakeLists.txt       |   1 +
 clang/test/CIR/CodeGen/basic.cpp              |  13 ++
 18 files changed, 903 insertions(+), 1 deletion(-)
 create mode 100644 clang/lib/CIR/CodeGen/Address.h
 create mode 100644 clang/lib/CIR/CodeGen/CIRGenDecl.cpp
 create mode 100644 clang/lib/CIR/CodeGen/CIRGenExpr.cpp
 create mode 100644 clang/lib/CIR/CodeGen/CIRGenValue.h
 create mode 100644 clang/lib/CIR/Dialect/IR/CIRMemorySlot.cpp
 create mode 100644 clang/test/CIR/CodeGen/basic.cpp

diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
index f03241a875845..a62b00cc8ed33 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,49 @@ 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,
+                           mlir::Value dynAllocSize) {
+    return create<cir::AllocaOp>(loc, addrType, type, name, alignment,
+                                 dynAllocSize);
+  }
+
+  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 45d39807b35c8..1ff5e215c10a0 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -115,6 +115,149 @@ 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 `init` attribute indicates that the local variable represented
+    by this alloca was originally initialized in C/C++ source code. In such
+    cases, the first use contains the initialization (a cir.store, a cir.call
+    to a ctor, etc).
+
+    The presence of the `const` attribute indicates that the local variable is
+    declared with C/C++ `const` keyword.
+
+    The `dynAllocSize` specifies the size to dynamically allocate on the stack
+    and ignores the allocation size based on the original type. This is useful
+    when handling VLAs and is omitted when declaring regular local variables.
+
+    The result type is a pointer to the input's type.
+
+    Example:
+
+    ```mlir
+    // int count = 3;
+    %0 = cir.alloca i32, !cir.ptr<i32>, ["count", init] {alignment = 4 : i64}
+
+    // int *ptr;
+    %1 = cir.alloca !cir.ptr<i32>, !cir.ptr<!cir.ptr<i32>>, ["ptr"] {alignment = 8 : i64}
+    ...
+    ```
+  }];
+
+  let arguments = (ins
+    Optional<PrimitiveInt>:$dynAllocSize,
+    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)>,
+
+    OpBuilder<(ins "mlir::Type":$addr,
+                   "mlir::Type":$allocaType,
+                   "llvm::StringRef":$name,
+                   "mlir::IntegerAttr":$alignment,
+                   "mlir::Value":$dynAllocSize),
+    [{
+      if (dynAllocSize)
+        $_state.addOperands(dynAllocSize);
+      build($_builder, $_state, addr, allocaType, name, alignment);
+    }]>
+  ];
+
+  let extraClassDeclaration = [{
+    // Whether the alloca input type is a pointer.
+    bool isPointerType() { return ::mlir::isa<::cir::PointerType>(getAllocaType()); }
+
+    bool isDynamic() { return (bool)getDynAllocSize(); }
+  }];
+
+  let assemblyFormat = [{
+    $allocaType `,` qualified(type($addr)) `,`
+    ($dynAllocSize^ `:` type($dynAllocSize) `,`)?
+    `[` $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. A unit attribute `deref` can be used to
+    mark the resulting value as used by another operation to dereference
+    a pointer. A unit attribute `volatile` can be used to indicate a volatile
+    loading. Load 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
+
+    // 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..97e919b5c2d74 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -36,6 +36,24 @@ struct MissingFeatures {
   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 opAllocaTLS() { return false; }
+  static bool opAllocaOpenMPThreadPrivate() { return false; }
+  static bool opAllocaEscapeByReference() { return false; }
+  static bool opAllocaReference() { return false; }
+
+  // Options for casts
+  static bool scalarConversionOpts() { 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..c68facde9a18c
--- /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 LLVM_CLANG_LIB_CIR_ADDRESS_H
+#define LLVM_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 // LLVM_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..34e0b18594efe
--- /dev/null
+++ b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
@@ -0,0 +1,82 @@
+//===----------------------------------------------------------------------===//
+//
+// 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/Expr.h"
+#include "clang/CIR/MissingFeatures.h"
+
+using namespace clang;
+using namespace clang::CIRGen;
+
+/// 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) {
+  QualType ty = d.getType();
+  assert(ty.getAddressSpace() == LangAS::Default);
+
+  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.
+  mlir::Value addrVal;
+  address = createTempAlloca(allocaTy, alignment, loc, d.getName(),
+                             /*ArraySize=*/nullptr);
+  setAddrOfLocalVar(&d, address);
+  // TODO: emit var init and cleanup
+}
+
+void CIRGenFunction::emitVarDecl(const VarDecl &d) {
+  if (d.hasExternalStorage()) {
+    // Don't emit it now, allow it to be emitted lazily on its first use.
+    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..8bb670d0d5bae
--- /dev/null
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -0,0 +1,128 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "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();
+
+  auto ptr = addr.getPointer();
+  if (mlir::isa<cir::VoidType>(eltTy))
+    cgm.errorNYI(loc, "emitLoadOfScalar: void type");
+
+  auto 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");
+}
+
+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");
+}
+
+mlir::Value CIRGenFunction::emitAlloca(StringRef name, mlir::Type ty,
+                                       mlir::Location loc, CharUnits alignment,
+                                       mlir::Value arraySize) {
+  mlir::Block *entryBlock = getCurFunctionEntryBlock();
+
+  // CIR uses its own alloca AS 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());
+  auto localVarPtrTy = builder.getPointerTo(ty);
+  auto 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, arraySize);
+    assert(!cir::MissingFeatures::opAllocaVarDeclContext());
+  }
+  return addr;
+}
+
+/// This creates an alloca and inserts it into the entry block if \p ArraySize
+/// is nullptr, otherwise 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 arraySize) {
+  mlir::Value alloca = emitAlloca(name.str(), ty, loc, align, arraySize);
+  return Address(alloca, ty, align);
+}
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
index 24a959108f73b..affb657622f67 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) {
+    // TODO: Handle constant emission
+    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 = cgf.getContext().getCanonicalType(srcType);
+    dstType = cgf.getContext().getCanonicalType(dstType);
+    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,34 @@ 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();
+
+  // Since almost all cast kinds apply to scalars, this switch doesn't have a
+  // default case, so the compiler will warn on a missing case. The cases are
+  // in the same order as in the CastKind enum.
+  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..cf71e317ace46 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -153,6 +153,7 @@ mlir::LogicalResult CIRGenFunction::emitFunctionBody(const clang::Stmt *body) {
     emitCompoundStmtWithoutScope(*block);
   else
     result = emitStmt(body, /*useCurrentScope=*/true);
+
   return result;
 }
 
@@ -217,4 +218,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..0d2cc90428d91 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
+  /// delcs.
+  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,10 @@ class CIRGenFunction : public CIRGenTypeCache {
 
   mlir::MLIRContext &getMLIRContext() { return cgm.getMLIRContext(); }
 
+  mlir::Value emitAlloca(llvm::StringRef name, mlir::Type ty,
+                         mlir::Location loc, clang::CharUnits alignment,
+                         mlir::Value arraySize = nullptr);
+
   /// Use to track source locations across nested visitor traversals.
   /// Always use a `SourceLocRAIIObject` to change currSrcLoc.
   std::optional<mlir::Location> currSrcLoc;
@@ -121,8 +140,46 @@ 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);
+
+  /// 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 +191,11 @@ 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",
+                           mlir::Value arraySize = nullptr);
+};
 } // 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..af218bc70cacc 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,16 @@ 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 auto *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..9c527e3fef2a0
--- /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 LLVM_CLANG_LIB_CIR_CIRGENVALUE_H
+#define LLVM_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 // LLVM_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 bfc74d4373f34..29446d4af97ad 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..1813982f66b5e
--- /dev/null
+++ b/clang/test/CIR/CodeGen/basic.cpp
@@ -0,0 +1,13 @@
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o - | FileCheck %s
+
+int f1();
+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}
+// CIR-NEXT: %[[I:.*]] = cir.load %[[I_PTR]] : !cir.ptr<!cir.int<s, 32>>, !cir.int<s, 32>
+// CIR-NEXT: cir.return %[[I]] : !cir.int<s, 32>



More information about the cfe-commits mailing list