[clang] 8eb9b94 - [CIR] Emit init of local variables (#130164)

via cfe-commits cfe-commits at lists.llvm.org
Fri Mar 7 10:23:10 PST 2025


Author: Andy Kaylor
Date: 2025-03-07T10:23:06-08:00
New Revision: 8eb9b947af267f38ad53cfc67a41c5c3d978aa27

URL: https://github.com/llvm/llvm-project/commit/8eb9b947af267f38ad53cfc67a41c5c3d978aa27
DIFF: https://github.com/llvm/llvm-project/commit/8eb9b947af267f38ad53cfc67a41c5c3d978aa27.diff

LOG: [CIR] Emit init of local variables (#130164)

Local variable initialization was previously being ignored. This change
adds support for initialization of scalar variables with constant values
and introduces the constant emitter framework.

Added: 
    clang/lib/CIR/CodeGen/CIRGenConstantEmitter.h
    clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
    clang/test/CIR/CodeGen/local-vars.cpp
    clang/test/CIR/Lowering/local-vars.cpp

Modified: 
    clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
    clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
    clang/include/clang/CIR/MissingFeatures.h
    clang/lib/CIR/CodeGen/CIRGenDecl.cpp
    clang/lib/CIR/CodeGen/CIRGenExpr.cpp
    clang/lib/CIR/CodeGen/CIRGenFunction.h
    clang/lib/CIR/CodeGen/CIRGenModule.h
    clang/lib/CIR/CodeGen/CMakeLists.txt
    clang/test/CIR/CodeGen/basic.cpp
    clang/test/CIR/Lowering/basic.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
index b65797e40d5f9..017ae0c53a984 100644
--- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
+++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
@@ -26,6 +26,10 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
   CIRBaseBuilderTy(mlir::MLIRContext &mlirContext)
       : mlir::OpBuilder(&mlirContext) {}
 
+  cir::ConstantOp getConstant(mlir::Location loc, mlir::TypedAttr attr) {
+    return create<cir::ConstantOp>(loc, attr.getType(), attr);
+  }
+
   cir::ConstantOp getBool(bool state, mlir::Location loc) {
     return create<cir::ConstantOp>(loc, getBoolTy(), getCIRBoolAttr(state));
   }

diff  --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
index ece04c225e322..7b3741de29075 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
@@ -54,6 +54,20 @@ def CIR_BoolAttr : CIR_Attr<"Bool", "bool", [TypedAttrInterface]> {
   }];
 }
 
+//===----------------------------------------------------------------------===//
+// ZeroAttr
+//===----------------------------------------------------------------------===//
+
+def ZeroAttr : CIR_Attr<"Zero", "zero", [TypedAttrInterface]> {
+  let summary = "Attribute to represent zero initialization";
+  let description = [{
+    The ZeroAttr is used to indicate zero initialization on structs.
+  }];
+
+  let parameters = (ins AttributeSelfTypeParameter<"">:$type);
+  let assemblyFormat = [{}];
+}
+
 //===----------------------------------------------------------------------===//
 // UndefAttr
 //===----------------------------------------------------------------------===//

diff  --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index 6f845b7689e51..d20cd0560a7c1 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -41,16 +41,17 @@ struct MissingFeatures {
   static bool supportComdat() { return false; }
 
   // Load/store attributes
-  static bool opLoadThreadLocal() { return false; }
+  static bool opLoadStoreThreadLocal() { return false; }
   static bool opLoadEmitScalarRangeCheck() { return false; }
   static bool opLoadBooleanRepresentation() { return false; }
   static bool opLoadStoreTbaa() { return false; }
   static bool opLoadStoreMemOrder() { return false; }
   static bool opLoadStoreVolatile() { return false; }
   static bool opLoadStoreAlignment() { return false; }
+  static bool opLoadStoreAtomic() { return false; }
+  static bool opLoadStoreObjC() { return false; }
 
   // AllocaOp handling
-  static bool opAllocaVarDeclContext() { return false; }
   static bool opAllocaStaticLocal() { return false; }
   static bool opAllocaNonGC() { return false; }
   static bool opAllocaImpreciseLifetime() { return false; }
@@ -61,6 +62,7 @@ struct MissingFeatures {
   static bool opAllocaReference() { return false; }
   static bool opAllocaAnnotations() { return false; }
   static bool opAllocaDynAllocSize() { return false; }
+  static bool opAllocaCaptureByInit() { return false; }
 
   // FuncOp handling
   static bool opFuncOpenCLKernelMetadata() { return false; }
@@ -76,6 +78,10 @@ struct MissingFeatures {
   static bool constructABIArgDirectExtend() { return false; }
   static bool opGlobalViewAttr() { return false; }
   static bool lowerModeOptLevel() { return false; }
+  static bool opTBAA() { return false; }
+  static bool objCLifetime() { return false; }
+  static bool emitNullabilityCheck() { return false; }
+  static bool astVarDeclInterface() { return false; }
 };
 
 } // namespace cir

diff  --git a/clang/lib/CIR/CodeGen/CIRGenConstantEmitter.h b/clang/lib/CIR/CodeGen/CIRGenConstantEmitter.h
new file mode 100644
index 0000000000000..5b22a8e59908d
--- /dev/null
+++ b/clang/lib/CIR/CodeGen/CIRGenConstantEmitter.h
@@ -0,0 +1,100 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// A helper class for emitting expressions and values as cir::ConstantOp
+// and as initializers for global variables.
+//
+// Note: this is based on clang's LLVM IR codegen in ConstantEmitter.h, reusing
+// this class interface makes it easier move forward with bringing CIR codegen
+// to completion.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef CLANG_LIB_CIR_CODEGEN_CIRGENCONSTANTEMITTER_H
+#define CLANG_LIB_CIR_CODEGEN_CIRGENCONSTANTEMITTER_H
+
+#include "CIRGenFunction.h"
+#include "CIRGenModule.h"
+#include "llvm/ADT/SmallVector.h"
+
+namespace clang::CIRGen {
+
+class ConstantEmitter {
+public:
+  CIRGenModule &cgm;
+  const CIRGenFunction *cgf;
+
+private:
+  bool abstract = false;
+
+  /// Whether we're in a constant context.
+  bool inConstantContext = false;
+
+public:
+  /// Initialize this emission in the context of the given function.
+  /// Use this if the expression might contain contextual references like
+  /// block addresses or PredefinedExprs.
+  ConstantEmitter(CIRGenFunction &cgf) : cgm(cgf.cgm), cgf(&cgf) {}
+
+  ConstantEmitter(const ConstantEmitter &other) = delete;
+  ConstantEmitter &operator=(const ConstantEmitter &other) = delete;
+
+  // All of the "abstract" emission methods below permit the emission to
+  // be immediately discarded without finalizing anything.  Therefore, they
+  // must also promise not to do anything that will, in the future, require
+  // finalization:
+  //
+  //   - using the CGF (if present) for anything other than establishing
+  //     semantic context; for example, an expression with ignored
+  //     side-effects must not be emitted as an abstract expression
+  //
+  //   - doing anything that would not be safe to duplicate within an
+  //     initializer or to propagate to another context; for example,
+  //     side effects, or emitting an initialization that requires a
+  //     reference to its current location.
+  mlir::Attribute emitForMemory(mlir::Attribute c, QualType t);
+
+  /// Emit the result of the given expression as an abstract constant,
+  /// asserting that it succeeded.  This is only safe to do when the
+  /// expression is known to be a constant expression with either a fairly
+  /// simple type or a known simple form.
+  mlir::Attribute emitAbstract(SourceLocation loc, const APValue &value,
+                               QualType t);
+
+  mlir::Attribute tryEmitConstantExpr(const ConstantExpr *CE);
+
+  // These are private helper routines of the constant emitter that
+  // can't actually be private because things are split out into helper
+  // functions and classes.
+
+  mlir::Attribute tryEmitPrivateForVarInit(const VarDecl &d);
+
+  mlir::Attribute tryEmitPrivate(const APValue &value, QualType destType);
+  mlir::Attribute tryEmitPrivateForMemory(const APValue &value, QualType t);
+
+  /// Try to emit the initializer of the given declaration as an abstract
+  /// constant.
+  mlir::Attribute tryEmitAbstractForInitializer(const VarDecl &d);
+
+private:
+  class AbstractStateRAII {
+    ConstantEmitter &emitter;
+    bool oldValue;
+
+  public:
+    AbstractStateRAII(ConstantEmitter &emitter, bool value)
+        : emitter(emitter), oldValue(emitter.abstract) {
+      emitter.abstract = value;
+    }
+    ~AbstractStateRAII() { emitter.abstract = oldValue; }
+  };
+};
+
+} // namespace clang::CIRGen
+
+#endif // CLANG_LIB_CIR_CODEGEN_CIRGENCONSTANTEMITTER_H

diff  --git a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
index 406026b0b9f27..27ed0113a4f55 100644
--- a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
@@ -10,23 +10,29 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "CIRGenConstantEmitter.h"
 #include "CIRGenFunction.h"
+#include "mlir/IR/Location.h"
 #include "clang/AST/Attr.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/Expr.h"
+#include "clang/AST/ExprCXX.h"
 #include "clang/CIR/MissingFeatures.h"
 
 using namespace clang;
 using namespace clang::CIRGen;
 
-void CIRGenFunction::emitAutoVarAlloca(const VarDecl &d) {
+CIRGenFunction::AutoVarEmission
+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());
+  mlir::Location loc = getLoc(d.getSourceRange());
 
-  if (d.isEscapingByref())
+  CIRGenFunction::AutoVarEmission emission(d);
+  emission.IsEscapingByRef = d.isEscapingByref();
+  if (emission.IsEscapingByRef)
     cgm.errorNYI(d.getSourceRange(),
                  "emitAutoVarDecl: decl escaping by reference");
 
@@ -46,21 +52,130 @@ void CIRGenFunction::emitAutoVarAlloca(const VarDecl &d) {
   address = createTempAlloca(allocaTy, alignment, loc, d.getName());
   declare(address.getPointer(), &d, ty, getLoc(d.getSourceRange()), alignment);
 
+  emission.Addr = address;
   setAddrOfLocalVar(&d, address);
+
+  return emission;
+}
+
+/// Determine whether the given initializer is trivial in the sense
+/// that it requires no code to be generated.
+bool CIRGenFunction::isTrivialInitializer(const Expr *init) {
+  if (!init)
+    return true;
+
+  if (const CXXConstructExpr *construct = dyn_cast<CXXConstructExpr>(init))
+    if (CXXConstructorDecl *constructor = construct->getConstructor())
+      if (constructor->isTrivial() && constructor->isDefaultConstructor() &&
+          !construct->requiresZeroInitialization())
+        return true;
+
+  return false;
 }
 
-void CIRGenFunction::emitAutoVarInit(const clang::VarDecl &d) {
+void CIRGenFunction::emitAutoVarInit(
+    const CIRGenFunction::AutoVarEmission &emission) {
+  assert(emission.Variable && "emission was not valid!");
+
+  // If this was emitted as a global constant, we're done.
+  if (emission.wasEmittedAsGlobal())
+    return;
+
+  const VarDecl &d = *emission.Variable;
+
   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");
+  if (!type.isPODType(getContext())) {
+    cgm.errorNYI(d.getSourceRange(), "emitAutoVarInit: non-POD type");
+    return;
+  }
+
+  const Address addr = emission.Addr;
+
+  // Check whether this is a byref variable that's potentially
+  // captured and moved by its own initializer.  If so, we'll need to
+  // emit the initializer first, then copy into the variable.
+  assert(!cir::MissingFeatures::opAllocaCaptureByInit());
+
+  // Note: constexpr already initializes everything correctly.
+  LangOptions::TrivialAutoVarInitKind trivialAutoVarInit =
+      (d.isConstexpr()
+           ? LangOptions::TrivialAutoVarInitKind::Uninitialized
+           : (d.getAttr<UninitializedAttr>()
+                  ? LangOptions::TrivialAutoVarInitKind::Uninitialized
+                  : getContext().getLangOpts().getTrivialAutoVarInit()));
+
+  auto initializeWhatIsTechnicallyUninitialized = [&](Address addr) {
+    if (trivialAutoVarInit ==
+        LangOptions::TrivialAutoVarInitKind::Uninitialized)
+      return;
+
+    cgm.errorNYI(d.getSourceRange(), "emitAutoVarInit: trivial initialization");
+  };
+
+  if (isTrivialInitializer(init)) {
+    initializeWhatIsTechnicallyUninitialized(addr);
+    return;
+  }
+
+  mlir::Attribute constant;
+  if (emission.IsConstantAggregate ||
+      d.mightBeUsableInConstantExpressions(getContext())) {
+    // FIXME: Differently from LLVM we try not to emit / lower too much
+    // here for CIR since we are interested in seeing the ctor in some
+    // analysis later on. So CIR's implementation of ConstantEmitter will
+    // frequently return an empty Attribute, to signal we want to codegen
+    // some trivial ctor calls and whatnots.
+    constant = ConstantEmitter(*this).tryEmitAbstractForInitializer(d);
+    if (constant && !mlir::isa<cir::ZeroAttr>(constant) &&
+        (trivialAutoVarInit !=
+         LangOptions::TrivialAutoVarInitKind::Uninitialized)) {
+      cgm.errorNYI(d.getSourceRange(), "emitAutoVarInit: constant aggregate");
+      return;
+    }
+  }
+
+  // NOTE(cir): In case we have a constant initializer, we can just emit a
+  // store. But, in CIR, we wish to retain any ctor calls, so if it is a
+  // CXX temporary object creation, we ensure the ctor call is used deferring
+  // its removal/optimization to the CIR lowering.
+  if (!constant || isa<CXXTemporaryObjectExpr>(init)) {
+    initializeWhatIsTechnicallyUninitialized(addr);
+    LValue lv = LValue::makeAddr(addr, type);
+    emitExprAsInit(init, &d, lv);
+    // In case lv has uses it means we indeed initialized something
+    // out of it while trying to build the expression, mark it as such.
+    mlir::Value val = lv.getAddress().getPointer();
+    assert(val && "Should have an address");
+    auto allocaOp = dyn_cast_or_null<cir::AllocaOp>(val.getDefiningOp());
+    assert(allocaOp && "Address should come straight out of the alloca");
+
+    if (!allocaOp.use_empty())
+      allocaOp.setInitAttr(mlir::UnitAttr::get(&getMLIRContext()));
+    return;
+  }
+
+  // FIXME(cir): migrate most of this file to use mlir::TypedAttr directly.
+  auto typedConstant = mlir::dyn_cast<mlir::TypedAttr>(constant);
+  assert(typedConstant && "expected typed attribute");
+  if (!emission.IsConstantAggregate) {
+    // For simple scalar/complex initialization, store the value directly.
+    LValue lv = LValue::makeAddr(addr, type);
+    assert(init && "expected initializer");
+    mlir::Location initLoc = getLoc(init->getSourceRange());
+    // lv.setNonGC(true);
+    return emitStoreThroughLValue(
+        RValue::get(builder.getConstant(initLoc, typedConstant)), lv);
   }
 }
 
-void CIRGenFunction::emitAutoVarCleanups(const clang::VarDecl &d) {
+void CIRGenFunction::emitAutoVarCleanups(
+    const CIRGenFunction::AutoVarEmission &emission) {
+  const VarDecl &d = *emission.Variable;
+
   // Check the type for a cleanup.
   if (d.needsDestruction(getContext()))
     cgm.errorNYI(d.getSourceRange(), "emitAutoVarCleanups: type cleanup");
@@ -76,9 +191,9 @@ void CIRGenFunction::emitAutoVarCleanups(const clang::VarDecl &d) {
 /// 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);
+  CIRGenFunction::AutoVarEmission emission = emitAutoVarAlloca(d);
+  emitAutoVarInit(emission);
+  emitAutoVarCleanups(emission);
 }
 
 void CIRGenFunction::emitVarDecl(const VarDecl &d) {
@@ -94,10 +209,54 @@ void CIRGenFunction::emitVarDecl(const VarDecl &d) {
 
   assert(d.hasLocalStorage());
 
-  assert(!cir::MissingFeatures::opAllocaVarDeclContext());
+  CIRGenFunction::VarDeclContext varDeclCtx{*this, &d};
   return emitAutoVarDecl(d);
 }
 
+void CIRGenFunction::emitScalarInit(const Expr *init, mlir::Location loc,
+                                    LValue lvalue, bool capturedByInit) {
+  assert(!cir::MissingFeatures::objCLifetime());
+
+  SourceLocRAIIObject locRAII{*this, loc};
+  mlir::Value value = emitScalarExpr(init);
+  if (capturedByInit) {
+    cgm.errorNYI(init->getSourceRange(), "emitScalarInit: captured by init");
+    return;
+  }
+  assert(!cir::MissingFeatures::emitNullabilityCheck());
+  emitStoreThroughLValue(RValue::get(value), lvalue, true);
+  return;
+}
+
+void CIRGenFunction::emitExprAsInit(const Expr *init, const ValueDecl *d,
+                                    LValue lvalue, bool capturedByInit) {
+  SourceLocRAIIObject loc{*this, getLoc(init->getSourceRange())};
+  if (capturedByInit) {
+    cgm.errorNYI(init->getSourceRange(), "emitExprAsInit: captured by init");
+    return;
+  }
+
+  QualType type = d->getType();
+
+  if (type->isReferenceType()) {
+    cgm.errorNYI(init->getSourceRange(), "emitExprAsInit: reference type");
+    return;
+  }
+  switch (CIRGenFunction::getEvaluationKind(type)) {
+  case cir::TEK_Scalar:
+    emitScalarInit(init, getLoc(d->getSourceRange()), lvalue);
+    return;
+  case cir::TEK_Complex: {
+    cgm.errorNYI(init->getSourceRange(), "emitExprAsInit: complex type");
+    return;
+  }
+  case cir::TEK_Aggregate:
+    cgm.errorNYI(init->getSourceRange(), "emitExprAsInit: aggregate type");
+    return;
+  }
+  llvm_unreachable("bad evaluation kind");
+}
+
 void CIRGenFunction::emitDecl(const Decl &d) {
   switch (d.getKind()) {
   case Decl::Var: {

diff  --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
index ccc3e20875263..07fb4cf8f1513 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp
@@ -25,9 +25,77 @@ using namespace clang;
 using namespace clang::CIRGen;
 using namespace cir;
 
+void CIRGenFunction::emitStoreThroughLValue(RValue src, LValue dst,
+                                            bool isInit) {
+  if (!dst.isSimple()) {
+    cgm.errorNYI(dst.getPointer().getLoc(),
+                 "emitStoreThroughLValue: non-simple lvalue");
+    return;
+  }
+
+  assert(!cir::MissingFeatures::opLoadStoreObjC());
+
+  assert(src.isScalar() && "Can't emit an aggregate store with this method");
+  emitStoreOfScalar(src.getScalarVal(), dst, isInit);
+}
+
+void CIRGenFunction::emitStoreOfScalar(mlir::Value value, Address addr,
+                                       bool isVolatile, QualType ty,
+                                       bool isInit, bool isNontemporal) {
+  assert(!cir::MissingFeatures::opLoadStoreThreadLocal());
+
+  if (ty->getAs<clang::VectorType>()) {
+    cgm.errorNYI(addr.getPointer().getLoc(), "emitStoreOfScalar vector type");
+    return;
+  }
+
+  value = emitToMemory(value, ty);
+
+  assert(!cir::MissingFeatures::opLoadStoreAtomic());
+
+  // Update the alloca with more info on initialization.
+  assert(addr.getPointer() && "expected pointer to exist");
+  auto srcAlloca =
+      dyn_cast_or_null<cir::AllocaOp>(addr.getPointer().getDefiningOp());
+  if (currVarDecl && srcAlloca) {
+    const VarDecl *vd = currVarDecl;
+    assert(vd && "VarDecl expected");
+    if (vd->hasInit())
+      srcAlloca.setInitAttr(mlir::UnitAttr::get(&getMLIRContext()));
+  }
+
+  assert(currSrcLoc && "must pass in source location");
+  builder.createStore(*currSrcLoc, value, addr.getPointer() /*, isVolatile*/);
+
+  if (isNontemporal) {
+    cgm.errorNYI(addr.getPointer().getLoc(), "emitStoreOfScalar nontemporal");
+    return;
+  }
+
+  assert(!cir::MissingFeatures::opTBAA());
+}
+
+mlir::Value CIRGenFunction::emitToMemory(mlir::Value value, QualType ty) {
+  // Bool has a 
diff erent representation in memory than in registers,
+  // but in ClangIR, it is simply represented as a cir.bool value.
+  // This function is here as a placeholder for possible future changes.
+  return value;
+}
+
+void CIRGenFunction::emitStoreOfScalar(mlir::Value value, LValue lvalue,
+                                       bool isInit) {
+  if (lvalue.getType()->isConstantMatrixType()) {
+    assert(0 && "NYI: emitStoreOfScalar constant matrix type");
+    return;
+  }
+
+  emitStoreOfScalar(value, lvalue.getAddress(), lvalue.isVolatile(),
+                    lvalue.getType(), isInit, /*isNontemporal=*/false);
+}
+
 mlir::Value CIRGenFunction::emitLoadOfScalar(LValue lvalue,
                                              SourceLocation loc) {
-  assert(!cir::MissingFeatures::opLoadThreadLocal());
+  assert(!cir::MissingFeatures::opLoadStoreThreadLocal());
   assert(!cir::MissingFeatures::opLoadEmitScalarRangeCheck());
   assert(!cir::MissingFeatures::opLoadBooleanRepresentation());
 
@@ -115,7 +183,7 @@ mlir::Value CIRGenFunction::emitAlloca(StringRef name, mlir::Type ty,
     builder.restoreInsertionPoint(builder.getBestAllocaInsertPoint(entryBlock));
     addr = builder.createAlloca(loc, /*addr type*/ localVarPtrTy,
                                 /*var type*/ ty, name, alignIntAttr);
-    assert(!cir::MissingFeatures::opAllocaVarDeclContext());
+    assert(!cir::MissingFeatures::astVarDeclInterface());
   }
   return addr;
 }

diff  --git a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
new file mode 100644
index 0000000000000..1ea7f6212766c
--- /dev/null
+++ b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
@@ -0,0 +1,309 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 Constant Expr nodes as LLVM code.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Address.h"
+#include "CIRGenConstantEmitter.h"
+#include "CIRGenFunction.h"
+#include "CIRGenModule.h"
+#include "mlir/IR/Attributes.h"
+#include "mlir/IR/BuiltinAttributeInterfaces.h"
+#include "mlir/IR/BuiltinAttributes.h"
+#include "clang/AST/APValue.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Attr.h"
+#include "clang/AST/OperationKinds.h"
+#include "clang/AST/RecordLayout.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/Basic/Builtins.h"
+#include "clang/Basic/Specifiers.h"
+#include "clang/CIR/Dialect/IR/CIRAttrs.h"
+#include "clang/CIR/Dialect/IR/CIRTypes.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/Sequence.h"
+#include "llvm/Support/ErrorHandling.h"
+
+using namespace clang;
+using namespace clang::CIRGen;
+
+//===----------------------------------------------------------------------===//
+//                             ConstExprEmitter
+//===----------------------------------------------------------------------===//
+
+// This class only needs to handle arrays, structs and unions.
+//
+// In LLVM codegen, when outside C++11 mode, those types are not constant
+// folded, while all other types are handled by constant folding.
+//
+// In CIR codegen, instead of folding things here, we should defer that work
+// to MLIR: do not attempt to do much here.
+class ConstExprEmitter
+    : public StmtVisitor<ConstExprEmitter, mlir::Attribute, QualType> {
+  CIRGenModule &cgm;
+  LLVM_ATTRIBUTE_UNUSED ConstantEmitter &emitter;
+
+public:
+  ConstExprEmitter(ConstantEmitter &emitter)
+      : cgm(emitter.cgm), emitter(emitter) {}
+
+  //===--------------------------------------------------------------------===//
+  //                            Visitor Methods
+  //===--------------------------------------------------------------------===//
+
+  mlir::Attribute VisitStmt(Stmt *S, QualType T) { return {}; }
+
+  mlir::Attribute VisitConstantExpr(ConstantExpr *ce, QualType t) {
+    if (mlir::Attribute result = emitter.tryEmitConstantExpr(ce))
+      return result;
+    return Visit(ce->getSubExpr(), t);
+  }
+
+  mlir::Attribute VisitParenExpr(ParenExpr *pe, QualType t) {
+    return Visit(pe->getSubExpr(), t);
+  }
+
+  mlir::Attribute
+  VisitSubstNonTypeTemplateParmExpr(SubstNonTypeTemplateParmExpr *pe,
+                                    QualType t) {
+    return Visit(pe->getReplacement(), t);
+  }
+
+  mlir::Attribute VisitGenericSelectionExpr(GenericSelectionExpr *ge,
+                                            QualType t) {
+    return Visit(ge->getResultExpr(), t);
+  }
+
+  mlir::Attribute VisitChooseExpr(ChooseExpr *ce, QualType t) {
+    return Visit(ce->getChosenSubExpr(), t);
+  }
+
+  mlir::Attribute VisitCompoundLiteralExpr(CompoundLiteralExpr *e, QualType t) {
+    return Visit(e->getInitializer(), t);
+  }
+
+  mlir::Attribute VisitCastExpr(CastExpr *e, QualType destType) {
+    cgm.errorNYI(e->getBeginLoc(), "ConstExprEmitter::VisitCastExpr");
+    return {};
+  }
+
+  mlir::Attribute VisitCXXDefaultInitExpr(CXXDefaultInitExpr *die, QualType t) {
+    cgm.errorNYI(die->getBeginLoc(),
+                 "ConstExprEmitter::VisitCXXDefaultInitExpr");
+    return {};
+  }
+
+  mlir::Attribute VisitExprWithCleanups(ExprWithCleanups *e, QualType t) {
+    // Since this about constant emission no need to wrap this under a scope.
+    return Visit(e->getSubExpr(), t);
+  }
+
+  mlir::Attribute VisitMaterializeTemporaryExpr(MaterializeTemporaryExpr *e,
+                                                QualType t) {
+    return Visit(e->getSubExpr(), t);
+  }
+
+  mlir::Attribute VisitImplicitValueInitExpr(ImplicitValueInitExpr *E,
+                                             QualType T) {
+    cgm.errorNYI(E->getBeginLoc(),
+                 "ConstExprEmitter::VisitImplicitValueInitExpr");
+    return {};
+  }
+
+  mlir::Attribute VisitInitListExpr(InitListExpr *ile, QualType t) {
+    cgm.errorNYI(ile->getBeginLoc(), "ConstExprEmitter::VisitInitListExpr");
+    return {};
+  }
+
+  mlir::Attribute VisitDesignatedInitUpdateExpr(DesignatedInitUpdateExpr *e,
+                                                QualType destType) {
+    mlir::Attribute c = Visit(e->getBase(), destType);
+    if (!c)
+      return {};
+
+    cgm.errorNYI(e->getBeginLoc(),
+                 "ConstExprEmitter::VisitDesignatedInitUpdateExpr");
+    return {};
+  }
+
+  mlir::Attribute VisitCXXConstructExpr(CXXConstructExpr *e, QualType ty) {
+    cgm.errorNYI(e->getBeginLoc(), "ConstExprEmitter::VisitCXXConstructExpr");
+    return {};
+  }
+
+  mlir::Attribute VisitStringLiteral(StringLiteral *e, QualType t) {
+    cgm.errorNYI(e->getBeginLoc(), "ConstExprEmitter::VisitStringLiteral");
+    return {};
+  }
+
+  mlir::Attribute VisitObjCEncodeExpr(ObjCEncodeExpr *e, QualType t) {
+    cgm.errorNYI(e->getBeginLoc(), "ConstExprEmitter::VisitObjCEncodeExpr");
+    return {};
+  }
+
+  mlir::Attribute VisitUnaryExtension(const UnaryOperator *e, QualType t) {
+    return Visit(e->getSubExpr(), t);
+  }
+
+  // Utility methods
+  mlir::Type convertType(QualType t) { return cgm.convertType(t); }
+};
+
+// TODO(cir): this can be shared with LLVM's codegen
+static QualType getNonMemoryType(CIRGenModule &cgm, QualType type) {
+  if (auto at = type->getAs<AtomicType>()) {
+    return cgm.getASTContext().getQualifiedType(at->getValueType(),
+                                                type.getQualifiers());
+  }
+  return type;
+}
+
+//===----------------------------------------------------------------------===//
+//                             ConstantEmitter
+//===----------------------------------------------------------------------===//
+
+mlir::Attribute
+ConstantEmitter::tryEmitAbstractForInitializer(const VarDecl &d) {
+  AbstractStateRAII state(*this, true);
+  return tryEmitPrivateForVarInit(d);
+}
+
+mlir::Attribute ConstantEmitter::tryEmitPrivateForVarInit(const VarDecl &d) {
+  // Make a quick check if variable can be default NULL initialized
+  // and avoid going through rest of code which may do, for c++11,
+  // initialization of memory to all NULLs.
+  if (!d.hasLocalStorage()) {
+    QualType ty = cgm.getASTContext().getBaseElementType(d.getType());
+    if (ty->isRecordType())
+      if (d.getInit() && isa<CXXConstructExpr>(d.getInit())) {
+        cgm.errorNYI(d.getInit()->getBeginLoc(),
+                     "tryEmitPrivateForVarInit CXXConstructExpr");
+        return {};
+      }
+  }
+  inConstantContext = d.hasConstantInitialization();
+
+  const Expr *e = d.getInit();
+  assert(e && "No initializer to emit");
+
+  QualType destType = d.getType();
+
+  if (!destType->isReferenceType()) {
+    QualType nonMemoryDestType = getNonMemoryType(cgm, destType);
+    if (mlir::Attribute c = ConstExprEmitter(*this).Visit(const_cast<Expr *>(e),
+                                                          nonMemoryDestType))
+      return emitForMemory(c, destType);
+  }
+
+  // Try to emit the initializer.  Note that this can allow some things that
+  // are not allowed by tryEmitPrivateForMemory alone.
+  if (APValue *value = d.evaluateValue())
+    return tryEmitPrivateForMemory(*value, destType);
+
+  return {};
+}
+
+mlir::Attribute ConstantEmitter::tryEmitConstantExpr(const ConstantExpr *ce) {
+  if (!ce->hasAPValueResult())
+    return {};
+
+  QualType retType = ce->getType();
+  if (ce->isGLValue())
+    retType = cgm.getASTContext().getLValueReferenceType(retType);
+
+  return emitAbstract(ce->getBeginLoc(), ce->getAPValueResult(), retType);
+}
+
+mlir::Attribute ConstantEmitter::tryEmitPrivateForMemory(const APValue &value,
+                                                         QualType destType) {
+  QualType nonMemoryDestType = getNonMemoryType(cgm, destType);
+  mlir::Attribute c = tryEmitPrivate(value, nonMemoryDestType);
+  return (c ? emitForMemory(c, destType) : nullptr);
+}
+
+mlir::Attribute ConstantEmitter::emitAbstract(SourceLocation loc,
+                                              const APValue &value,
+                                              QualType destType) {
+  AbstractStateRAII state(*this, true);
+  mlir::Attribute c = tryEmitPrivate(value, destType);
+  if (!c)
+    cgm.errorNYI(loc, "emitAbstract failed, emit null constaant");
+  return c;
+}
+
+mlir::Attribute ConstantEmitter::emitForMemory(mlir::Attribute c,
+                                               QualType destType) {
+  // For an _Atomic-qualified constant, we may need to add tail padding.
+  if (destType->getAs<AtomicType>()) {
+    cgm.errorNYI("emitForMemory: atomic type");
+    return {};
+  }
+
+  return c;
+}
+
+mlir::Attribute ConstantEmitter::tryEmitPrivate(const APValue &value,
+                                                QualType destType) {
+  auto &builder = cgm.getBuilder();
+  switch (value.getKind()) {
+  case APValue::None:
+  case APValue::Indeterminate:
+    cgm.errorNYI("ConstExprEmitter::tryEmitPrivate none or indeterminate");
+    return {};
+  case APValue::Int: {
+    mlir::Type ty = cgm.convertType(destType);
+    if (mlir::isa<cir::BoolType>(ty))
+      return builder.getCIRBoolAttr(value.getInt().getZExtValue());
+    assert(mlir::isa<cir::IntType>(ty) && "expected integral type");
+    return cgm.getBuilder().getAttr<cir::IntAttr>(ty, value.getInt());
+  }
+  case APValue::Float: {
+    const llvm::APFloat &init = value.getFloat();
+    if (&init.getSemantics() == &llvm::APFloat::IEEEhalf() &&
+        !cgm.getASTContext().getLangOpts().NativeHalfType &&
+        cgm.getASTContext().getTargetInfo().useFP16ConversionIntrinsics()) {
+      cgm.errorNYI("ConstExprEmitter::tryEmitPrivate half");
+      return {};
+    } else {
+      mlir::Type ty = cgm.convertType(destType);
+      assert(mlir::isa<cir::CIRFPTypeInterface>(ty) &&
+             "expected floating-point type");
+      return cgm.getBuilder().getAttr<cir::FPAttr>(ty, init);
+    }
+  }
+  case APValue::Array: {
+    cgm.errorNYI("ConstExprEmitter::tryEmitPrivate array");
+    return {};
+  }
+  case APValue::Vector: {
+    cgm.errorNYI("ConstExprEmitter::tryEmitPrivate vector");
+    return {};
+  }
+  case APValue::MemberPointer: {
+    cgm.errorNYI("ConstExprEmitter::tryEmitPrivate member pointer");
+    return {};
+  }
+  case APValue::LValue:
+    cgm.errorNYI("ConstExprEmitter::tryEmitPrivate lvalue");
+    return {};
+  case APValue::Struct:
+  case APValue::Union:
+    cgm.errorNYI("ConstExprEmitter::tryEmitPrivate struct or union");
+    return {};
+  case APValue::FixedPoint:
+  case APValue::ComplexInt:
+  case APValue::ComplexFloat:
+  case APValue::AddrLabelDiff:
+    cgm.errorNYI("ConstExprEmitter::tryEmitPrivate fixed point, complex int, "
+                 "complex float, addr label 
diff ");
+    return {};
+  }
+  llvm_unreachable("Unknown APValue kind");
+}

diff  --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index cf896d3c0a946..72445f62232a4 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -26,6 +26,7 @@
 #include "clang/AST/Decl.h"
 #include "clang/AST/Type.h"
 #include "clang/CIR/Dialect/IR/CIRDialect.h"
+#include "clang/CIR/MissingFeatures.h"
 #include "clang/CIR/TypeEvaluationKind.h"
 
 #include "llvm/ADT/ScopedHashTable.h"
@@ -105,6 +106,27 @@ class CIRGenFunction : public CIRGenTypeCache {
   mlir::Value emitAlloca(llvm::StringRef name, mlir::Type ty,
                          mlir::Location loc, clang::CharUnits alignment);
 
+private:
+  // Track current variable initialization (if there's one)
+  const clang::VarDecl *currVarDecl = nullptr;
+  class VarDeclContext {
+    CIRGenFunction &p;
+    const clang::VarDecl *oldVal = nullptr;
+
+  public:
+    VarDeclContext(CIRGenFunction &p, const VarDecl *value) : p(p) {
+      if (p.currVarDecl)
+        oldVal = p.currVarDecl;
+      p.currVarDecl = value;
+    }
+
+    /// Can be used to restore the state early, before the dtor
+    /// is run.
+    void restore() { p.currVarDecl = oldVal; }
+    ~VarDeclContext() { restore(); }
+  };
+
+public:
   /// Use to track source locations across nested visitor traversals.
   /// Always use a `SourceLocRAIIObject` to change currSrcLoc.
   std::optional<mlir::Location> currSrcLoc;
@@ -170,16 +192,97 @@ class CIRGenFunction : public CIRGenTypeCache {
 
   void emitDecl(const clang::Decl &d);
 
+  void emitScalarInit(const clang::Expr *init, mlir::Location loc,
+                      LValue lvalue, bool capturedByInit = false);
+
   LValue emitDeclRefLValue(const clang::DeclRefExpr *e);
 
+  /// Determine whether the given initializer is trivial in the sense
+  /// that it requires no code to be generated.
+  bool isTrivialInitializer(const Expr *init);
+
+  /// Emit an expression as an initializer for an object (variable, field, etc.)
+  /// at the given location.  The expression is not necessarily the normal
+  /// initializer for the object, and the address is not necessarily
+  /// its normal location.
+  ///
+  /// \param init the initializing expression
+  /// \param d the object to act as if we're initializing
+  /// \param lvalue the lvalue to initialize
+  /// \param capturedByInit true if \p d is a __block variable whose address is
+  /// potentially changed by the initializer
+  void emitExprAsInit(const clang::Expr *init, const clang::ValueDecl *d,
+                      LValue lvalue, bool capturedByInit = false);
+
   /// 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);
+  struct AutoVarEmission {
+    const clang::VarDecl *Variable;
+    /// The address of the alloca for languages with explicit address space
+    /// (e.g. OpenCL) or alloca casted to generic pointer for address space
+    /// agnostic languages (e.g. C++). Invalid if the variable was emitted
+    /// as a global constant.
+    Address Addr;
+
+    /// True if the variable is of aggregate type and has a constant
+    /// initializer.
+    bool IsConstantAggregate = false;
+
+    /// True if the variable is a __block variable that is captured by an
+    /// escaping block.
+    bool IsEscapingByRef = false;
+
+    mlir::Value NRVOFlag{};
+
+    struct Invalid {};
+    AutoVarEmission(Invalid) : Variable(nullptr), Addr(Address::invalid()) {}
+
+    AutoVarEmission(const clang::VarDecl &variable)
+        : Variable(&variable), Addr(Address::invalid()) {}
+
+    static AutoVarEmission invalid() { return AutoVarEmission(Invalid()); }
+
+    bool wasEmittedAsGlobal() const { return !Addr.isValid(); }
+
+    /// Returns the raw, allocated address, which is not necessarily
+    /// the address of the object itself. It is casted to default
+    /// address space for address space agnostic languages.
+    Address getAllocatedAddress() const { return Addr; }
+
+    /// Returns the address of the object within this declaration.
+    /// Note that this does not chase the forwarding pointer for
+    /// __block decls.
+    Address getObjectAddress(CIRGenFunction &CGF) const {
+      if (!IsEscapingByRef)
+        return Addr;
+
+      assert(!cir::MissingFeatures::opAllocaEscapeByReference());
+      return Address::invalid();
+    }
+  };
+
+  AutoVarEmission emitAutoVarAlloca(const clang::VarDecl &d);
+  void emitAutoVarInit(const AutoVarEmission &emission);
+  void emitAutoVarCleanups(const AutoVarEmission &emission);
+
+  void emitStoreOfScalar(mlir::Value value, Address addr, bool isVolatile,
+                         clang::QualType ty, bool isInit = false,
+                         bool isNontemporal = false);
+  void emitStoreOfScalar(mlir::Value value, LValue lvalue, bool isInit);
+
+  /// Given a value and its clang type, returns the value casted to its memory
+  /// representation.
+  /// Note: CIR defers most of the special casting to the final lowering passes
+  /// to conserve the high level information.
+  mlir::Value emitToMemory(mlir::Value Value, clang::QualType Ty);
+
+  /// Store the specified rvalue into the specified
+  /// lvalue, where both are guaranteed to the have the same type, and that type
+  /// is 'Ty'.
+  void emitStoreThroughLValue(RValue Src, LValue Dst, bool isInit = false);
 
   /// This method handles emission of any variable declaration
   /// inside a function, including static vars etc.

diff  --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h
index 5fb5ef505a8c1..2a798f4cd56a9 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.h
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.h
@@ -147,6 +147,20 @@ class CIRGenModule : public CIRGenTypeCache {
     return diags.Report(loc, diagID) << feature << name;
   }
 
+  DiagnosticBuilder errorNYI(mlir::Location loc, llvm::StringRef feature) {
+    // TODO: Convert the location to a SourceLocation
+    unsigned diagID = diags.getCustomDiagID(
+        DiagnosticsEngine::Error, "ClangIR code gen Not Yet Implemented: %0");
+    return diags.Report(diagID) << feature;
+  }
+
+  DiagnosticBuilder errorNYI(llvm::StringRef feature) {
+    // TODO: Make a default location? currSrcLoc?
+    unsigned diagID = diags.getCustomDiagID(
+        DiagnosticsEngine::Error, "ClangIR code gen Not Yet Implemented: %0");
+    return diags.Report(diagID) << feature;
+  }
+
   DiagnosticBuilder errorNYI(SourceRange, llvm::StringRef);
 
   template <typename T>

diff  --git a/clang/lib/CIR/CodeGen/CMakeLists.txt b/clang/lib/CIR/CodeGen/CMakeLists.txt
index e6d3cbabd853b..8ee65c2763e70 100644
--- a/clang/lib/CIR/CodeGen/CMakeLists.txt
+++ b/clang/lib/CIR/CodeGen/CMakeLists.txt
@@ -10,6 +10,7 @@ add_clang_library(clangCIR
   CIRGenerator.cpp
   CIRGenDecl.cpp
   CIRGenExpr.cpp
+  CIRGenExprConstant.cpp
   CIRGenExprScalar.cpp
   CIRGenFunction.cpp
   CIRGenModule.cpp

diff  --git a/clang/test/CIR/CodeGen/basic.cpp b/clang/test/CIR/CodeGen/basic.cpp
index 6a2faa725a34d..ef922cc2b46fc 100644
--- a/clang/test/CIR/CodeGen/basic.cpp
+++ b/clang/test/CIR/CodeGen/basic.cpp
@@ -1,9 +1,4 @@
-// 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
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o - 2>&1 | FileCheck %s
 
 int f1() {
   int i;
@@ -22,13 +17,15 @@ int f2() {
 }
 
 // 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_PTR:.*]] = cir.alloca !cir.int<s, 32>, !cir.ptr<!cir.int<s, 32>>, ["i", init, const] {alignment = 4 : i64}
+// CHECK:    %[[TWO:.*]] = cir.const #cir.int<2> : !cir.int<s, 32>
+// CHECK:    cir.store %[[TWO]], %[[I_PTR]] : !cir.int<s, 32>, !cir.ptr<!cir.int<s, 32>>
 // 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;
-  }
+  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}

diff  --git a/clang/test/CIR/CodeGen/local-vars.cpp b/clang/test/CIR/CodeGen/local-vars.cpp
new file mode 100644
index 0000000000000..14be8f4da902b
--- /dev/null
+++ b/clang/test/CIR/CodeGen/local-vars.cpp
@@ -0,0 +1,65 @@
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o - 2>&1 | FileCheck %s
+
+void test() {
+  int i = 1;
+  long l = 2l;
+  float f = 3.0f;
+  double d = 4.0;
+  bool b1 = true;
+  bool b2 = false;
+  const int ci = 1;
+  const long cl = 2l;
+  const float cf = 3.0f;
+  const double cd = 4.0;
+  const bool cb1 = true;
+  const bool cb2 = false;
+  int uii;
+  long uil;
+  float uif;
+  double uid;
+  bool uib;
+}
+
+// CHECK: module
+// CHECK: cir.func @test()
+// CHECK:    %[[I_PTR:.*]] = cir.alloca !cir.int<s, 32>, !cir.ptr<!cir.int<s, 32>>, ["i", init] {alignment = 4 : i64}
+// CHECK:    %[[L_PTR:.*]] = cir.alloca !cir.int<s, 64>, !cir.ptr<!cir.int<s, 64>>, ["l", init] {alignment = 8 : i64}
+// CHECK:    %[[F_PTR:.*]] = cir.alloca !cir.float, !cir.ptr<!cir.float>, ["f", init] {alignment = 4 : i64}
+// CHECK:    %[[D_PTR:.*]] = cir.alloca !cir.double, !cir.ptr<!cir.double>, ["d", init] {alignment = 8 : i64}
+// CHECK:    %[[B1_PTR:.*]] = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["b1", init] {alignment = 1 : i64}
+// CHECK:    %[[B2_PTR:.*]] = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["b2", init] {alignment = 1 : i64}
+// CHECK:    %[[CI_PTR:.*]] = cir.alloca !cir.int<s, 32>, !cir.ptr<!cir.int<s, 32>>, ["ci", init, const] {alignment = 4 : i64}
+// CHECK:    %[[CL_PTR:.*]] = cir.alloca !cir.int<s, 64>, !cir.ptr<!cir.int<s, 64>>, ["cl", init, const] {alignment = 8 : i64}
+// CHECK:    %[[CF_PTR:.*]] = cir.alloca !cir.float, !cir.ptr<!cir.float>, ["cf", init, const] {alignment = 4 : i64}
+// CHECK:    %[[CD_PTR:.*]] = cir.alloca !cir.double, !cir.ptr<!cir.double>, ["cd", init, const] {alignment = 8 : i64}
+// CHECK:    %[[CB1_PTR:.*]] = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["cb1", init, const] {alignment = 1 : i64}
+// CHECK:    %[[CB2_PTR:.*]] = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["cb2", init, const] {alignment = 1 : i64}
+// CHECK:    %[[UII_PTR:.*]] = cir.alloca !cir.int<s, 32>, !cir.ptr<!cir.int<s, 32>>, ["uii"] {alignment = 4 : i64}
+// CHECK:    %[[UIL_PTR:.*]] = cir.alloca !cir.int<s, 64>, !cir.ptr<!cir.int<s, 64>>, ["uil"] {alignment = 8 : i64}
+// CHECK:    %[[UIF_PTR:.*]] = cir.alloca !cir.float, !cir.ptr<!cir.float>, ["uif"] {alignment = 4 : i64}
+// CHECK:    %[[UID_PTR:.*]] = cir.alloca !cir.double, !cir.ptr<!cir.double>, ["uid"] {alignment = 8 : i64}
+// CHECK:    %[[UIB_PTR:.*]] = cir.alloca !cir.bool, !cir.ptr<!cir.bool>, ["uib"] {alignment = 1 : i64}
+// CHECK:    %[[ONE:.*]] = cir.const #cir.int<1> : !cir.int<s, 32>
+// CHECK:    cir.store %[[ONE]], %[[I_PTR]] : !cir.int<s, 32>, !cir.ptr<!cir.int<s, 32>>
+// CHECK:    %[[TWO:.*]] = cir.const #cir.int<2> : !cir.int<s, 64>
+// CHECK:    cir.store %[[TWO]], %[[L_PTR]] : !cir.int<s, 64>, !cir.ptr<!cir.int<s, 64>>
+// CHECK:    %[[THREE:.*]] = cir.const #cir.fp<3.0{{.*}}> : !cir.float
+// CHECK:    cir.store %[[THREE]], %[[F_PTR]] : !cir.float, !cir.ptr<!cir.float>
+// CHECK:    %[[FOUR:.*]] = cir.const #cir.fp<4.0{{.*}}> : !cir.double
+// CHECK:    cir.store %[[FOUR]], %[[D_PTR]] : !cir.double, !cir.ptr<!cir.double>
+// CHECK:    %[[TRUE:.*]] = cir.const #true
+// CHECK:    cir.store %[[TRUE]], %[[B1_PTR]] : !cir.bool, !cir.ptr<!cir.bool>
+// CHECK:    %[[FALSE:.*]] = cir.const #false
+// CHECK:    cir.store %[[FALSE]], %[[B2_PTR]] : !cir.bool, !cir.ptr<!cir.bool>
+// CHECK:    %[[ONEC:.*]] = cir.const #cir.int<1> : !cir.int<s, 32>
+// CHECK:    cir.store %[[ONEC]], %[[CI_PTR]] : !cir.int<s, 32>, !cir.ptr<!cir.int<s, 32>>
+// CHECK:    %[[TWOC:.*]] = cir.const #cir.int<2> : !cir.int<s, 64>
+// CHECK:    cir.store %[[TWOC]], %[[CL_PTR]] : !cir.int<s, 64>, !cir.ptr<!cir.int<s, 64>>
+// CHECK:    %[[THREEC:.*]] = cir.const #cir.fp<3.0{{.*}}> : !cir.float
+// CHECK:    cir.store %[[THREEC]], %[[CF_PTR]] : !cir.float, !cir.ptr<!cir.float>
+// CHECK:    %[[FOURC:.*]] = cir.const #cir.fp<4.0{{.*}}> : !cir.double
+// CHECK:    cir.store %[[FOURC]], %[[CD_PTR]] : !cir.double, !cir.ptr<!cir.double>
+// CHECK:    %[[TRUEC:.*]] = cir.const #true
+// CHECK:    cir.store %[[TRUEC]], %[[CB1_PTR]] : !cir.bool, !cir.ptr<!cir.bool>
+// CHECK:    %[[FALSEC:.*]] = cir.const #false
+// CHECK:    cir.store %[[FALSEC]], %[[CB2_PTR]] : !cir.bool, !cir.ptr<!cir.bool>

diff  --git a/clang/test/CIR/Lowering/basic.cpp b/clang/test/CIR/Lowering/basic.cpp
index 2c29368bd5835..d1dc343a068a8 100644
--- a/clang/test/CIR/Lowering/basic.cpp
+++ b/clang/test/CIR/Lowering/basic.cpp
@@ -1,9 +1,4 @@
-// RUN: not %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %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
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o - 2>&1 | FileCheck %s
 
 int f1() {
   int i;
@@ -22,6 +17,7 @@ int f2() {
 
 // CHECK: define{{.*}} i32 @f2() {
 // CHECK:    %[[I_PTR:.*]] = alloca i32, i64 1, align 4
+// CHECK:    store i32 2, ptr %[[I_PTR]], align 4
 // CHECK:    %[[I:.*]] = load i32, ptr %[[I_PTR]], align 4
 // CHECK:    ret i32 %[[I]]
 

diff  --git a/clang/test/CIR/Lowering/local-vars.cpp b/clang/test/CIR/Lowering/local-vars.cpp
new file mode 100644
index 0000000000000..bd47ed14065c6
--- /dev/null
+++ b/clang/test/CIR/Lowering/local-vars.cpp
@@ -0,0 +1,55 @@
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o - 2>&1 | FileCheck %s
+
+void test() {
+  int i = 1;
+  long l = 2l;
+  float f = 3.0f;
+  double d = 4.0;
+  bool b1 = true;
+  bool b2 = false;
+  const int ci = 1;
+  const long cl = 2l;
+  const float cf = 3.0f;
+  const double cd = 4.0;
+  const bool cb1 = true;
+  const bool cb2 = false;
+  int uii;
+  long uil;
+  float uif;
+  double uid;
+  bool uib;
+}
+
+// Note: The alignment of i64 stores below is wrong. That should be fixed
+//       when we add alignment attributes to the load/store ops.
+
+// CHECK: define{{.*}} void @test()
+// CHECK:    %[[I_PTR:.*]] = alloca i32, i64 1, align 4
+// CHECK:    %[[L_PTR:.*]] = alloca i64, i64 1, align 8
+// CHECK:    %[[F_PTR:.*]] = alloca float, i64 1, align 4
+// CHECK:    %[[D_PTR:.*]] = alloca double, i64 1, align 8
+// CHECK:    %[[B1_PTR:.*]] = alloca i8, i64 1, align 1
+// CHECK:    %[[B2_PTR:.*]] = alloca i8, i64 1, align 1
+// CHECK:    %[[CI_PTR:.*]] = alloca i32, i64 1, align 4
+// CHECK:    %[[CL_PTR:.*]] = alloca i64, i64 1, align 8
+// CHECK:    %[[CF_PTR:.*]] = alloca float, i64 1, align 4
+// CHECK:    %[[CD_PTR:.*]] = alloca double, i64 1, align 8
+// CHECK:    %[[CB1_PTR:.*]] = alloca i8, i64 1, align 1
+// CHECK:    %[[CB2_PTR:.*]] = alloca i8, i64 1, align 1
+// CHECK:    %[[UII_PTR:.*]] = alloca i32, i64 1, align 4
+// CHECK:    %[[UIL_PTR:.*]] = alloca i64, i64 1, align 8
+// CHECK:    %[[UIF_PTR:.*]] = alloca float, i64 1, align 4
+// CHECK:    %[[UID_PTR:.*]] = alloca double, i64 1, align 8
+// CHECK:    %[[UIB_PTR:.*]] = alloca i8, i64 1, align 1
+// CHECK:    store i32 1, ptr %[[I_PTR]], align 4
+// CHECK:    store i64 2, ptr %[[L_PTR]], align 4
+// CHECK:    store float 3.000000e+00, ptr %[[F_PTR]], align 4
+// CHECK:    store double 4.000000e+00, ptr %[[D_PTR]], align 8
+// CHECK:    store i8 1, ptr %[[B1_PTR]], align 1
+// CHECK:    store i8 0, ptr %[[B2_PTR]], align 1
+// CHECK:    store i32 1, ptr %[[CI_PTR]], align 4
+// CHECK:    store i64 2, ptr %[[CL_PTR]], align 4
+// CHECK:    store float 3.000000e+00, ptr %[[CF_PTR]], align 4
+// CHECK:    store double 4.000000e+00, ptr %[[CD_PTR]], align 8
+// CHECK:    store i8 1, ptr %[[CB1_PTR]], align 1
+// CHECK:    store i8 0, ptr %[[CB2_PTR]], align 1


        


More information about the cfe-commits mailing list