Skip to content

Commit 56cc929

Browse files
authored
[CIR] Upstream func args alloca handling (#129167)
This change adds support for collecting function arguments and storing them in alloca memory slots.
1 parent a1fdcfa commit 56cc929

File tree

10 files changed

+212
-11
lines changed

10 files changed

+212
-11
lines changed

clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,11 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
6969
return create<cir::LoadOp>(loc, ptr);
7070
}
7171

72+
cir::StoreOp createStore(mlir::Location loc, mlir::Value val,
73+
mlir::Value dst) {
74+
return create<cir::StoreOp>(loc, val, dst);
75+
}
76+
7277
//
7378
// Block handling helpers
7479
// ----------------------

clang/include/clang/CIR/Dialect/IR/CIROps.td

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,45 @@ def LoadOp : CIR_Op<"load", [
228228
// FIXME: add verifier.
229229
}
230230

231+
//===----------------------------------------------------------------------===//
232+
// StoreOp
233+
//===----------------------------------------------------------------------===//
234+
235+
def StoreOp : CIR_Op<"store", [
236+
TypesMatchWith<"type of 'value' matches pointee type of 'addr'",
237+
"addr", "value",
238+
"cast<PointerType>($_self).getPointee()">,
239+
DeclareOpInterfaceMethods<PromotableMemOpInterface>]> {
240+
241+
let summary = "Store value to memory address";
242+
let description = [{
243+
`cir.store` stores a value (first operand) to the memory address specified
244+
in the second operand. A unit attribute `volatile` can be used to indicate
245+
a volatile store. Store's can be marked atomic by using
246+
`atomic(<mem_order>)`.
247+
248+
`align` can be used to specify an alignment that's different from the
249+
default, which is computed from `result`'s type ABI data layout.
250+
251+
Example:
252+
253+
```mlir
254+
// Store a function argument to local storage, address in %0.
255+
cir.store %arg0, %0 : i32, !cir.ptr<i32>
256+
```
257+
}];
258+
259+
let arguments = (ins CIR_AnyType:$value,
260+
Arg<CIR_PointerType, "the address to store the value",
261+
[MemWrite]>:$addr);
262+
263+
let assemblyFormat = [{
264+
$value `,` $addr attr-dict `:` type($value) `,` qualified(type($addr))
265+
}];
266+
267+
// FIXME: add verifier.
268+
}
269+
231270
//===----------------------------------------------------------------------===//
232271
// ReturnOp
233272
//===----------------------------------------------------------------------===//

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ struct MissingFeatures {
5959
// Misc
6060
static bool scalarConversionOpts() { return false; }
6161
static bool tryEmitAsConstant() { return false; }
62+
static bool constructABIArgDirectExtend() { return false; }
6263
};
6364

6465
} // namespace cir

clang/lib/CIR/CodeGen/Address.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,14 @@ class Address {
5252
elementType);
5353
}
5454

55+
Address(mlir::Value pointer, clang::CharUnits alignment)
56+
: Address(pointer,
57+
mlir::cast<cir::PointerType>(pointer.getType()).getPointee(),
58+
alignment) {
59+
assert((!alignment.isZero() || pointer == nullptr) &&
60+
"creating valid address with invalid alignment");
61+
}
62+
5563
static Address invalid() { return Address(nullptr); }
5664
bool isValid() const {
5765
return pointerAndKnownNonNull.getPointer() != nullptr;

clang/lib/CIR/CodeGen/CIRGenCall.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// These classes wrap the information about a call or function
10+
// definition used to handle ABI compliancy.
11+
//
12+
//===----------------------------------------------------------------------===//
13+
14+
#ifndef CLANG_LIB_CODEGEN_CIRGENCALL_H
15+
#define CLANG_LIB_CODEGEN_CIRGENCALL_H
16+
17+
#include "clang/AST/GlobalDecl.h"
18+
#include "llvm/ADT/SmallVector.h"
19+
20+
namespace clang::CIRGen {
21+
22+
/// Type for representing both the decl and type of parameters to a function.
23+
/// The decl must be either a ParmVarDecl or ImplicitParamDecl.
24+
class FunctionArgList : public llvm::SmallVector<const clang::VarDecl *, 16> {};
25+
26+
} // namespace clang::CIRGen
27+
28+
#endif // CLANG_LIB_CODEGEN_CIRGENCALL_H

clang/lib/CIR/CodeGen/CIRGenDecl.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ void CIRGenFunction::emitAutoVarAlloca(const VarDecl &d) {
4444
mlir::Type allocaTy = convertTypeForMem(ty);
4545
// Create the temp alloca and declare variable using it.
4646
address = createTempAlloca(allocaTy, alignment, loc, d.getName());
47-
declare(address, &d, ty, getLoc(d.getSourceRange()), alignment);
47+
declare(address.getPointer(), &d, ty, getLoc(d.getSourceRange()), alignment);
4848

4949
setAddrOfLocalVar(&d, address);
5050
}

clang/lib/CIR/CodeGen/CIRGenFunction.cpp

Lines changed: 69 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212

1313
#include "CIRGenFunction.h"
1414

15+
#include "CIRGenCall.h"
16+
#include "mlir/IR/Location.h"
1517
#include "clang/AST/GlobalDecl.h"
1618
#include "clang/CIR/MissingFeatures.h"
1719

@@ -132,15 +134,17 @@ mlir::Location CIRGenFunction::getLoc(mlir::Location lhs, mlir::Location rhs) {
132134
return mlir::FusedLoc::get(locs, metadata, &getMLIRContext());
133135
}
134136

135-
mlir::LogicalResult CIRGenFunction::declare(Address addr, const Decl *var,
136-
QualType ty, mlir::Location loc,
137-
CharUnits alignment) {
137+
mlir::LogicalResult CIRGenFunction::declare(mlir::Value addrVal,
138+
const Decl *var, QualType ty,
139+
mlir::Location loc,
140+
CharUnits alignment, bool isParam) {
138141
const auto *namedVar = dyn_cast_or_null<NamedDecl>(var);
139142
assert(namedVar && "Needs a named decl");
140143
assert(!cir::MissingFeatures::cgfSymbolTable());
141144

142-
mlir::Value addrVal = addr.getPointer();
143145
auto allocaOp = cast<cir::AllocaOp>(addrVal.getDefiningOp());
146+
if (isParam)
147+
allocaOp.setInitAttr(mlir::UnitAttr::get(&getMLIRContext()));
144148
if (ty->isReferenceType() || ty.isConstQualified())
145149
allocaOp.setConstantAttr(mlir::UnitAttr::get(&getMLIRContext()));
146150

@@ -149,16 +153,49 @@ mlir::LogicalResult CIRGenFunction::declare(Address addr, const Decl *var,
149153

150154
void CIRGenFunction::startFunction(GlobalDecl gd, QualType returnType,
151155
cir::FuncOp fn, cir::FuncType funcType,
152-
SourceLocation loc,
156+
FunctionArgList args, SourceLocation loc,
153157
SourceLocation startLoc) {
154158
assert(!curFn &&
155159
"CIRGenFunction can only be used for one function at a time");
156160

157161
fnRetTy = returnType;
158162
curFn = fn;
159163

164+
const auto *fd = dyn_cast_or_null<FunctionDecl>(gd.getDecl());
165+
160166
mlir::Block *entryBB = &fn.getBlocks().front();
161167
builder.setInsertionPointToStart(entryBB);
168+
169+
// TODO(cir): this should live in `emitFunctionProlog
170+
// Declare all the function arguments in the symbol table.
171+
for (const auto nameValue : llvm::zip(args, entryBB->getArguments())) {
172+
const VarDecl *paramVar = std::get<0>(nameValue);
173+
mlir::Value paramVal = std::get<1>(nameValue);
174+
CharUnits alignment = getContext().getDeclAlign(paramVar);
175+
mlir::Location paramLoc = getLoc(paramVar->getSourceRange());
176+
paramVal.setLoc(paramLoc);
177+
178+
mlir::Value addrVal =
179+
emitAlloca(cast<NamedDecl>(paramVar)->getName(),
180+
convertType(paramVar->getType()), paramLoc, alignment);
181+
182+
declare(addrVal, paramVar, paramVar->getType(), paramLoc, alignment,
183+
/*isParam=*/true);
184+
185+
setAddrOfLocalVar(paramVar, Address(addrVal, alignment));
186+
187+
bool isPromoted = isa<ParmVarDecl>(paramVar) &&
188+
cast<ParmVarDecl>(paramVar)->isKNRPromoted();
189+
assert(!cir::MissingFeatures::constructABIArgDirectExtend());
190+
if (isPromoted)
191+
cgm.errorNYI(fd->getSourceRange(), "Function argument demotion");
192+
193+
// Location of the store to the param storage tracked as beginning of
194+
// the function body.
195+
mlir::Location fnBodyBegin = getLoc(fd->getBody()->getBeginLoc());
196+
builder.CIRBaseBuilderTy::createStore(fnBodyBegin, paramVal, addrVal);
197+
}
198+
assert(builder.getInsertionBlock() && "Should be valid");
162199
}
163200

164201
void CIRGenFunction::finishFunction(SourceLocation endLoc) {}
@@ -187,8 +224,10 @@ cir::FuncOp CIRGenFunction::generateCode(clang::GlobalDecl gd, cir::FuncOp fn,
187224
// This will be used once more code is upstreamed.
188225
[[maybe_unused]] mlir::Block *entryBB = fn.addEntryBlock();
189226

190-
startFunction(gd, funcDecl->getReturnType(), fn, funcType, loc,
191-
bodyRange.getBegin());
227+
FunctionArgList args;
228+
QualType retTy = buildFunctionArgList(gd, args);
229+
230+
startFunction(gd, retTy, fn, funcType, args, loc, bodyRange.getBegin());
192231

193232
if (isa<CXXDestructorDecl>(funcDecl))
194233
getCIRGenModule().errorNYI(bodyRange, "C++ destructor definition");
@@ -234,6 +273,29 @@ cir::FuncOp CIRGenFunction::generateCode(clang::GlobalDecl gd, cir::FuncOp fn,
234273
return fn;
235274
}
236275

276+
clang::QualType CIRGenFunction::buildFunctionArgList(clang::GlobalDecl gd,
277+
FunctionArgList &args) {
278+
const auto *fd = cast<FunctionDecl>(gd.getDecl());
279+
QualType retTy = fd->getReturnType();
280+
281+
const auto *md = dyn_cast<CXXMethodDecl>(fd);
282+
if (md && md->isInstance())
283+
cgm.errorNYI(fd->getSourceRange(), "buildFunctionArgList: CXXMethodDecl");
284+
285+
if (isa<CXXConstructorDecl>(fd))
286+
cgm.errorNYI(fd->getSourceRange(),
287+
"buildFunctionArgList: CXXConstructorDecl");
288+
289+
for (auto *param : fd->parameters())
290+
args.push_back(param);
291+
292+
if (md && (isa<CXXConstructorDecl>(md) || isa<CXXDestructorDecl>(md)))
293+
cgm.errorNYI(fd->getSourceRange(),
294+
"buildFunctionArgList: implicit structor params");
295+
296+
return retTy;
297+
}
298+
237299
/// Emit code to compute a designator that specifies the location
238300
/// of the expression.
239301
/// FIXME: document this function better.

clang/lib/CIR/CodeGen/CIRGenFunction.h

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#define CLANG_LIB_CIR_CODEGEN_CIRGENFUNCTION_H
1515

1616
#include "CIRGenBuilder.h"
17+
#include "CIRGenCall.h"
1718
#include "CIRGenModule.h"
1819
#include "CIRGenTypeCache.h"
1920
#include "CIRGenValue.h"
@@ -96,9 +97,9 @@ class CIRGenFunction : public CIRGenTypeCache {
9697
private:
9798
/// Declare a variable in the current scope, return success if the variable
9899
/// wasn't declared yet.
99-
mlir::LogicalResult declare(Address addr, const clang::Decl *var,
100+
mlir::LogicalResult declare(mlir::Value addrVal, const clang::Decl *var,
100101
clang::QualType ty, mlir::Location loc,
101-
clang::CharUnits alignment);
102+
clang::CharUnits alignment, bool isParam = false);
102103

103104
public:
104105
mlir::Value emitAlloca(llvm::StringRef name, mlir::Type ty,
@@ -196,12 +197,16 @@ class CIRGenFunction : public CIRGenTypeCache {
196197
cir::FuncOp generateCode(clang::GlobalDecl gd, cir::FuncOp fn,
197198
cir::FuncType funcType);
198199

200+
clang::QualType buildFunctionArgList(clang::GlobalDecl gd,
201+
FunctionArgList &args);
202+
199203
/// Emit code for the start of a function.
200204
/// \param loc The location to be associated with the function.
201205
/// \param startLoc The location of the function body.
202206
void startFunction(clang::GlobalDecl gd, clang::QualType retTy,
203207
cir::FuncOp fn, cir::FuncType funcType,
204-
clang::SourceLocation loc, clang::SourceLocation startLoc);
208+
FunctionArgList args, clang::SourceLocation loc,
209+
clang::SourceLocation startLoc);
205210

206211
Address createTempAlloca(mlir::Type ty, CharUnits align, mlir::Location loc,
207212
const Twine &name = "tmp");

clang/lib/CIR/Dialect/IR/CIRMemorySlot.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,3 +75,36 @@ DeletionKind cir::LoadOp::removeBlockingUses(
7575
getResult().replaceAllUsesWith(reachingDefinition);
7676
return DeletionKind::Delete;
7777
}
78+
79+
//===----------------------------------------------------------------------===//
80+
// Interfaces for StoreOp
81+
//===----------------------------------------------------------------------===//
82+
83+
bool cir::StoreOp::loadsFrom(const MemorySlot &slot) { return false; }
84+
85+
bool cir::StoreOp::storesTo(const MemorySlot &slot) {
86+
return getAddr() == slot.ptr;
87+
}
88+
89+
Value cir::StoreOp::getStored(const MemorySlot &slot, OpBuilder &builder,
90+
Value reachingDef, const DataLayout &dataLayout) {
91+
return getValue();
92+
}
93+
94+
bool cir::StoreOp::canUsesBeRemoved(
95+
const MemorySlot &slot, const SmallPtrSetImpl<OpOperand *> &blockingUses,
96+
SmallVectorImpl<OpOperand *> &newBlockingUses,
97+
const DataLayout &dataLayout) {
98+
if (blockingUses.size() != 1)
99+
return false;
100+
Value blockingUse = (*blockingUses.begin())->get();
101+
return blockingUse == slot.ptr && getAddr() == slot.ptr &&
102+
getValue() != slot.ptr && slot.elemType == getValue().getType();
103+
}
104+
105+
DeletionKind cir::StoreOp::removeBlockingUses(
106+
const MemorySlot &slot, const SmallPtrSetImpl<OpOperand *> &blockingUses,
107+
OpBuilder &builder, Value reachingDefinition,
108+
const DataLayout &dataLayout) {
109+
return DeletionKind::Delete;
110+
}

clang/test/CIR/CodeGen/basic.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,23 @@ int f2() {
2525
// CHECK: %[[I_PTR:.*]] = cir.alloca !cir.int<s, 32>, !cir.ptr<!cir.int<s, 32>>, ["i", const] {alignment = 4 : i64}
2626
// CHECK: %[[I:.*]] = cir.load %[[I_PTR]] : !cir.ptr<!cir.int<s, 32>>, !cir.int<s, 32>
2727
// CHECK: cir.return %[[I]] : !cir.int<s, 32>
28+
29+
int f3(int i) {
30+
return i;
31+
}
32+
33+
// CHECK: cir.func @f3(%[[ARG:.*]]: !cir.int<s, 32> loc({{.*}})) -> !cir.int<s, 32>
34+
// CHECK: %[[ARG_ALLOCA:.*]] = cir.alloca !cir.int<s, 32>, !cir.ptr<!cir.int<s, 32>>, ["i", init] {alignment = 4 : i64}
35+
// CHECK: cir.store %[[ARG]], %[[ARG_ALLOCA]] : !cir.int<s, 32>, !cir.ptr<!cir.int<s, 32>>
36+
// CHECK: %[[ARG_VAL:.*]] = cir.load %[[ARG_ALLOCA]] : !cir.ptr<!cir.int<s, 32>>, !cir.int<s, 32>
37+
// CHECK: cir.return %[[ARG_VAL]] : !cir.int<s, 32>
38+
39+
int f4(const int i) {
40+
return i;
41+
}
42+
43+
// CHECK: cir.func @f4(%[[ARG:.*]]: !cir.int<s, 32> loc({{.*}})) -> !cir.int<s, 32>
44+
// CHECK: %[[ARG_ALLOCA:.*]] = cir.alloca !cir.int<s, 32>, !cir.ptr<!cir.int<s, 32>>, ["i", init, const] {alignment = 4 : i64}
45+
// CHECK: cir.store %[[ARG]], %[[ARG_ALLOCA]] : !cir.int<s, 32>, !cir.ptr<!cir.int<s, 32>>
46+
// CHECK: %[[ARG_VAL:.*]] = cir.load %[[ARG_ALLOCA]] : !cir.ptr<!cir.int<s, 32>>, !cir.int<s, 32>
47+
// CHECK: cir.return %[[ARG_VAL]] : !cir.int<s, 32>

0 commit comments

Comments
 (0)