Skip to content

Commit 235ef9e

Browse files
committed
[CIR] Upstream basic alloca and load support
This change implements basic support in ClangIR for local variables using the cir.alloca and cir.load operations.
1 parent 1b39328 commit 235ef9e

File tree

18 files changed

+903
-1
lines changed

18 files changed

+903
-1
lines changed

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

Lines changed: 44 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,49 @@ 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+
mlir::Value dynAllocSize) {
60+
return create<cir::AllocaOp>(loc, addrType, type, name, alignment,
61+
dynAllocSize);
62+
}
63+
64+
cir::LoadOp createLoad(mlir::Location loc, mlir::Value ptr,
65+
bool isVolatile = false, uint64_t alignment = 0) {
66+
mlir::IntegerAttr intAttr;
67+
if (alignment)
68+
intAttr = mlir::IntegerAttr::get(
69+
mlir::IntegerType::get(ptr.getContext(), 64), alignment);
70+
71+
return create<cir::LoadOp>(loc, ptr);
72+
}
73+
74+
//
75+
// Block handling helpers
76+
// ----------------------
77+
//
78+
static OpBuilder::InsertPoint getBestAllocaInsertPoint(mlir::Block *block) {
79+
auto last =
80+
std::find_if(block->rbegin(), block->rend(), [](mlir::Operation &op) {
81+
// TODO: Add LabelOp missing feature here
82+
return mlir::isa<cir::AllocaOp>(&op);
83+
});
84+
85+
if (last != block->rend())
86+
return OpBuilder::InsertPoint(block, ++mlir::Block::iterator(&*last));
87+
return OpBuilder::InsertPoint(block, block->begin());
88+
};
89+
90+
mlir::IntegerAttr getSizeFromCharUnits(mlir::MLIRContext *ctx,
91+
clang::CharUnits size) {
92+
// Note that mlir::IntegerType is used instead of cir::IntType here
93+
// because we don't need sign information for this to be useful, so keep
94+
// it simple.
95+
return mlir::IntegerAttr::get(mlir::IntegerType::get(ctx, 64),
96+
size.getQuantity());
97+
}
5498
};
5599

56100
} // 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: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,149 @@ 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 `init` attribute indicates that the local variable represented
143+
by this alloca was originally initialized in C/C++ source code. In such
144+
cases, the first use contains the initialization (a cir.store, a cir.call
145+
to a ctor, etc).
146+
147+
The presence of the `const` attribute indicates that the local variable is
148+
declared with C/C++ `const` keyword.
149+
150+
The `dynAllocSize` specifies the size to dynamically allocate on the stack
151+
and ignores the allocation size based on the original type. This is useful
152+
when handling VLAs and is omitted when declaring regular local variables.
153+
154+
The result type is a pointer to the input's type.
155+
156+
Example:
157+
158+
```mlir
159+
// int count = 3;
160+
%0 = cir.alloca i32, !cir.ptr<i32>, ["count", init] {alignment = 4 : i64}
161+
162+
// int *ptr;
163+
%1 = cir.alloca !cir.ptr<i32>, !cir.ptr<!cir.ptr<i32>>, ["ptr"] {alignment = 8 : i64}
164+
...
165+
```
166+
}];
167+
168+
let arguments = (ins
169+
Optional<PrimitiveInt>:$dynAllocSize,
170+
TypeAttr:$allocaType,
171+
StrAttr:$name,
172+
UnitAttr:$init,
173+
UnitAttr:$constant,
174+
ConfinedAttr<OptionalAttr<I64Attr>, [IntMinValue<0>]>:$alignment,
175+
OptionalAttr<ArrayAttr>:$annotations
176+
);
177+
178+
let results = (outs Res<CIR_PointerType, "",
179+
[MemAlloc<AutomaticAllocationScopeResource>]>:$addr);
180+
181+
let skipDefaultBuilders = 1;
182+
let builders = [
183+
OpBuilder<(ins "mlir::Type":$addr, "mlir::Type":$allocaType,
184+
"llvm::StringRef":$name,
185+
"mlir::IntegerAttr":$alignment)>,
186+
187+
OpBuilder<(ins "mlir::Type":$addr,
188+
"mlir::Type":$allocaType,
189+
"llvm::StringRef":$name,
190+
"mlir::IntegerAttr":$alignment,
191+
"mlir::Value":$dynAllocSize),
192+
[{
193+
if (dynAllocSize)
194+
$_state.addOperands(dynAllocSize);
195+
build($_builder, $_state, addr, allocaType, name, alignment);
196+
}]>
197+
];
198+
199+
let extraClassDeclaration = [{
200+
// Whether the alloca input type is a pointer.
201+
bool isPointerType() { return ::mlir::isa<::cir::PointerType>(getAllocaType()); }
202+
203+
bool isDynamic() { return (bool)getDynAllocSize(); }
204+
}];
205+
206+
let assemblyFormat = [{
207+
$allocaType `,` qualified(type($addr)) `,`
208+
($dynAllocSize^ `:` type($dynAllocSize) `,`)?
209+
`[` $name
210+
(`,` `init` $init^)?
211+
(`,` `const` $constant^)?
212+
`]`
213+
($annotations^)? attr-dict
214+
}];
215+
216+
let hasVerifier = 0;
217+
}
218+
219+
//===----------------------------------------------------------------------===//
220+
// LoadOp
221+
//===----------------------------------------------------------------------===//
222+
223+
def LoadOp : CIR_Op<"load", [
224+
TypesMatchWith<"type of 'result' matches pointee type of 'addr'",
225+
"addr", "result",
226+
"cast<PointerType>($_self).getPointee()">,
227+
DeclareOpInterfaceMethods<PromotableMemOpInterface>]> {
228+
229+
let summary = "Load value from memory adddress";
230+
let description = [{
231+
`cir.load` reads a value (lvalue to rvalue conversion) given an address
232+
backed up by a `cir.ptr` type. A unit attribute `deref` can be used to
233+
mark the resulting value as used by another operation to dereference
234+
a pointer. A unit attribute `volatile` can be used to indicate a volatile
235+
loading. Load can be marked atomic by using `atomic(<mem_order>)`.
236+
237+
`align` can be used to specify an alignment that's different from the
238+
default, which is computed from `result`'s type ABI data layout.
239+
240+
Example:
241+
242+
```mlir
243+
244+
// Read from local variable, address in %0.
245+
%1 = cir.load %0 : !cir.ptr<i32>, i32
246+
```
247+
}];
248+
249+
let arguments = (ins Arg<CIR_PointerType, "the address to load from",
250+
[MemRead]>:$addr
251+
);
252+
let results = (outs CIR_AnyType:$result);
253+
254+
let assemblyFormat = [{
255+
$addr `:` qualified(type($addr)) `,` type($result) attr-dict
256+
}];
257+
258+
// FIXME: add verifier.
259+
}
260+
118261
//===----------------------------------------------------------------------===//
119262
// ReturnOp
120263
//===----------------------------------------------------------------------===//

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,24 @@ struct MissingFeatures {
3636
static bool opGlobalConstant() { return false; }
3737
static bool opGlobalAlignment() { return false; }
3838
static bool opGlobalLinkage() { return false; }
39+
40+
// Load attributes
41+
static bool opLoadThreadLocal() { return false; }
42+
static bool opLoadEmitScalarRangeCheck() { return false; }
43+
static bool opLoadBooleanRepresentation() { return false; }
44+
45+
// AllocaOp handling
46+
static bool opAllocaVarDeclContext() { return false; }
47+
static bool opAllocaStaticLocal() { return false; }
48+
static bool opAllocaNonGC() { return false; }
49+
static bool opAllocaImpreciseLifetime() { return false; }
50+
static bool opAllocaTLS() { return false; }
51+
static bool opAllocaOpenMPThreadPrivate() { return false; }
52+
static bool opAllocaEscapeByReference() { return false; }
53+
static bool opAllocaReference() { return false; }
54+
55+
// Options for casts
56+
static bool scalarConversionOpts() { return false; }
3957
};
4058

4159
} // 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 LLVM_CLANG_LIB_CIR_ADDRESS_H
15+
#define LLVM_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 // LLVM_CLANG_LIB_CIR_ADDRESS_H

0 commit comments

Comments
 (0)