Skip to content

[CIR] Upstream func args alloca handling #129167

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Feb 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
// ----------------------
Expand Down
39 changes: 39 additions & 0 deletions clang/include/clang/CIR/Dialect/IR/CIROps.td
Original file line number Diff line number Diff line change
Expand Up @@ -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
//===----------------------------------------------------------------------===//
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/CIR/MissingFeatures.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ struct MissingFeatures {
// Misc
static bool scalarConversionOpts() { return false; }
static bool tryEmitAsConstant() { return false; }
static bool constructABIArgDirectExtend() { return false; }
};

} // namespace cir
Expand Down
8 changes: 8 additions & 0 deletions clang/lib/CIR/CodeGen/Address.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
28 changes: 28 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenCall.h
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion clang/lib/CIR/CodeGen/CIRGenDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
76 changes: 69 additions & 7 deletions clang/lib/CIR/CodeGen/CIRGenFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down Expand Up @@ -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()));

Expand All @@ -149,16 +153,49 @@ 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");

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())) {
const VarDecl *paramVar = std::get<0>(nameValue);
mlir::Value paramVal = std::get<1>(nameValue);
CharUnits alignment = getContext().getDeclAlign(paramVar);
mlir::Location 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) {}
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -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.
Expand Down
11 changes: 8 additions & 3 deletions clang/lib/CIR/CodeGen/CIRGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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");
Expand Down
33 changes: 33 additions & 0 deletions clang/lib/CIR/Dialect/IR/CIRMemorySlot.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
20 changes: 20 additions & 0 deletions clang/test/CIR/CodeGen/basic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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>