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 Indirect: target address space of the pointer passed in place of
81 /// the value. Zero is the default address space.
82 unsigned indirectAddrSpace = 0;
83
84 /// For Direct with coercion: the byte offset within the original aggregate
85 /// at which the coerced value lives. Non-zero when the low eightbyte is
86 /// NO_CLASS and the value is carried in a later eightbyte (x86-64 SysV).
87 unsigned directOffset = 0;
88
89 /// How many integer and vector argument registers the value occupies. Both
90 /// zero means it travels in memory, which is also what a target whose
91 /// classifier does not record the demand reports.
92 unsigned neededIntRegs = 0;
93 unsigned neededSseRegs = 0;
94
95 /// Whether the value is passed as-is, so a rewriter can leave it alone.
96 /// Only an uncoerced Direct qualifies. Extend counts as needing a rewrite
97 /// even though it only adds an attribute, because the attribute changes
98 /// observable behavior.
99 bool isPassThrough() const { return kind == ArgKind::Direct && !coercedType; }
100
101 /// Whether two classifications describe the same wire format. Every field
102 /// participates, so a field added above must be added here as well.
103 bool operator==(const ArgClassification &other) const {
104 return kind == other.kind && coercedType == other.coercedType &&
105 indirectAlign == other.indirectAlign &&
106 signExtend == other.signExtend && canFlatten == other.canFlatten &&
107 byVal == other.byVal &&
109 directOffset == other.directOffset &&
110 neededIntRegs == other.neededIntRegs &&
112 }
113
115 return getDirect(/*coerced=*/nullptr, /*offset=*/0);
116 }
117
118 static ArgClassification getDirect(Type coerced, unsigned offset) {
119 // isPassThrough reads only coercedType, so an offset with no coerced
120 // type to read at it would be silently ignored.
121 assert((!offset || coerced) &&
122 "a direct offset needs a coerced type to read at it");
125 c.coercedType = coerced;
126 c.directOffset = offset;
127 return c;
128 }
129
133 return c;
134 }
135
136 static ArgClassification getIndirect(llvm::Align align, bool byVal,
137 unsigned addrSpace) {
140 c.indirectAlign = align;
141 c.byVal = byVal;
142 c.indirectAddrSpace = addrSpace;
143 return c;
144 }
145
146 static ArgClassification getExtend(Type coerced, bool signExt) {
149 c.coercedType = coerced;
150 c.signExtend = signExt;
151 return c;
152 }
153};
154
155/// Holds the full ABI classification for a function: return type and
156/// all arguments.
160
161 /// Whether the classified return type was the source language's void.
162 ///
163 /// A void return classifies as Ignore, and so does a return the ABI drops,
164 /// such as an empty record. The two need opposite treatment: void is
165 /// already its own wire form, while a dropped record return has to be
166 /// rewritten to one. returnInfo alone cannot tell them apart, so whoever
167 /// produces the classification records it here, next to the return type it
168 /// came from. A consumer that re-derived it from something else could pair
169 /// a classification with the wrong answer, and reading a dropped return as
170 /// void means silently skipping the rewrite it needs.
171 ///
172 /// Left false when unknown, which costs a needless rewrite rather than a
173 /// skipped one.
174 bool returnsVoid = false;
175
176 /// Whether any value in the signature is passed differently from how it is
177 /// written, so a rewriter has work to do.
178 bool needsRewrite() const {
179 if (!returnsVoid && !returnInfo.isPassThrough())
180 return true;
181 return !llvm::all_of(argInfos, [](const ArgClassification &ac) {
182 return ac.isPassThrough();
183 });
184 }
185};
186
187/// ABIRewriteContext is the abstract interface that each dialect
188/// implements to perform ABI-specific rewrites on its operations.
189///
190/// The pass orchestrator calls these methods after ABI classification
191/// to rewrite function definitions and call sites.
193public:
194 virtual ~ABIRewriteContext() = default;
195
196 /// Rewrite a function definition to use ABI-lowered types.
197 ///
198 /// This creates a new function with the lowered signature, rewrites
199 /// the function body to adapt between the ABI types and the
200 /// original high-level types, and replaces the original function.
201 ///
202 /// \param funcOp The function to rewrite (via FunctionOpInterface).
203 /// \param fc The ABI classification for this function.
204 /// \param builder The OpBuilder to use for modifications.
205 /// \returns success() if the function was rewritten.
206 virtual LogicalResult
207 rewriteFunctionDefinition(FunctionOpInterface funcOp,
208 const FunctionClassification &fc,
209 OpBuilder &builder) = 0;
210
211 /// Rewrite a call operation to match the callee's ABI-lowered
212 /// signature.
213 ///
214 /// This coerces arguments, handles indirect returns (sret), and
215 /// adapts the call result back to the original high-level type.
216 ///
217 /// \param callOp The call operation to rewrite.
218 /// \param fc The ABI classification for the callee.
219 /// \param builder The OpBuilder to use for modifications.
220 /// \returns success() if the call was rewritten.
221 virtual LogicalResult rewriteCallSite(Operation *callOp,
222 const FunctionClassification &fc,
223 OpBuilder &builder) = 0;
224
225 /// Rewrite a single "fetch the next vararg" operation (e.g. C `va_arg`) to
226 /// match how \p ac says the fetched type is passed at the ABI level.
227 ///
228 /// \p ac classifies only the one type being fetched, in isolation, with the
229 /// whole register budget available. A vararg fetch advances a runtime
230 /// cursor (the platform va_list) through registers and then memory, so
231 /// whether a given fetch lands in a register depends on how much of the
232 /// budget earlier variadic arguments already consumed at run time, not on
233 /// the fetch's static position.
234 ///
235 /// An implementation may erase \p vaArgOp and replace its result, so a
236 /// caller walking the IR must collect the fetches before rewriting any of
237 /// them.
238 ///
239 /// The default implementation reports failure, so a dialect that has not
240 /// implemented vararg fetches does not need to override this. An overrider
241 /// that fails is responsible for emitting its own diagnostic.
242 ///
243 /// \param vaArgOp The fetch to rewrite. May be erased.
244 /// \param ac The ABI classification of the fetched type.
245 /// \param builder The OpBuilder to use for modifications.
246 /// \returns success() if the operation was rewritten.
247 virtual LogicalResult rewriteVAArg(Operation *vaArgOp,
248 const ArgClassification &ac,
249 OpBuilder &builder) {
250 return failure();
251 }
252
253 /// Return the dialect namespace this context handles (e.g. "cir").
254 virtual StringRef getDialectNamespace() const = 0;
255};
256
257} // namespace abi
258} // namespace mlir
259
260#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)
bool isPassThrough() const
Whether the value is passed as-is, so a rewriter can leave it alone.
unsigned indirectAddrSpace
For Indirect: target address space of the pointer passed in place of the value.
static ArgClassification getDirect()
bool operator==(const ArgClassification &other) const
Whether two classifications describe the same wire format.
static ArgClassification getIndirect(llvm::Align align, bool byVal, unsigned addrSpace)
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