llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clangir Author: Andy Kaylor (andykaylor) <details> <summary>Changes</summary> This change implements basic support in ClangIR for local variables using the cir.alloca and cir.load operations. --- Patch is 42.22 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/128792.diff 18 Files Affected: - (modified) clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h (+44) - (modified) clang/include/clang/CIR/Dialect/IR/CIRAttrs.td (+15) - (modified) clang/include/clang/CIR/Dialect/IR/CIROps.td (+143) - (modified) clang/include/clang/CIR/MissingFeatures.h (+18) - (added) clang/lib/CIR/CodeGen/Address.h (+76) - (added) clang/lib/CIR/CodeGen/CIRGenDecl.cpp (+82) - (added) clang/lib/CIR/CodeGen/CIRGenExpr.cpp (+128) - (modified) clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp (+66) - (modified) clang/lib/CIR/CodeGen/CIRGenFunction.cpp (+17) - (modified) clang/lib/CIR/CodeGen/CIRGenFunction.h (+61-1) - (modified) clang/lib/CIR/CodeGen/CIRGenModule.h (+5) - (modified) clang/lib/CIR/CodeGen/CIRGenStmt.cpp (+12) - (added) clang/lib/CIR/CodeGen/CIRGenValue.h (+125) - (modified) clang/lib/CIR/CodeGen/CMakeLists.txt (+2) - (modified) clang/lib/CIR/Dialect/IR/CIRDialect.cpp (+18) - (added) clang/lib/CIR/Dialect/IR/CIRMemorySlot.cpp (+77) - (modified) clang/lib/CIR/Dialect/IR/CMakeLists.txt (+1) - (added) clang/test/CIR/CodeGen/basic.cpp (+13) ``````````diff 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. + a... [truncated] `````````` </details> https://github.com/llvm/llvm-project/pull/128792 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits