[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