Files
clang-p2996/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
Andy Kaylor 8eb9b947af [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.
2025-03-07 10:23:06 -08:00

310 lines
11 KiB
C++

//===----------------------------------------------------------------------===//
//
// 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");
}