https://github.com/andykaylor updated https://github.com/llvm/llvm-project/pull/129167
>From d1fa2629b5786befa8ca8f839cf948df4644d615 Mon Sep 17 00:00:00 2001 From: Andy Kaylor <akay...@nvidia.com> Date: Tue, 25 Feb 2025 16:52:18 -0800 Subject: [PATCH 1/2] [CIR] Upstream func args alloca handling This change adds support for collecting function arguments and storing them in alloca memory slots. --- .../CIR/Dialect/Builder/CIRBaseBuilder.h | 5 ++ clang/include/clang/CIR/Dialect/IR/CIROps.td | 39 ++++++++++ clang/include/clang/CIR/MissingFeatures.h | 1 + clang/lib/CIR/CodeGen/Address.h | 8 ++ clang/lib/CIR/CodeGen/CIRGenCall.h | 28 +++++++ clang/lib/CIR/CodeGen/CIRGenDecl.cpp | 2 +- clang/lib/CIR/CodeGen/CIRGenFunction.cpp | 76 +++++++++++++++++-- clang/lib/CIR/CodeGen/CIRGenFunction.h | 11 ++- clang/lib/CIR/Dialect/IR/CIRMemorySlot.cpp | 33 ++++++++ clang/test/CIR/CodeGen/basic.cpp | 20 +++++ 10 files changed, 212 insertions(+), 11 deletions(-) create mode 100644 clang/lib/CIR/CodeGen/CIRGenCall.h diff --git a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h index 14afdfc2758ea..b65797e40d5f9 100644 --- a/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h +++ b/clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h @@ -69,6 +69,11 @@ class CIRBaseBuilderTy : public mlir::OpBuilder { return create<cir::LoadOp>(loc, ptr); } + cir::StoreOp createStore(mlir::Location loc, mlir::Value val, + mlir::Value dst) { + return create<cir::StoreOp>(loc, val, dst); + } + // // Block handling helpers // ---------------------- diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 083cf46a93ae6..48178b0ff247d 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -228,6 +228,45 @@ def LoadOp : CIR_Op<"load", [ // FIXME: add verifier. } +//===----------------------------------------------------------------------===// +// StoreOp +//===----------------------------------------------------------------------===// + +def StoreOp : CIR_Op<"store", [ + TypesMatchWith<"type of 'value' matches pointee type of 'addr'", + "addr", "value", + "cast<PointerType>($_self).getPointee()">, + DeclareOpInterfaceMethods<PromotableMemOpInterface>]> { + + let summary = "Store value to memory address"; + let description = [{ + `cir.store` stores a value (first operand) to the memory address specified + in the second operand. A unit attribute `volatile` can be used to indicate + a volatile store. Store's 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 + // Store a function argument to local storage, address in %0. + cir.store %arg0, %0 : i32, !cir.ptr<i32> + ``` + }]; + + let arguments = (ins CIR_AnyType:$value, + Arg<CIR_PointerType, "the address to store the value", + [MemWrite]>:$addr); + + let assemblyFormat = [{ + $value `,` $addr attr-dict `:` type($value) `,` qualified(type($addr)) + }]; + + // FIXME: add verifier. +} + //===----------------------------------------------------------------------===// // ReturnOp //===----------------------------------------------------------------------===// diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index 5c7e10d018809..9b416ef61055e 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -59,6 +59,7 @@ struct MissingFeatures { // Misc static bool scalarConversionOpts() { return false; } static bool tryEmitAsConstant() { return false; } + static bool constructABIArgDirectExtend() { return false; } }; } // namespace cir diff --git a/clang/lib/CIR/CodeGen/Address.h b/clang/lib/CIR/CodeGen/Address.h index 72e7e1dcf1560..fba1ffd90877b 100644 --- a/clang/lib/CIR/CodeGen/Address.h +++ b/clang/lib/CIR/CodeGen/Address.h @@ -52,6 +52,14 @@ class Address { elementType); } + Address(mlir::Value pointer, clang::CharUnits alignment) + : Address(pointer, + mlir::cast<cir::PointerType>(pointer.getType()).getPointee(), + alignment) { + assert((!alignment.isZero() || pointer == nullptr) && + "creating valid address with invalid alignment"); + } + static Address invalid() { return Address(nullptr); } bool isValid() const { return pointerAndKnownNonNull.getPointer() != nullptr; diff --git a/clang/lib/CIR/CodeGen/CIRGenCall.h b/clang/lib/CIR/CodeGen/CIRGenCall.h new file mode 100644 index 0000000000000..0996167feeef6 --- /dev/null +++ b/clang/lib/CIR/CodeGen/CIRGenCall.h @@ -0,0 +1,28 @@ +//===----------------------------------------------------------------------===// +// +// 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 wrap the information about a call or function +// definition used to handle ABI compliancy. +// +//===----------------------------------------------------------------------===// + +#ifndef CLANG_LIB_CODEGEN_CIRGENCALL_H +#define CLANG_LIB_CODEGEN_CIRGENCALL_H + +#include "clang/AST/GlobalDecl.h" +#include "llvm/ADT/SmallVector.h" + +namespace clang::CIRGen { + +/// Type for representing both the decl and type of parameters to a function. +/// The decl must be either a ParmVarDecl or ImplicitParamDecl. +class FunctionArgList : public llvm::SmallVector<const clang::VarDecl *, 16> {}; + +} // namespace clang::CIRGen + +#endif // CLANG_LIB_CODEGEN_CIRGENCALL_H diff --git a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp index e44cad559d509..c34d42eff6966 100644 --- a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp @@ -44,7 +44,7 @@ void CIRGenFunction::emitAutoVarAlloca(const VarDecl &d) { mlir::Type allocaTy = convertTypeForMem(ty); // Create the temp alloca and declare variable using it. address = createTempAlloca(allocaTy, alignment, loc, d.getName()); - declare(address, &d, ty, getLoc(d.getSourceRange()), alignment); + declare(address.getPointer(), &d, ty, getLoc(d.getSourceRange()), alignment); setAddrOfLocalVar(&d, address); } diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp index 86986b5847e98..76912d412fd06 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp @@ -12,6 +12,8 @@ #include "CIRGenFunction.h" +#include "CIRGenCall.h" +#include "mlir/IR/Location.h" #include "clang/AST/GlobalDecl.h" #include "clang/CIR/MissingFeatures.h" @@ -132,15 +134,17 @@ 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) { +mlir::LogicalResult CIRGenFunction::declare(mlir::Value addrVal, + const Decl *var, QualType ty, + mlir::Location loc, + CharUnits alignment, bool isParam) { 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 (isParam) + allocaOp.setInitAttr(mlir::UnitAttr::get(&getMLIRContext())); if (ty->isReferenceType() || ty.isConstQualified()) allocaOp.setConstantAttr(mlir::UnitAttr::get(&getMLIRContext())); @@ -149,7 +153,7 @@ mlir::LogicalResult CIRGenFunction::declare(Address addr, const Decl *var, void CIRGenFunction::startFunction(GlobalDecl gd, QualType returnType, cir::FuncOp fn, cir::FuncType funcType, - SourceLocation loc, + FunctionArgList args, SourceLocation loc, SourceLocation startLoc) { assert(!curFn && "CIRGenFunction can only be used for one function at a time"); @@ -157,8 +161,41 @@ void CIRGenFunction::startFunction(GlobalDecl gd, QualType returnType, fnRetTy = returnType; curFn = fn; + const auto *fd = dyn_cast_or_null<FunctionDecl>(gd.getDecl()); + mlir::Block *entryBB = &fn.getBlocks().front(); builder.setInsertionPointToStart(entryBB); + + // TODO(cir): this should live in `emitFunctionProlog + // Declare all the function arguments in the symbol table. + for (const auto nameValue : llvm::zip(args, entryBB->getArguments())) { + auto *paramVar = std::get<0>(nameValue); + mlir::Value paramVal = std::get<1>(nameValue); + auto alignment = getContext().getDeclAlign(paramVar); + auto paramLoc = getLoc(paramVar->getSourceRange()); + paramVal.setLoc(paramLoc); + + mlir::Value addrVal = + emitAlloca(cast<NamedDecl>(paramVar)->getName(), + convertType(paramVar->getType()), paramLoc, alignment); + + declare(addrVal, paramVar, paramVar->getType(), paramLoc, alignment, + /*isParam=*/true); + + setAddrOfLocalVar(paramVar, Address(addrVal, alignment)); + + bool isPromoted = isa<ParmVarDecl>(paramVar) && + cast<ParmVarDecl>(paramVar)->isKNRPromoted(); + assert(!cir::MissingFeatures::constructABIArgDirectExtend()); + if (isPromoted) + cgm.errorNYI(fd->getSourceRange(), "Function argument demotion"); + + // Location of the store to the param storage tracked as beginning of + // the function body. + mlir::Location fnBodyBegin = getLoc(fd->getBody()->getBeginLoc()); + builder.CIRBaseBuilderTy::createStore(fnBodyBegin, paramVal, addrVal); + } + assert(builder.getInsertionBlock() && "Should be valid"); } void CIRGenFunction::finishFunction(SourceLocation endLoc) {} @@ -187,8 +224,10 @@ cir::FuncOp CIRGenFunction::generateCode(clang::GlobalDecl gd, cir::FuncOp fn, // This will be used once more code is upstreamed. [[maybe_unused]] mlir::Block *entryBB = fn.addEntryBlock(); - startFunction(gd, funcDecl->getReturnType(), fn, funcType, loc, - bodyRange.getBegin()); + FunctionArgList args; + QualType retTy = buildFunctionArgList(gd, args); + + startFunction(gd, retTy, fn, funcType, args, loc, bodyRange.getBegin()); if (isa<CXXDestructorDecl>(funcDecl)) getCIRGenModule().errorNYI(bodyRange, "C++ destructor definition"); @@ -234,6 +273,29 @@ cir::FuncOp CIRGenFunction::generateCode(clang::GlobalDecl gd, cir::FuncOp fn, return fn; } +clang::QualType CIRGenFunction::buildFunctionArgList(clang::GlobalDecl gd, + FunctionArgList &args) { + const auto *fd = cast<FunctionDecl>(gd.getDecl()); + QualType retTy = fd->getReturnType(); + + const auto *md = dyn_cast<CXXMethodDecl>(fd); + if (md && md->isInstance()) + cgm.errorNYI(fd->getSourceRange(), "buildFunctionArgList: CXXMethodDecl"); + + if (isa<CXXConstructorDecl>(fd)) + cgm.errorNYI(fd->getSourceRange(), + "buildFunctionArgList: CXXConstructorDecl"); + + for (auto *param : fd->parameters()) + args.push_back(param); + + if (md && (isa<CXXConstructorDecl>(md) || isa<CXXDestructorDecl>(md))) + cgm.errorNYI(fd->getSourceRange(), + "buildFunctionArgList: implicit structor params"); + + return retTy; +} + /// Emit code to compute a designator that specifies the location /// of the expression. /// FIXME: document this function better. diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index e0888acdc3dce..6b383378ae764 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -14,6 +14,7 @@ #define CLANG_LIB_CIR_CODEGEN_CIRGENFUNCTION_H #include "CIRGenBuilder.h" +#include "CIRGenCall.h" #include "CIRGenModule.h" #include "CIRGenTypeCache.h" #include "CIRGenValue.h" @@ -96,9 +97,9 @@ class CIRGenFunction : public CIRGenTypeCache { 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, + mlir::LogicalResult declare(mlir::Value addrVal, const clang::Decl *var, clang::QualType ty, mlir::Location loc, - clang::CharUnits alignment); + clang::CharUnits alignment, bool isParam = false); public: mlir::Value emitAlloca(llvm::StringRef name, mlir::Type ty, @@ -196,12 +197,16 @@ class CIRGenFunction : public CIRGenTypeCache { cir::FuncOp generateCode(clang::GlobalDecl gd, cir::FuncOp fn, cir::FuncType funcType); + clang::QualType buildFunctionArgList(clang::GlobalDecl gd, + FunctionArgList &args); + /// Emit code for the start of a function. /// \param loc The location to be associated with the function. /// \param startLoc The location of the function body. void startFunction(clang::GlobalDecl gd, clang::QualType retTy, cir::FuncOp fn, cir::FuncType funcType, - clang::SourceLocation loc, clang::SourceLocation startLoc); + FunctionArgList args, clang::SourceLocation loc, + clang::SourceLocation startLoc); Address createTempAlloca(mlir::Type ty, CharUnits align, mlir::Location loc, const Twine &name = "tmp"); diff --git a/clang/lib/CIR/Dialect/IR/CIRMemorySlot.cpp b/clang/lib/CIR/Dialect/IR/CIRMemorySlot.cpp index af6b5e4fbd9f6..5e44837979af3 100644 --- a/clang/lib/CIR/Dialect/IR/CIRMemorySlot.cpp +++ b/clang/lib/CIR/Dialect/IR/CIRMemorySlot.cpp @@ -75,3 +75,36 @@ DeletionKind cir::LoadOp::removeBlockingUses( getResult().replaceAllUsesWith(reachingDefinition); return DeletionKind::Delete; } + +//===----------------------------------------------------------------------===// +// Interfaces for StoreOp +//===----------------------------------------------------------------------===// + +bool cir::StoreOp::loadsFrom(const MemorySlot &slot) { return false; } + +bool cir::StoreOp::storesTo(const MemorySlot &slot) { + return getAddr() == slot.ptr; +} + +Value cir::StoreOp::getStored(const MemorySlot &slot, OpBuilder &builder, + Value reachingDef, const DataLayout &dataLayout) { + return getValue(); +} + +bool cir::StoreOp::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 && + getValue() != slot.ptr && slot.elemType == getValue().getType(); +} + +DeletionKind cir::StoreOp::removeBlockingUses( + const MemorySlot &slot, const SmallPtrSetImpl<OpOperand *> &blockingUses, + OpBuilder &builder, Value reachingDefinition, + const DataLayout &dataLayout) { + return DeletionKind::Delete; +} diff --git a/clang/test/CIR/CodeGen/basic.cpp b/clang/test/CIR/CodeGen/basic.cpp index 210afcd541159..6a2faa725a34d 100644 --- a/clang/test/CIR/CodeGen/basic.cpp +++ b/clang/test/CIR/CodeGen/basic.cpp @@ -25,3 +25,23 @@ int f2() { // 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> + +int f3(int i) { + return i; + } + +// CHECK: cir.func @f3(%[[ARG:.*]]: !cir.int<s, 32> loc({{.*}})) -> !cir.int<s, 32> +// CHECK: %[[ARG_ALLOCA:.*]] = cir.alloca !cir.int<s, 32>, !cir.ptr<!cir.int<s, 32>>, ["i", init] {alignment = 4 : i64} +// CHECK: cir.store %[[ARG]], %[[ARG_ALLOCA]] : !cir.int<s, 32>, !cir.ptr<!cir.int<s, 32>> +// CHECK: %[[ARG_VAL:.*]] = cir.load %[[ARG_ALLOCA]] : !cir.ptr<!cir.int<s, 32>>, !cir.int<s, 32> +// CHECK: cir.return %[[ARG_VAL]] : !cir.int<s, 32> + +int f4(const int i) { + return i; +} + +// CHECK: cir.func @f4(%[[ARG:.*]]: !cir.int<s, 32> loc({{.*}})) -> !cir.int<s, 32> +// CHECK: %[[ARG_ALLOCA:.*]] = cir.alloca !cir.int<s, 32>, !cir.ptr<!cir.int<s, 32>>, ["i", init, const] {alignment = 4 : i64} +// CHECK: cir.store %[[ARG]], %[[ARG_ALLOCA]] : !cir.int<s, 32>, !cir.ptr<!cir.int<s, 32>> +// CHECK: %[[ARG_VAL:.*]] = cir.load %[[ARG_ALLOCA]] : !cir.ptr<!cir.int<s, 32>>, !cir.int<s, 32> +// CHECK: cir.return %[[ARG_VAL]] : !cir.int<s, 32> >From 9f548a1492a3da5d9f1d4358916f88531e0dc9e7 Mon Sep 17 00:00:00 2001 From: Andy Kaylor <akay...@nvidia.com> Date: Fri, 28 Feb 2025 10:39:09 -0800 Subject: [PATCH 2/2] Replace auto with correct types --- clang/lib/CIR/CodeGen/CIRGenFunction.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp index 76912d412fd06..7861a48c93244 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp @@ -169,10 +169,10 @@ void CIRGenFunction::startFunction(GlobalDecl gd, QualType returnType, // TODO(cir): this should live in `emitFunctionProlog // Declare all the function arguments in the symbol table. for (const auto nameValue : llvm::zip(args, entryBB->getArguments())) { - auto *paramVar = std::get<0>(nameValue); + const VarDecl *paramVar = std::get<0>(nameValue); mlir::Value paramVal = std::get<1>(nameValue); - auto alignment = getContext().getDeclAlign(paramVar); - auto paramLoc = getLoc(paramVar->getSourceRange()); + CharUnits alignment = getContext().getDeclAlign(paramVar); + mlir::Location paramLoc = getLoc(paramVar->getSourceRange()); paramVal.setLoc(paramLoc); mlir::Value addrVal = _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits