diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp index 1175fdc0be2cf..305e88b20de77 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "Address.h" +#include "CIRGenConstantEmitter.h" #include "CIRGenFunction.h" #include "CIRGenModule.h" #include "CIRGenValue.h" @@ -1495,3 +1496,57 @@ cir::AllocaOp CIRGenFunction::createTempAlloca(mlir::Type ty, emitAlloca(name.str(), ty, loc, CharUnits(), ip, arraySize) .getDefiningOp()); } + +/// Try to emit a reference to the given value without producing it as +/// an l-value. For many cases, this is just an optimization, but it avoids +/// us needing to emit global copies of variables if they're named without +/// triggering a formal use in a context where we can't emit a direct +/// reference to them, for instance if a block or lambda or a member of a +/// local class uses a const int variable or constexpr variable from an +/// enclosing function. +/// +/// For named members of enums, this is the only way they are emitted. +CIRGenFunction::ConstantEmission +CIRGenFunction::tryEmitAsConstant(DeclRefExpr *refExpr) { + ValueDecl *value = refExpr->getDecl(); + + // There is a lot more to do here, but for now only EnumConstantDecl is + // supported. + assert(!cir::MissingFeatures::tryEmitAsConstant()); + + // The value needs to be an enum constant or a constant variable. + if (!isa(value)) + return ConstantEmission(); + + Expr::EvalResult result; + if (!refExpr->EvaluateAsRValue(result, getContext())) + return ConstantEmission(); + + QualType resultType = refExpr->getType(); + + // As long as we're only handling EnumConstantDecl, there should be no + // side-effects. + assert(!result.HasSideEffects); + + // Emit as a constant. + // FIXME(cir): have emitAbstract build a TypedAttr instead (this requires + // somewhat heavy refactoring...) + mlir::Attribute c = ConstantEmitter(*this).emitAbstract( + refExpr->getLocation(), result.Val, resultType); + mlir::TypedAttr cstToEmit = mlir::dyn_cast_if_present(c); + assert(cstToEmit && "expected a typed attribute"); + + assert(!cir::MissingFeatures::generateDebugInfo()); + + return ConstantEmission::forValue(cstToEmit); +} + +mlir::Value CIRGenFunction::emitScalarConstant( + const CIRGenFunction::ConstantEmission &constant, Expr *e) { + assert(constant && "not a constant"); + if (constant.isReference()) { + cgm.errorNYI(e->getSourceRange(), "emitScalarConstant: reference"); + return {}; + } + return builder.getConstant(getLoc(e->getSourceRange()), constant.getValue()); +} diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp index a7899fccc47fb..5b779404731df 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp @@ -140,7 +140,9 @@ class ScalarExprEmitter : public StmtVisitor { // l-values mlir::Value VisitDeclRefExpr(DeclRefExpr *e) { - assert(!cir::MissingFeatures::tryEmitAsConstant()); + if (CIRGenFunction::ConstantEmission constant = cgf.tryEmitAsConstant(e)) + return cgf.emitScalarConstant(constant, e); + return emitLoadOfLValue(e); } diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index ee014adc961be..e5264ba7d3fdb 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -374,6 +374,41 @@ class CIRGenFunction : public CIRGenTypeCache { /// that we can just remove the code. bool containsLabel(const clang::Stmt *s, bool ignoreCaseStmts = false); + class ConstantEmission { + // Cannot use mlir::TypedAttr directly here because of bit availability. + llvm::PointerIntPair valueAndIsReference; + ConstantEmission(mlir::TypedAttr c, bool isReference) + : valueAndIsReference(c, isReference) {} + + public: + ConstantEmission() {} + static ConstantEmission forReference(mlir::TypedAttr c) { + return ConstantEmission(c, true); + } + static ConstantEmission forValue(mlir::TypedAttr c) { + return ConstantEmission(c, false); + } + + explicit operator bool() const { + return valueAndIsReference.getOpaqueValue() != nullptr; + } + + bool isReference() const { return valueAndIsReference.getInt(); } + LValue getReferenceLValue(CIRGenFunction &cgf, Expr *refExpr) const { + assert(isReference()); + cgf.cgm.errorNYI(refExpr->getSourceRange(), + "ConstantEmission::getReferenceLValue"); + return {}; + } + + mlir::TypedAttr getValue() const { + assert(!isReference()); + return mlir::cast(valueAndIsReference.getPointer()); + } + }; + + ConstantEmission tryEmitAsConstant(DeclRefExpr *refExpr); + struct AutoVarEmission { const clang::VarDecl *Variable; /// The address of the alloca for languages with explicit address space @@ -840,6 +875,8 @@ class CIRGenFunction : public CIRGenTypeCache { mlir::LogicalResult emitReturnStmt(const clang::ReturnStmt &s); + mlir::Value emitScalarConstant(const ConstantEmission &constant, Expr *e); + /// Emit a conversion from the specified type to the specified destination /// type, both of which are CIR scalar types. mlir::Value emitScalarConversion(mlir::Value src, clang::QualType srcType, diff --git a/clang/test/CIR/CodeGen/enum.cpp b/clang/test/CIR/CodeGen/enum.cpp new file mode 100644 index 0000000000000..5d9b1057aaa14 --- /dev/null +++ b/clang/test/CIR/CodeGen/enum.cpp @@ -0,0 +1,16 @@ +// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck %s --input-file=%t.cir + +enum Numbers { + Zero, + One, + Two, + Three +}; + +int f() { + return Numbers::One; +} + +// CHECK: cir.func{{.*}} @_Z1fv +// CHECK: cir.const #cir.int<1> : !u32i