Skip to content

Commit 3989b78

Browse files
authored
[CIR] Upstream basic alloca and load support (#128792)
This change implements basic support in ClangIR for local variables using the cir.alloca and cir.load operations.
1 parent 72e00d6 commit 3989b78

File tree

18 files changed

+945
-1
lines changed

18 files changed

+945
-1
lines changed

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

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#ifndef LLVM_CLANG_CIR_DIALECT_BUILDER_CIRBASEBUILDER_H
1010
#define LLVM_CLANG_CIR_DIALECT_BUILDER_CIRBASEBUILDER_H
1111

12+
#include "clang/AST/CharUnits.h"
1213
#include "clang/CIR/Dialect/IR/CIRAttrs.h"
1314
#include "clang/CIR/Dialect/IR/CIRDialect.h"
1415
#include "clang/CIR/Dialect/IR/CIRTypes.h"
@@ -51,6 +52,47 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
5152
return cir::ConstPtrAttr::get(
5253
getContext(), mlir::cast<cir::PointerType>(type), valueAttr);
5354
}
55+
56+
mlir::Value createAlloca(mlir::Location loc, cir::PointerType addrType,
57+
mlir::Type type, llvm::StringRef name,
58+
mlir::IntegerAttr alignment) {
59+
return create<cir::AllocaOp>(loc, addrType, type, name, alignment);
60+
}
61+
62+
cir::LoadOp createLoad(mlir::Location loc, mlir::Value ptr,
63+
bool isVolatile = false, uint64_t alignment = 0) {
64+
mlir::IntegerAttr intAttr;
65+
if (alignment)
66+
intAttr = mlir::IntegerAttr::get(
67+
mlir::IntegerType::get(ptr.getContext(), 64), alignment);
68+
69+
return create<cir::LoadOp>(loc, ptr);
70+
}
71+
72+
//
73+
// Block handling helpers
74+
// ----------------------
75+
//
76+
static OpBuilder::InsertPoint getBestAllocaInsertPoint(mlir::Block *block) {
77+
auto last =
78+
std::find_if(block->rbegin(), block->rend(), [](mlir::Operation &op) {
79+
// TODO: Add LabelOp missing feature here
80+
return mlir::isa<cir::AllocaOp>(&op);
81+
});
82+
83+
if (last != block->rend())
84+
return OpBuilder::InsertPoint(block, ++mlir::Block::iterator(&*last));
85+
return OpBuilder::InsertPoint(block, block->begin());
86+
};
87+
88+
mlir::IntegerAttr getSizeFromCharUnits(mlir::MLIRContext *ctx,
89+
clang::CharUnits size) {
90+
// Note that mlir::IntegerType is used instead of cir::IntType here
91+
// because we don't need sign information for this to be useful, so keep
92+
// it simple.
93+
return mlir::IntegerAttr::get(mlir::IntegerType::get(ctx, 64),
94+
size.getQuantity());
95+
}
5496
};
5597

5698
} // namespace cir

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,21 @@ def CIR_BoolAttr : CIR_Attr<"Bool", "bool", [TypedAttrInterface]> {
5454
}];
5555
}
5656

57+
//===----------------------------------------------------------------------===//
58+
// UndefAttr
59+
//===----------------------------------------------------------------------===//
60+
61+
def UndefAttr : CIR_Attr<"Undef", "undef", [TypedAttrInterface]> {
62+
let summary = "Represent an undef constant";
63+
let description = [{
64+
The UndefAttr represents an undef constant, corresponding to LLVM's notion
65+
of undef.
66+
}];
67+
68+
let parameters = (ins AttributeSelfTypeParameter<"">:$type);
69+
let assemblyFormat = [{}];
70+
}
71+
5772
//===----------------------------------------------------------------------===//
5873
// IntegerAttr
5974
//===----------------------------------------------------------------------===//

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

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,119 @@ def ConstantOp : CIR_Op<"const",
115115
let hasFolder = 1;
116116
}
117117

118+
//===----------------------------------------------------------------------===//
119+
// AllocaOp
120+
//===----------------------------------------------------------------------===//
121+
122+
class AllocaTypesMatchWith<string summary, string lhsArg, string rhsArg,
123+
string transform, string comparator = "std::equal_to<>()">
124+
: PredOpTrait<summary, CPred<
125+
comparator # "(" #
126+
!subst("$_self", "$" # lhsArg # ".getType()", transform) #
127+
", $" # rhsArg # ")">> {
128+
string lhs = lhsArg;
129+
string rhs = rhsArg;
130+
string transformer = transform;
131+
}
132+
133+
def AllocaOp : CIR_Op<"alloca", [
134+
AllocaTypesMatchWith<"'allocaType' matches pointee type of 'addr'",
135+
"addr", "allocaType",
136+
"cast<PointerType>($_self).getPointee()">,
137+
DeclareOpInterfaceMethods<PromotableAllocationOpInterface>]> {
138+
let summary = "Defines a scope-local variable";
139+
let description = [{
140+
The `cir.alloca` operation defines a scope-local variable.
141+
142+
The presence of the `const` attribute indicates that the local variable is
143+
declared with C/C++ `const` keyword.
144+
145+
The result type is a pointer to the input's type.
146+
147+
Example:
148+
149+
```mlir
150+
// int count;
151+
%0 = cir.alloca i32, !cir.ptr<i32>, ["count"] {alignment = 4 : i64}
152+
153+
// int *ptr;
154+
%1 = cir.alloca !cir.ptr<i32>, !cir.ptr<!cir.ptr<i32>>, ["ptr"] {alignment = 8 : i64}
155+
...
156+
```
157+
}];
158+
159+
let arguments = (ins
160+
TypeAttr:$allocaType,
161+
StrAttr:$name,
162+
UnitAttr:$init,
163+
UnitAttr:$constant,
164+
ConfinedAttr<OptionalAttr<I64Attr>, [IntMinValue<0>]>:$alignment,
165+
OptionalAttr<ArrayAttr>:$annotations
166+
);
167+
168+
let results = (outs Res<CIR_PointerType, "",
169+
[MemAlloc<AutomaticAllocationScopeResource>]>:$addr);
170+
171+
let skipDefaultBuilders = 1;
172+
let builders = [
173+
OpBuilder<(ins "mlir::Type":$addr,
174+
"mlir::Type":$allocaType,
175+
"llvm::StringRef":$name,
176+
"mlir::IntegerAttr":$alignment)>
177+
];
178+
179+
let extraClassDeclaration = [{
180+
// Whether the alloca input type is a pointer.
181+
bool isPointerType() { return ::mlir::isa<::cir::PointerType>(getAllocaType()); }
182+
}];
183+
184+
let assemblyFormat = [{
185+
$allocaType `,` qualified(type($addr)) `,`
186+
`[` $name
187+
(`,` `init` $init^)?
188+
(`,` `const` $constant^)?
189+
`]`
190+
($annotations^)? attr-dict
191+
}];
192+
193+
let hasVerifier = 0;
194+
}
195+
196+
//===----------------------------------------------------------------------===//
197+
// LoadOp
198+
//===----------------------------------------------------------------------===//
199+
200+
def LoadOp : CIR_Op<"load", [
201+
TypesMatchWith<"type of 'result' matches pointee type of 'addr'",
202+
"addr", "result",
203+
"cast<PointerType>($_self).getPointee()">,
204+
DeclareOpInterfaceMethods<PromotableMemOpInterface>]> {
205+
206+
let summary = "Load value from memory adddress";
207+
let description = [{
208+
`cir.load` reads a value (lvalue to rvalue conversion) given an address
209+
backed up by a `cir.ptr` type.
210+
211+
Example:
212+
213+
```mlir
214+
215+
// Read from local variable, address in %0.
216+
%1 = cir.load %0 : !cir.ptr<i32>, i32
217+
```
218+
}];
219+
220+
let arguments = (ins Arg<CIR_PointerType, "the address to load from",
221+
[MemRead]>:$addr);
222+
let results = (outs CIR_AnyType:$result);
223+
224+
let assemblyFormat = [{
225+
$addr `:` qualified(type($addr)) `,` type($result) attr-dict
226+
}];
227+
228+
// FIXME: add verifier.
229+
}
230+
118231
//===----------------------------------------------------------------------===//
119232
// ReturnOp
120233
//===----------------------------------------------------------------------===//

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,35 @@ struct MissingFeatures {
3030
// This isn't needed until we add support for bools.
3131
static bool convertTypeForMemory() { return false; }
3232

33+
// CIRGenFunction implementation details
34+
static bool cgfSymbolTable() { return false; }
35+
3336
// Unhandled global/linkage information.
3437
static bool opGlobalDSOLocal() { return false; }
3538
static bool opGlobalThreadLocal() { return false; }
3639
static bool opGlobalConstant() { return false; }
3740
static bool opGlobalAlignment() { return false; }
3841
static bool opGlobalLinkage() { return false; }
42+
43+
// Load attributes
44+
static bool opLoadThreadLocal() { return false; }
45+
static bool opLoadEmitScalarRangeCheck() { return false; }
46+
static bool opLoadBooleanRepresentation() { return false; }
47+
48+
// AllocaOp handling
49+
static bool opAllocaVarDeclContext() { return false; }
50+
static bool opAllocaStaticLocal() { return false; }
51+
static bool opAllocaNonGC() { return false; }
52+
static bool opAllocaImpreciseLifetime() { return false; }
53+
static bool opAllocaPreciseLifetime() { return false; }
54+
static bool opAllocaTLS() { return false; }
55+
static bool opAllocaOpenMPThreadPrivate() { return false; }
56+
static bool opAllocaEscapeByReference() { return false; }
57+
static bool opAllocaReference() { return false; }
58+
59+
// Misc
60+
static bool scalarConversionOpts() { return false; }
61+
static bool tryEmitAsConstant() { return false; }
3962
};
4063

4164
} // namespace cir

clang/lib/CIR/CodeGen/Address.h

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
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+
// This class provides a simple wrapper for a pair of a pointer and an
10+
// alignment.
11+
//
12+
//===----------------------------------------------------------------------===//
13+
14+
#ifndef CLANG_LIB_CIR_ADDRESS_H
15+
#define CLANG_LIB_CIR_ADDRESS_H
16+
17+
#include "mlir/IR/Value.h"
18+
#include "clang/AST/CharUnits.h"
19+
#include "clang/CIR/Dialect/IR/CIRTypes.h"
20+
#include "llvm/ADT/PointerIntPair.h"
21+
22+
namespace clang::CIRGen {
23+
24+
class Address {
25+
26+
// The boolean flag indicates whether the pointer is known to be non-null.
27+
llvm::PointerIntPair<mlir::Value, 1, bool> pointerAndKnownNonNull;
28+
29+
/// The expected CIR type of the pointer. Carrying accurate element type
30+
/// information in Address makes it more convenient to work with Address
31+
/// values and allows frontend assertions to catch simple mistakes.
32+
mlir::Type elementType;
33+
34+
clang::CharUnits alignment;
35+
36+
protected:
37+
Address(std::nullptr_t) : elementType(nullptr) {}
38+
39+
public:
40+
Address(mlir::Value pointer, mlir::Type elementType,
41+
clang::CharUnits alignment)
42+
: pointerAndKnownNonNull(pointer, false), elementType(elementType),
43+
alignment(alignment) {
44+
assert(mlir::isa<cir::PointerType>(pointer.getType()) &&
45+
"Expected cir.ptr type");
46+
47+
assert(pointer && "Pointer cannot be null");
48+
assert(elementType && "Element type cannot be null");
49+
assert(!alignment.isZero() && "Alignment cannot be zero");
50+
51+
assert(mlir::cast<cir::PointerType>(pointer.getType()).getPointee() ==
52+
elementType);
53+
}
54+
55+
static Address invalid() { return Address(nullptr); }
56+
bool isValid() const {
57+
return pointerAndKnownNonNull.getPointer() != nullptr;
58+
}
59+
60+
mlir::Value getPointer() const {
61+
assert(isValid());
62+
return pointerAndKnownNonNull.getPointer();
63+
}
64+
65+
mlir::Type getElementType() const {
66+
assert(isValid());
67+
assert(mlir::cast<cir::PointerType>(
68+
pointerAndKnownNonNull.getPointer().getType())
69+
.getPointee() == elementType);
70+
return elementType;
71+
}
72+
};
73+
74+
} // namespace clang::CIRGen
75+
76+
#endif // CLANG_LIB_CIR_ADDRESS_H

0 commit comments

Comments
 (0)