Skip to content

Commit 7023f4b

Browse files
committed
[MLIR] Introduce std.alloca op
Introduce the alloca op for stack memory allocation. When converting to the LLVM dialect, this is lowered to an llvm.alloca. Refactor the std to llvm conversion for alloc op to reuse with alloca. Drop useAlloca option with alloc op lowering. Differential Revision: https://reviews.llvm.org/D76602
1 parent 6aabb10 commit 7023f4b

File tree

9 files changed

+467
-240
lines changed

9 files changed

+467
-240
lines changed

mlir/include/mlir/Conversion/Passes.td

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,8 +225,6 @@ def ConvertStandardToLLVM : Pass<"convert-std-to-llvm"> {
225225
}];
226226
let constructor = "mlir::createLowerToLLVMPass()";
227227
let options = [
228-
Option<"useAlloca", "use-alloca", "bool", /*default=*/"false",
229-
"Use `alloca` instead of `call @malloc` for converting std.alloc">,
230228
Option<"useBarePtrCallConv", "use-bare-ptr-memref-call-conv", "bool",
231229
/*default=*/"false",
232230
"Replace FuncOp's MemRef arguments with bare pointers to the MemRef "

mlir/include/mlir/Conversion/StandardToLLVM/ConvertStandardToLLVMPass.h

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,7 @@ class OwningRewritePatternList;
2121
/// Standard dialect to the LLVM dialect, excluding non-memory-related
2222
/// operations and FuncOp.
2323
void populateStdToLLVMMemoryConversionPatters(
24-
LLVMTypeConverter &converter, OwningRewritePatternList &patterns,
25-
bool useAlloca);
24+
LLVMTypeConverter &converter, OwningRewritePatternList &patterns);
2625

2726
/// Collect a set of patterns to convert from the Standard dialect to the LLVM
2827
/// dialect, excluding the memory-related operations.
@@ -38,33 +37,34 @@ void populateStdToLLVMDefaultFuncOpConversionPattern(
3837
bool emitCWrappers = false);
3938

4039
/// Collect a set of default patterns to convert from the Standard dialect to
41-
/// LLVM. If `useAlloca` is set, the patterns for AllocOp and DeallocOp will
42-
/// generate `llvm.alloca` instead of calls to "malloc".
40+
/// LLVM.
4341
void populateStdToLLVMConversionPatterns(LLVMTypeConverter &converter,
4442
OwningRewritePatternList &patterns,
45-
bool useAlloca = false,
4643
bool emitCWrappers = false);
4744

4845
/// Collect a set of patterns to convert from the Standard dialect to
4946
/// LLVM using the bare pointer calling convention for MemRef function
50-
/// arguments. If `useAlloca` is set, the patterns for AllocOp and DeallocOp
51-
/// will generate `llvm.alloca` instead of calls to "malloc".
47+
/// arguments.
5248
void populateStdToLLVMBarePtrConversionPatterns(
53-
LLVMTypeConverter &converter, OwningRewritePatternList &patterns,
54-
bool useAlloca = false);
49+
LLVMTypeConverter &converter, OwningRewritePatternList &patterns);
5550

5651
/// Value to pass as bitwidth for the index type when the converter is expected
5752
/// to derive the bitwidth from the LLVM data layout.
5853
static constexpr unsigned kDeriveIndexBitwidthFromDataLayout = 0;
5954

55+
struct LowerToLLVMOptions {
56+
bool useBarePtrCallConv = false;
57+
bool emitCWrappers = false;
58+
unsigned indexBitwidth = kDeriveIndexBitwidthFromDataLayout;
59+
};
60+
6061
/// Creates a pass to convert the Standard dialect into the LLVMIR dialect.
61-
/// By default stdlib malloc/free are used for allocating MemRef payloads.
62-
/// Specifying `useAlloca-true` emits stack allocations instead. In the future
63-
/// this may become an enum when we have concrete uses for other options.
62+
/// stdlib malloc/free is used for allocating memrefs allocated with std.alloc,
63+
/// while LLVM's alloca is used for those allocated with std.alloca.
6464
std::unique_ptr<OpPassBase<ModuleOp>> createLowerToLLVMPass(
65-
bool useAlloca = false, bool useBarePtrCallConv = false,
66-
bool emitCWrappers = false,
67-
unsigned indexBitwidth = kDeriveIndexBitwidthFromDataLayout);
65+
const LowerToLLVMOptions &options = {
66+
/*useBarePtrCallConv=*/false, /*emitCWrappers=*/false,
67+
/*indexBitwidth=*/kDeriveIndexBitwidthFromDataLayout});
6868

6969
} // namespace mlir
7070

mlir/include/mlir/Dialect/StandardOps/IR/Ops.td

Lines changed: 88 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,56 @@ class FloatArithmeticOp<string mnemonic, list<OpTrait> traits = []> :
124124
ArithmeticOp<mnemonic, traits>,
125125
Arguments<(ins FloatLike:$lhs, FloatLike:$rhs)>;
126126

127+
// Base class for memref allocating ops: alloca and alloc.
128+
//
129+
// %0 = alloclike(%m)[%s] : memref<8x?xf32, (d0, d1)[s0] -> ((d0 + s0), d1)>
130+
//
131+
class AllocLikeOp<string mnemonic, list<OpTrait> traits = []> :
132+
Std_Op<mnemonic, traits> {
133+
134+
let arguments = (ins Variadic<Index>:$value,
135+
Confined<OptionalAttr<I64Attr>, [IntMinValue<0>]>:$alignment);
136+
let results = (outs AnyMemRef);
137+
138+
let builders = [OpBuilder<
139+
"Builder *builder, OperationState &result, MemRefType memrefType", [{
140+
result.types.push_back(memrefType);
141+
}]>,
142+
OpBuilder<
143+
"Builder *builder, OperationState &result, MemRefType memrefType, " #
144+
"ValueRange operands, IntegerAttr alignment = IntegerAttr()", [{
145+
result.addOperands(operands);
146+
result.types.push_back(memrefType);
147+
if (alignment)
148+
result.addAttribute(getAlignmentAttrName(), alignment);
149+
}]>];
150+
151+
let extraClassDeclaration = [{
152+
static StringRef getAlignmentAttrName() { return "alignment"; }
153+
154+
MemRefType getType() { return getResult().getType().cast<MemRefType>(); }
155+
156+
/// Returns the number of symbolic operands (the ones in square brackets),
157+
/// which bind to the symbols of the memref's layout map.
158+
unsigned getNumSymbolicOperands() {
159+
return getNumOperands() - getType().getNumDynamicDims();
160+
}
161+
162+
/// Returns the symbolic operands (the ones in square brackets), which bind
163+
/// to the symbols of the memref's layout map.
164+
operand_range getSymbolicOperands() {
165+
return {operand_begin() + getType().getNumDynamicDims(), operand_end()};
166+
}
167+
168+
/// Returns the dynamic sizes for this alloc operation if specified.
169+
operand_range getDynamicSizes() { return getOperands(); }
170+
}];
171+
172+
let parser = [{ return ::parseAllocLikeOp(parser, result); }];
173+
174+
let hasCanonicalizer = 1;
175+
}
176+
127177
//===----------------------------------------------------------------------===//
128178
// AbsFOp
129179
//===----------------------------------------------------------------------===//
@@ -225,7 +275,7 @@ def AddIOp : IntArithmeticOp<"addi", [Commutative]> {
225275
// AllocOp
226276
//===----------------------------------------------------------------------===//
227277

228-
def AllocOp : Std_Op<"alloc"> {
278+
def AllocOp : AllocLikeOp<"alloc"> {
229279
let summary = "memory allocation operation";
230280
let description = [{
231281
The `alloc` operation allocates a region of memory, as specified by its
@@ -234,23 +284,24 @@ def AllocOp : Std_Op<"alloc"> {
234284
Example:
235285

236286
```mlir
237-
%0 = alloc() : memref<8x64xf32, (d0, d1) -> (d0, d1), 1>
287+
%0 = alloc() : memref<8x64xf32, 1>
238288
```
239289

240290
The optional list of dimension operands are bound to the dynamic dimensions
241291
specified in its memref type. In the example below, the ssa value '%d' is
242292
bound to the second dimension of the memref (which is dynamic).
243293

244294
```mlir
245-
%0 = alloc(%d) : memref<8x?xf32, (d0, d1) -> (d0, d1), 1>
295+
%0 = alloc(%d) : memref<8x?xf32, 1>
246296
```
247297

248298
The optional list of symbol operands are bound to the symbols of the
249299
memrefs affine map. In the example below, the ssa value '%s' is bound to
250300
the symbol 's0' in the affine map specified in the allocs memref type.
251301

252302
```mlir
253-
%0 = alloc()[%s] : memref<8x64xf32, (d0, d1)[s0] -> ((d0 + s0), d1), 1>
303+
%0 = alloc()[%s] : memref<8x64xf32,
304+
affine_map<(d0, d1)[s0] -> ((d0 + s0), d1)>, 1>
254305
```
255306

256307
This operation returns a single ssa value of memref type, which can be used
@@ -262,49 +313,49 @@ def AllocOp : Std_Op<"alloc"> {
262313

263314
```mlir
264315
%0 = alloc()[%s] {alignment = 8} :
265-
memref<8x64xf32, (d0, d1)[s0] -> ((d0 + s0), d1), 1>
316+
memref<8x64xf32, affine_map<(d0, d1)[s0] -> ((d0 + s0), d1)>, 1>
266317
```
267318
}];
319+
}
268320

269-
let arguments = (ins Variadic<Index>:$value,
270-
Confined<OptionalAttr<I64Attr>, [IntMinValue<0>]>:$alignment);
271-
let results = (outs AnyMemRef);
321+
//===----------------------------------------------------------------------===//
322+
// AllocaOp
323+
//===----------------------------------------------------------------------===//
272324

273-
let builders = [OpBuilder<
274-
"Builder *builder, OperationState &result, MemRefType memrefType", [{
275-
result.types.push_back(memrefType);
276-
}]>,
277-
OpBuilder<
278-
"Builder *builder, OperationState &result, MemRefType memrefType, " #
279-
"ArrayRef<Value> operands, IntegerAttr alignment = IntegerAttr()", [{
280-
result.addOperands(operands);
281-
result.types.push_back(memrefType);
282-
if (alignment)
283-
result.addAttribute(getAlignmentAttrName(), alignment);
284-
}]>];
325+
def AllocaOp : AllocLikeOp<"alloca"> {
326+
let summary = "stack memory allocation operation";
327+
let description = [{
328+
The `alloca` operation allocates memory on the stack, to be automatically
329+
released when the stack frame is discarded. The amount of memory allocated
330+
is specified by its memref and additional operands. For example:
285331

286-
let extraClassDeclaration = [{
287-
static StringRef getAlignmentAttrName() { return "alignment"; }
332+
```mlir
333+
%0 = alloca() : memref<8x64xf32>
334+
```
288335

289-
MemRefType getType() { return getResult().getType().cast<MemRefType>(); }
336+
The optional list of dimension operands are bound to the dynamic dimensions
337+
specified in its memref type. In the example below, the SSA value '%d' is
338+
bound to the second dimension of the memref (which is dynamic).
290339

291-
/// Returns the number of symbolic operands (the ones in square brackets),
292-
/// which bind to the symbols of the memref's layout map.
293-
unsigned getNumSymbolicOperands() {
294-
return getNumOperands() - getType().getNumDynamicDims();
295-
}
340+
```mlir
341+
%0 = alloca(%d) : memref<8x?xf32>
342+
```
296343

297-
/// Returns the symbolic operands (the ones in square brackets), which bind
298-
/// to the symbols of the memref's layout map.
299-
operand_range getSymbolicOperands() {
300-
return {operand_begin() + getType().getNumDynamicDims(), operand_end()};
301-
}
344+
The optional list of symbol operands are bound to the symbols of the
345+
memref's affine map. In the example below, the SSA value '%s' is bound to
346+
the symbol 's0' in the affine map specified in the allocs memref type.
302347

303-
/// Returns the dynamic sizes for this alloc operation if specified.
304-
operand_range getDynamicSizes() { return getOperands(); }
305-
}];
348+
```mlir
349+
%0 = alloca()[%s] : memref<8x64xf32,
350+
affine_map<(d0, d1)[s0] -> ((d0 + s0), d1)>>
351+
```
306352

307-
let hasCanonicalizer = 1;
353+
This operation returns a single SSA value of memref type, which can be used
354+
by subsequent load and store operations. An optional alignment attribute, if
355+
specified, guarantees alignment at least to that boundary. If not specified,
356+
an alignment on any convenient boundary compatible with the type will be
357+
chosen.
358+
}];
308359
}
309360

310361
//===----------------------------------------------------------------------===//

0 commit comments

Comments
 (0)