llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang Author: Andy Kaylor (andykaylor) <details> <summary>Changes</summary> 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. --- Patch is 48.28 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/130164.diff 14 Files Affected: - (modified) clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h (+4) - (modified) clang/include/clang/CIR/Dialect/IR/CIRAttrs.td (+14) - (modified) clang/include/clang/CIR/MissingFeatures.h (+8-2) - (added) clang/lib/CIR/CodeGen/CIRGenConstantEmitter.h (+98) - (modified) clang/lib/CIR/CodeGen/CIRGenDecl.cpp (+169-11) - (modified) clang/lib/CIR/CodeGen/CIRGenExpr.cpp (+70-2) - (added) clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp (+322) - (modified) clang/lib/CIR/CodeGen/CIRGenFunction.h (+106-3) - (modified) clang/lib/CIR/CodeGen/CIRGenModule.h (+14) - (modified) clang/lib/CIR/CodeGen/CMakeLists.txt (+1) - (modified) clang/test/CIR/CodeGen/basic.cpp (+6-9) - (added) clang/test/CIR/CodeGen/local-vars.cpp (+65) - (modified) clang/test/CIR/Lowering/basic.cpp (+2-6) - (added) clang/test/CIR/Lowering/local-vars.cpp (+55) ``````````diff 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..e29149c2e70f1 --- /dev/null +++ b/clang/lib/CIR/CodeGen/CIRGenConstantEmitter.h @@ -0,0 +1,98 @@ +//===----------------------------------------------------------------------===// +// +// 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: + struct AbstractState { + bool oldValue; + }; + AbstractState pushAbstract() { + AbstractState saved = {abstract}; + abstract = true; + return saved; + } + mlir::Attribute validateAndPopAbstract(mlir::Attribute C, AbstractState save); +}; + +} // 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..ed42cd6bd2c32 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,124 @@ 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) { + 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 +185,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 +203,59 @@ 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) { + Qualifiers::ObjCLifetime lifetime = Qualifiers::ObjCLifetime::OCL_None; + assert(!cir::MissingFeatures::objCLifetime()); + + if (!lifetime) { + 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; + } + + cgm.errorNYI(loc, "emitScalarInit: ObjCLifetime"); +} + +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 different 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::a... [truncated] `````````` </details> https://github.com/llvm/llvm-project/pull/130164 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits