MLIR 24.0.0git
ABIRewriteContext.h
Go to the documentation of this file.
1//===- ABIRewriteContext.h - Dialect-specific ABI rewriting -----*- C++ -*-===//
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 file defines ABIRewriteContext, the abstract interface for dialect-
10// specific ABI lowering rewrites. Each MLIR dialect that wants ABI lowering
11// (CIR, FIR, etc.) provides a concrete subclass.
12//
13// ABIRewriteContext consumes ABI classification results and drives the
14// creation of lowered function signatures, argument coercions, and call
15// site rewrites using dialect-specific operations.
16//
17//===----------------------------------------------------------------------===//
18
19#ifndef MLIR_ABI_ABIREWRITECONTEXT_H
20#define MLIR_ABI_ABIREWRITECONTEXT_H
21
22#include "mlir/IR/Builders.h"
23#include "mlir/IR/Operation.h"
24#include "mlir/IR/Types.h"
25#include "mlir/IR/Value.h"
27#include "llvm/ADT/STLExtras.h"
28#include "llvm/Support/Alignment.h"
29
30#include <cassert>
31
32namespace mlir {
33namespace abi {
34
35/// Classification of how a single argument or return value should be
36/// passed at the ABI level.
37///
38/// This is a dialect-agnostic representation. It mirrors the kinds
39/// found in the LLVM ABI library and in CIR's ABIArgInfo, but does
40/// not depend on either.
41enum class ArgKind : uint8_t {
42 /// Pass directly in registers, possibly coerced to a different type.
44
45 /// Like Direct, but with a sign/zero extension attribute.
47
48 /// Pass indirectly via a pointer (sret for returns, byval for args).
50
51 /// Ignore (void return, empty struct).
53
54 /// Expand an aggregate into its constituent scalar fields.
56};
57
58/// Describes how a single argument or return value is passed after ABI
59/// lowering.
62
63 /// The ABI-coerced type, if different from the original. Null means
64 /// use the original type.
65 Type coercedType = nullptr;
66
67 /// For Indirect: alignment of the pointed-to object.
68 llvm::Align indirectAlign = llvm::Align(1);
69
70 /// For Extend: whether to sign-extend (true) or zero-extend (false).
71 bool signExtend = false;
72
73 /// For Direct: whether a struct coercion can be flattened into
74 /// individual register-width arguments.
75 bool canFlatten = true;
76
77 /// For Indirect: whether the callee gets ownership (byval).
78 bool byVal = false;
79
80 /// For Direct with coercion: the byte offset within the original aggregate
81 /// at which the coerced value lives. Non-zero when the low eightbyte is
82 /// NO_CLASS and the value is carried in a later eightbyte (x86-64 SysV).
83 unsigned directOffset = 0;
84
85 /// How many integer and vector argument registers the value occupies. Both
86 /// zero means it travels in memory, which is also what a target whose
87 /// classifier does not record the demand reports.
88 unsigned neededIntRegs = 0;
89 unsigned neededSseRegs = 0;
90
91 /// Whether the value is passed as-is, so a rewriter can leave it alone.
92 /// Only an uncoerced Direct qualifies. Extend counts as needing a rewrite
93 /// even though it only adds an attribute, because the attribute changes
94 /// observable behavior.
95 bool isPassThrough() const { return kind == ArgKind::Direct && !coercedType; }
96
97 /// Whether two classifications describe the same wire format. Every field
98 /// participates, so a field added above must be added here as well.
99 bool operator==(const ArgClassification &other) const {
100 return kind == other.kind && coercedType == other.coercedType &&
101 indirectAlign == other.indirectAlign &&
102 signExtend == other.signExtend && canFlatten == other.canFlatten &&
103 byVal == other.byVal && directOffset == other.directOffset &&
104 neededIntRegs == other.neededIntRegs &&
106 }
107
109 return getDirect(/*coerced=*/nullptr, /*offset=*/0);
110 }
111
112 static ArgClassification getDirect(Type coerced, unsigned offset) {
113 // isPassThrough reads only coercedType, so an offset with no coerced
114 // type to read at it would be silently ignored.
115 assert((!offset || coerced) &&
116 "a direct offset needs a coerced type to read at it");
119 c.coercedType = coerced;
120 c.directOffset = offset;
121 return c;
122 }
123
127 return c;
128 }
129
130 static ArgClassification getIndirect(llvm::Align align, bool byVal = true) {
133 c.indirectAlign = align;
134 c.byVal = byVal;
135 return c;
136 }
137
138 static ArgClassification getExtend(Type coerced, bool signExt) {
141 c.coercedType = coerced;
142 c.signExtend = signExt;
143 return c;
144 }
145};
146
147/// Holds the full ABI classification for a function: return type and
148/// all arguments.
152
153 /// Whether the classified return type was the source language's void.
154 ///
155 /// A void return classifies as Ignore, and so does a return the ABI drops,
156 /// such as an empty record. The two need opposite treatment: void is
157 /// already its own wire form, while a dropped record return has to be
158 /// rewritten to one. returnInfo alone cannot tell them apart, so whoever
159 /// produces the classification records it here, next to the return type it
160 /// came from. A consumer that re-derived it from something else could pair
161 /// a classification with the wrong answer, and reading a dropped return as
162 /// void means silently skipping the rewrite it needs.
163 ///
164 /// Left false when unknown, which costs a needless rewrite rather than a
165 /// skipped one.
166 bool returnsVoid = false;
167
168 /// Whether any value in the signature is passed differently from how it is
169 /// written, so a rewriter has work to do.
170 bool needsRewrite() const {
171 if (!returnsVoid && !returnInfo.isPassThrough())
172 return true;
173 return !llvm::all_of(argInfos, [](const ArgClassification &ac) {
174 return ac.isPassThrough();
175 });
176 }
177};
178
179/// ABIRewriteContext is the abstract interface that each dialect
180/// implements to perform ABI-specific rewrites on its operations.
181///
182/// The pass orchestrator calls these methods after ABI classification
183/// to rewrite function definitions and call sites.
185public:
186 virtual ~ABIRewriteContext() = default;
187
188 /// Rewrite a function definition to use ABI-lowered types.
189 ///
190 /// This creates a new function with the lowered signature, rewrites
191 /// the function body to adapt between the ABI types and the
192 /// original high-level types, and replaces the original function.
193 ///
194 /// \param funcOp The function to rewrite (via FunctionOpInterface).
195 /// \param fc The ABI classification for this function.
196 /// \param builder The OpBuilder to use for modifications.
197 /// \returns success() if the function was rewritten.
198 virtual LogicalResult
199 rewriteFunctionDefinition(FunctionOpInterface funcOp,
200 const FunctionClassification &fc,
201 OpBuilder &builder) = 0;
202
203 /// Rewrite a call operation to match the callee's ABI-lowered
204 /// signature.
205 ///
206 /// This coerces arguments, handles indirect returns (sret), and
207 /// adapts the call result back to the original high-level type.
208 ///
209 /// \param callOp The call operation to rewrite.
210 /// \param fc The ABI classification for the callee.
211 /// \param builder The OpBuilder to use for modifications.
212 /// \returns success() if the call was rewritten.
213 virtual LogicalResult rewriteCallSite(Operation *callOp,
214 const FunctionClassification &fc,
215 OpBuilder &builder) = 0;
216
217 /// Rewrite a single "fetch the next vararg" operation (e.g. C `va_arg`) to
218 /// match how \p ac says the fetched type is passed at the ABI level.
219 ///
220 /// \p ac classifies only the one type being fetched, in isolation, with the
221 /// whole register budget available. A vararg fetch advances a runtime
222 /// cursor (the platform va_list) through registers and then memory, so
223 /// whether a given fetch lands in a register depends on how much of the
224 /// budget earlier variadic arguments already consumed at run time, not on
225 /// the fetch's static position.
226 ///
227 /// An implementation may erase \p vaArgOp and replace its result, so a
228 /// caller walking the IR must collect the fetches before rewriting any of
229 /// them.
230 ///
231 /// The default implementation reports failure, so a dialect that has not
232 /// implemented vararg fetches does not need to override this. An overrider
233 /// that fails is responsible for emitting its own diagnostic.
234 ///
235 /// \param vaArgOp The fetch to rewrite. May be erased.
236 /// \param ac The ABI classification of the fetched type.
237 /// \param builder The OpBuilder to use for modifications.
238 /// \returns success() if the operation was rewritten.
239 virtual LogicalResult rewriteVAArg(Operation *vaArgOp,
240 const ArgClassification &ac,
241 OpBuilder &builder) {
242 return failure();
243 }
244
245 /// Return the dialect namespace this context handles (e.g. "cir").
246 virtual StringRef getDialectNamespace() const = 0;
247};
248
249} // namespace abi
250} // namespace mlir
251
252#endif // MLIR_ABI_ABIREWRITECONTEXT_H
This class helps build Operations.
Definition Builders.h:210
Operation is the basic unit of execution within MLIR.
Definition Operation.h:87
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
ABIRewriteContext is the abstract interface that each dialect implements to perform ABI-specific rewr...
virtual StringRef getDialectNamespace() const =0
Return the dialect namespace this context handles (e.g. "cir").
virtual LogicalResult rewriteVAArg(Operation *vaArgOp, const ArgClassification &ac, OpBuilder &builder)
Rewrite a single "fetch the next vararg" operation (e.g.
virtual ~ABIRewriteContext()=default
virtual LogicalResult rewriteFunctionDefinition(FunctionOpInterface funcOp, const FunctionClassification &fc, OpBuilder &builder)=0
Rewrite a function definition to use ABI-lowered types.
virtual LogicalResult rewriteCallSite(Operation *callOp, const FunctionClassification &fc, OpBuilder &builder)=0
Rewrite a call operation to match the callee's ABI-lowered signature.
ArgKind
Classification of how a single argument or return value should be passed at the ABI level.
@ Indirect
Pass indirectly via a pointer (sret for returns, byval for args).
@ Extend
Like Direct, but with a sign/zero extension attribute.
@ Expand
Expand an aggregate into its constituent scalar fields.
@ Ignore
Ignore (void return, empty struct).
@ Direct
Pass directly in registers, possibly coerced to a different type.
Include the generated interface declarations.
Describes how a single argument or return value is passed after ABI lowering.
static ArgClassification getIgnore()
bool canFlatten
For Direct: whether a struct coercion can be flattened into individual register-width arguments.
bool signExtend
For Extend: whether to sign-extend (true) or zero-extend (false).
static ArgClassification getDirect(Type coerced, unsigned offset)
static ArgClassification getIndirect(llvm::Align align, bool byVal=true)
bool isPassThrough() const
Whether the value is passed as-is, so a rewriter can leave it alone.
static ArgClassification getDirect()
bool operator==(const ArgClassification &other) const
Whether two classifications describe the same wire format.
Type coercedType
The ABI-coerced type, if different from the original.
unsigned directOffset
For Direct with coercion: the byte offset within the original aggregate at which the coerced value li...
bool byVal
For Indirect: whether the callee gets ownership (byval).
unsigned neededIntRegs
How many integer and vector argument registers the value occupies.
llvm::Align indirectAlign
For Indirect: alignment of the pointed-to object.
static ArgClassification getExtend(Type coerced, bool signExt)
Holds the full ABI classification for a function: return type and all arguments.
bool returnsVoid
Whether the classified return type was the source language's void.
bool needsRewrite() const
Whether any value in the signature is passed differently from how it is written, so a rewriter has wo...
SmallVector< ArgClassification > argInfos