https://github.com/andykaylor updated https://github.com/llvm/llvm-project/pull/128792
>From 235ef9e23a314f8946b48143294da367b80f7d14 Mon Sep 17 00:00:00 2001 From: Andy Kaylor <akay...@nvidia.com> Date: Tue, 25 Feb 2025 12:31:08 -0800 Subject: [PATCH 1/3] [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> >From 39b2db10fa3eefb31556be0afa9ce9acfd0f73a8 Mon Sep 17 00:00:00 2001 From: Andy Kaylor <akay...@nvidia.com> Date: Tue, 25 Feb 2025 16:54:37 -0800 Subject: [PATCH 2/3] Fix test checks --- clang/test/CIR/CodeGen/basic.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clang/test/CIR/CodeGen/basic.cpp b/clang/test/CIR/CodeGen/basic.cpp index 1813982f66b5e..b60b8b8ed8cc0 100644 --- a/clang/test/CIR/CodeGen/basic.cpp +++ b/clang/test/CIR/CodeGen/basic.cpp @@ -9,5 +9,5 @@ int f1() { // 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> +// CHECK: %[[I:.*]] = cir.load %[[I_PTR]] : !cir.ptr<!cir.int<s, 32>>, !cir.int<s, 32> +// CHECK: cir.return %[[I]] : !cir.int<s, 32> >From 1ffe43b0ca298df1e35f692d98401579f32d528d Mon Sep 17 00:00:00 2001 From: Andy Kaylor <akay...@nvidia.com> Date: Wed, 26 Feb 2025 15:03:28 -0800 Subject: [PATCH 3/3] Address review feedback --- .../CIR/Dialect/Builder/CIRBaseBuilder.h | 6 +- clang/include/clang/CIR/Dialect/IR/CIROps.td | 40 ++----------- clang/include/clang/CIR/MissingFeatures.h | 7 ++- clang/lib/CIR/CodeGen/Address.h | 6 +- clang/lib/CIR/CodeGen/CIRGenDecl.cpp | 56 +++++++++++++++---- clang/lib/CIR/CodeGen/CIRGenExpr.cpp | 30 +++++----- clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp | 9 +-- clang/lib/CIR/CodeGen/CIRGenFunction.cpp | 16 ++++++ clang/lib/CIR/CodeGen/CIRGenFunction.h | 20 +++++-- clang/lib/CIR/CodeGen/CIRGenStmt.cpp | 3 +- clang/lib/CIR/CodeGen/CIRGenValue.h | 6 +- clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 10 ++-- clang/test/CIR/CodeGen/basic.cpp | 18 +++++- 13 files changed, 134 insertions(+), 93 deletions(-) diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h index a62b00cc8ed33..14afdfc2758ea 100644 --- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h +++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h @@ -55,10 +55,8 @@ class CIRBaseBuilderTy : public mlir::OpBuilder { 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); + mlir::IntegerAttr alignment) { + return create<cir::AllocaOp>(loc, addrType, type, name, alignment); } cir::LoadOp createLoad(mlir::Location loc, mlir::Value ptr, diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 1ff5e215c10a0..3202ed1ead3c6 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -139,25 +139,16 @@ def AllocaOp : CIR_Op<"alloca", [ 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 count; + %0 = cir.alloca i32, !cir.ptr<i32>, ["count"] {alignment = 4 : i64} // int *ptr; %1 = cir.alloca !cir.ptr<i32>, !cir.ptr<!cir.ptr<i32>>, ["ptr"] {alignment = 8 : i64} @@ -166,7 +157,6 @@ def AllocaOp : CIR_Op<"alloca", [ }]; let arguments = (ins - Optional<PrimitiveInt>:$dynAllocSize, TypeAttr:$allocaType, StrAttr:$name, UnitAttr:$init, @@ -180,32 +170,19 @@ def AllocaOp : CIR_Op<"alloca", [ 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); - }]> + "mlir::IntegerAttr":$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^)? @@ -229,13 +206,7 @@ def LoadOp : CIR_Op<"load", [ 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. + backed up by a `cir.ptr` type. Example: @@ -247,8 +218,7 @@ def LoadOp : CIR_Op<"load", [ }]; let arguments = (ins Arg<CIR_PointerType, "the address to load from", - [MemRead]>:$addr - ); + [MemRead]>:$addr); let results = (outs CIR_AnyType:$result); let assemblyFormat = [{ diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index 97e919b5c2d74..5c7e10d018809 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -30,6 +30,9 @@ struct MissingFeatures { // This isn't needed until we add support for bools. static bool convertTypeForMemory() { return false; } + // CIRGenFunction implementation details + static bool cgfSymbolTable() { return false; } + // Unhandled global/linkage information. static bool opGlobalDSOLocal() { return false; } static bool opGlobalThreadLocal() { return false; } @@ -47,13 +50,15 @@ struct MissingFeatures { static bool opAllocaStaticLocal() { return false; } static bool opAllocaNonGC() { return false; } static bool opAllocaImpreciseLifetime() { return false; } + static bool opAllocaPreciseLifetime() { 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 + // Misc static bool scalarConversionOpts() { return false; } + static bool tryEmitAsConstant() { return false; } }; } // namespace cir diff --git a/clang/lib/CIR/CodeGen/Address.h b/clang/lib/CIR/CodeGen/Address.h index c68facde9a18c..72e7e1dcf1560 100644 --- a/clang/lib/CIR/CodeGen/Address.h +++ b/clang/lib/CIR/CodeGen/Address.h @@ -11,8 +11,8 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_CLANG_LIB_CIR_ADDRESS_H -#define LLVM_CLANG_LIB_CIR_ADDRESS_H +#ifndef CLANG_LIB_CIR_ADDRESS_H +#define CLANG_LIB_CIR_ADDRESS_H #include "mlir/IR/Value.h" #include "clang/AST/CharUnits.h" @@ -73,4 +73,4 @@ class Address { } // namespace clang::CIRGen -#endif // LLVM_CLANG_LIB_CIR_ADDRESS_H +#endif // CLANG_LIB_CIR_ADDRESS_H diff --git a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp index 34e0b18594efe..444083b7641bf 100644 --- a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp @@ -11,18 +11,18 @@ //===----------------------------------------------------------------------===// #include "CIRGenFunction.h" +#include "clang/AST/Attr.h" +#include "clang/AST/Decl.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) { +void CIRGenFunction::emitAutoVarAlloca(const VarDecl &d) { QualType ty = d.getType(); - assert(ty.getAddressSpace() == LangAS::Default); + if (ty.getAddressSpace() != LangAS::Default) + cgm.errorNYI(d.getSourceRange(), "emitAutoVarAlloca: address space"); auto loc = getLoc(d.getSourceRange()); @@ -43,18 +43,50 @@ void CIRGenFunction::emitAutoVarDecl(const VarDecl &d) { // 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); + address = createTempAlloca(allocaTy, alignment, loc, d.getName()); + declare(address, &d, ty, getLoc(d.getSourceRange()), alignment); + setAddrOfLocalVar(&d, address); - // TODO: emit var init and cleanup +} + +void CIRGenFunction::emitAutoVarInit(const clang::VarDecl &d) { + 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"); + } +} + +void CIRGenFunction::emitAutoVarCleanups(const clang::VarDecl &d) { + // Check the type for a cleanup. + if (QualType::DestructionKind dtorKind = d.needsDestruction(getContext())) + cgm.errorNYI(d.getSourceRange(), "emitAutoVarCleanups: type cleanup"); + + assert(!cir::MissingFeatures::opAllocaPreciseLifetime()); + + // Handle the cleanup attribute. + if (d.hasAttr<CleanupAttr>()) + cgm.errorNYI(d.getSourceRange(), "emitAutoVarCleanups: CleanupAttr"); +} + + +/// 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) { + emitAutoVarAlloca(d); + emitAutoVarInit(d); + emitAutoVarCleanups(d); } void CIRGenFunction::emitVarDecl(const VarDecl &d) { - if (d.hasExternalStorage()) { - // Don't emit it now, allow it to be emitted lazily on its first use. + // If the declaration has external storage, don't emit it now, allow it to be + // emitted lazily on its first use. + if (d.hasExternalStorage()) return; - } if (d.getStorageDuration() != SD_Automatic) cgm.errorNYI(d.getSourceRange(), "emitVarDecl automatic storage duration"); diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp index 8bb670d0d5bae..2c09db9610258 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -13,6 +13,7 @@ #include "Address.h" #include "CIRGenFunction.h" #include "CIRGenValue.h" +#include "mlir/IR/BuiltinAttributes.h" #include "clang/AST/Attr.h" #include "clang/AST/CharUnits.h" #include "clang/AST/Decl.h" @@ -33,11 +34,11 @@ mlir::Value CIRGenFunction::emitLoadOfScalar(LValue lvalue, Address addr = lvalue.getAddress(); mlir::Type eltTy = addr.getElementType(); - auto ptr = addr.getPointer(); + mlir::Value ptr = addr.getPointer(); if (mlir::isa<cir::VoidType>(eltTy)) cgm.errorNYI(loc, "emitLoadOfScalar: void type"); - auto loadOp = builder.CIRBaseBuilderTy::createLoad(getLoc(loc), ptr, + mlir::Value loadOp = builder.CIRBaseBuilderTy::createLoad(getLoc(loc), ptr, false /*isVolatile*/); return loadOp; @@ -54,6 +55,7 @@ RValue CIRGenFunction::emitLoadOfLValue(LValue lv, SourceLocation loc) { return RValue::get(emitLoadOfScalar(lv, loc)); cgm.errorNYI(loc, "emitLoadOfLValue"); + return RValue::get(nullptr); } LValue CIRGenFunction::emitDeclRefLValue(const DeclRefExpr *e) { @@ -92,37 +94,35 @@ LValue CIRGenFunction::emitDeclRefLValue(const DeclRefExpr *e) { } cgm.errorNYI(e->getSourceRange(), "emitDeclRefLValue: unhandled decl type"); + return LValue(); } mlir::Value CIRGenFunction::emitAlloca(StringRef name, mlir::Type ty, - mlir::Location loc, CharUnits alignment, - mlir::Value arraySize) { + mlir::Location loc, CharUnits alignment) { 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. + // CIR uses its own alloca address space 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); + cir::PointerType localVarPtrTy = builder.getPointerTo(ty); + mlir::IntegerAttr 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); + /*var type*/ ty, name, alignIntAttr); 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 +/// This creates an alloca and 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); + mlir::Location loc, const Twine &name) { + mlir::Value alloca = emitAlloca(name.str(), ty, loc, align); return Address(alloca, ty, align); } diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp index affb657622f67..90a2fd2a5d806 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp @@ -63,7 +63,7 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> { // l-values mlir::Value VisitDeclRefExpr(DeclRefExpr *e) { - // TODO: Handle constant emission + assert(!cir::MissingFeatures::tryEmitAsConstant()); return emitLoadOfLValue(e); } @@ -91,8 +91,8 @@ class ScalarExprEmitter : public StmtVisitor<ScalarExprEmitter, mlir::Value> { 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); + srcType = srcType.getCanonicalType(); + dstType = dstType.getCanonicalType(); if (srcType == dstType) return src; @@ -120,9 +120,6 @@ mlir::Value ScalarExprEmitter::VisitCastExpr(CastExpr *ce) { 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)); diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp index cf71e317ace46..86986b5847e98 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp @@ -13,6 +13,7 @@ #include "CIRGenFunction.h" #include "clang/AST/GlobalDecl.h" +#include "clang/CIR/MissingFeatures.h" #include <cassert> @@ -131,6 +132,21 @@ mlir::Location CIRGenFunction::getLoc(mlir::Location lhs, mlir::Location rhs) { return mlir::FusedLoc::get(locs, metadata, &getMLIRContext()); } +mlir::LogicalResult CIRGenFunction::declare(Address addr, const Decl *var, + QualType ty, mlir::Location loc, + CharUnits alignment) { + const auto *namedVar = dyn_cast_or_null<NamedDecl>(var); + assert(namedVar && "Needs a named decl"); + assert(!cir::MissingFeatures::cgfSymbolTable()); + + mlir::Value addrVal = addr.getPointer(); + auto allocaOp = cast<cir::AllocaOp>(addrVal.getDefiningOp()); + if (ty->isReferenceType() || ty.isConstQualified()) + allocaOp.setConstantAttr(mlir::UnitAttr::get(&getMLIRContext())); + + return mlir::success(); +} + void CIRGenFunction::startFunction(GlobalDecl gd, QualType returnType, cir::FuncOp fn, cir::FuncType funcType, SourceLocation loc, diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index 0d2cc90428d91..e0888acdc3dce 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -55,7 +55,7 @@ class CIRGenFunction : public CIRGenTypeCache { using DeclMapTy = llvm::DenseMap<const clang::Decl *, Address>; /// This keeps track of the CIR allocas or globals for local C - /// delcs. + /// declarations. DeclMapTy LocalDeclMap; clang::ASTContext &getContext() const { return cgm.getASTContext(); } @@ -93,9 +93,16 @@ class CIRGenFunction : public CIRGenTypeCache { mlir::MLIRContext &getMLIRContext() { return cgm.getMLIRContext(); } +private: + /// Declare a variable in the current scope, return success if the variable + /// wasn't declared yet. + mlir::LogicalResult declare(Address addr, const clang::Decl *var, + clang::QualType ty, mlir::Location loc, + clang::CharUnits alignment); + +public: mlir::Value emitAlloca(llvm::StringRef name, mlir::Type ty, - mlir::Location loc, clang::CharUnits alignment, - mlir::Value arraySize = nullptr); + mlir::Location loc, clang::CharUnits alignment); /// Use to track source locations across nested visitor traversals. /// Always use a `SourceLocRAIIObject` to change currSrcLoc. @@ -169,6 +176,10 @@ class CIRGenFunction : public CIRGenTypeCache { /// 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); + /// This method handles emission of any variable declaration /// inside a function, including static vars etc. void emitVarDecl(const clang::VarDecl &d); @@ -193,8 +204,7 @@ class CIRGenFunction : public CIRGenTypeCache { clang::SourceLocation loc, clang::SourceLocation startLoc); Address createTempAlloca(mlir::Type ty, CharUnits align, mlir::Location loc, - const Twine &name = "tmp", - mlir::Value arraySize = nullptr); + const Twine &name = "tmp"); }; } // namespace clang::CIRGen diff --git a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp index af218bc70cacc..ed5d87a39704a 100644 --- a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp @@ -86,9 +86,8 @@ mlir::LogicalResult CIRGenFunction::emitSimpleStmt(const Stmt *s, mlir::LogicalResult CIRGenFunction::emitDeclStmt(const DeclStmt &s) { assert(builder.getInsertionBlock() && "expected valid insertion point"); - for (const auto *I : s.decls()) { + for (const Decl *I : s.decls()) emitDecl(*I); - } return mlir::success(); } diff --git a/clang/lib/CIR/CodeGen/CIRGenValue.h b/clang/lib/CIR/CodeGen/CIRGenValue.h index 9c527e3fef2a0..d29646983fd30 100644 --- a/clang/lib/CIR/CodeGen/CIRGenValue.h +++ b/clang/lib/CIR/CodeGen/CIRGenValue.h @@ -11,8 +11,8 @@ // //===----------------------------------------------------------------------===// -#ifndef LLVM_CLANG_LIB_CIR_CIRGENVALUE_H -#define LLVM_CLANG_LIB_CIR_CIRGENVALUE_H +#ifndef CLANG_LIB_CIR_CIRGENVALUE_H +#define CLANG_LIB_CIR_CIRGENVALUE_H #include "Address.h" @@ -122,4 +122,4 @@ class LValue { } // namespace clang::CIRGen -#endif // LLVM_CLANG_LIB_CIR_CIRGENVALUE_H +#endif // CLANG_LIB_CIR_CIRGENVALUE_H diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp index 29446d4af97ad..ce4b3fad696a9 100644 --- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp @@ -121,12 +121,12 @@ static void printOmittedTerminatorRegion(mlir::OpAsmPrinter &printer, // AllocaOp //===----------------------------------------------------------------------===// -void cir::AllocaOp::build(::mlir::OpBuilder &odsBuilder, - ::mlir::OperationState &odsState, ::mlir::Type addr, - ::mlir::Type allocaType, ::llvm::StringRef name, - ::mlir::IntegerAttr alignment) { +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)); + mlir::TypeAttr::get(allocaType)); odsState.addAttribute(getNameAttrName(odsState.name), odsBuilder.getStringAttr(name)); if (alignment) { diff --git a/clang/test/CIR/CodeGen/basic.cpp b/clang/test/CIR/CodeGen/basic.cpp index b60b8b8ed8cc0..210afcd541159 100644 --- a/clang/test/CIR/CodeGen/basic.cpp +++ b/clang/test/CIR/CodeGen/basic.cpp @@ -1,6 +1,10 @@ -// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o - | FileCheck %s +// 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 -int f1(); int f1() { int i; return i; @@ -11,3 +15,13 @@ int f1() { // CHECK: %[[I_PTR:.*]] = cir.alloca !cir.int<s, 32>, !cir.ptr<!cir.int<s, 32>>, ["i"] {alignment = 4 : i64} // 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 f2() { + const int i = 2; + return i; +} + +// 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:.*]] = cir.load %[[I_PTR]] : !cir.ptr<!cir.int<s, 32>>, !cir.int<s, 32> +// CHECK: cir.return %[[I]] : !cir.int<s, 32> _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits