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 /// Whether the value is passed as-is, so a rewriter can leave it alone.
86 /// Only an uncoerced Direct qualifies. Extend counts as needing a rewrite
87 /// even though it only adds an attribute, because the attribute changes
88 /// observable behavior.
89 bool isPassThrough() const { return kind == ArgKind::Direct && !coercedType; }
90
91 /// Whether two classifications describe the same wire format. Every field
92 /// participates, so a field added above must be added here as well.
93 bool operator==(const ArgClassification &other) const {
94 return kind == other.kind && coercedType == other.coercedType &&
96 signExtend == other.signExtend && canFlatten == other.canFlatten &&
97 byVal == other.byVal && directOffset == other.directOffset;
98 }
99
101 return getDirect(/*coerced=*/nullptr, /*offset=*/0);
102 }
103
104 static ArgClassification getDirect(Type coerced, unsigned offset) {
105 // isPassThrough reads only coercedType, so an offset with no coerced
106 // type to read at it would be silently ignored.
107 assert((!offset || coerced) &&
108 "a direct offset needs a coerced type to read at it");
111 c.coercedType = coerced;
112 c.directOffset = offset;
113 return c;
114 }
115
119 return c;
120 }
121
122 static ArgClassification getIndirect(llvm::Align align, bool byVal = true) {
125 c.indirectAlign = align;
126 c.byVal = byVal;
127 return c;
128 }
129
130 static ArgClassification getExtend(Type coerced, bool signExt) {
133 c.coercedType = coerced;
134 c.signExtend = signExt;
135 return c;
136 }
137};
138
139/// Holds the full ABI classification for a function: return type and
140/// all arguments.
144
145 /// Whether the classified return type was the source language's void.
146 ///
147 /// A void return classifies as Ignore, and so does a return the ABI drops,
148 /// such as an empty record. The two need opposite treatment: void is
149 /// already its own wire form, while a dropped record return has to be
150 /// rewritten to one. returnInfo alone cannot tell them apart, so whoever
151 /// produces the classification records it here, next to the return type it
152 /// came from. A consumer that re-derived it from something else could pair
153 /// a classification with the wrong answer, and reading a dropped return as
154 /// void means silently skipping the rewrite it needs.
155 ///
156 /// Left false when unknown, which costs a needless rewrite rather than a
157 /// skipped one.
158 bool returnsVoid = false;
159
160 /// Whether any value in the signature is passed differently from how it is
161 /// written, so a rewriter has work to do.
162 bool needsRewrite() const {
163 if (!returnsVoid && !returnInfo.isPassThrough())
164 return true;
165 return !llvm::all_of(argInfos, [](const ArgClassification &ac) {
166 return ac.isPassThrough();
167 });
168 }
169};
170
171/// ABIRewriteContext is the abstract interface that each dialect
172/// implements to perform ABI-specific rewrites on its operations.
173///
174/// The pass orchestrator calls these methods after ABI classification
175/// to rewrite function definitions and call sites.
177public:
178 virtual ~ABIRewriteContext() = default;
179
180 /// Rewrite a function definition to use ABI-lowered types.
181 ///
182 /// This creates a new function with the lowered signature, rewrites
183 /// the function body to adapt between the ABI types and the
184 /// original high-level types, and replaces the original function.
185 ///
186 /// \param funcOp The function to rewrite (via FunctionOpInterface).
187 /// \param fc The ABI classification for this function.
188 /// \param builder The OpBuilder to use for modifications.
189 /// \returns success() if the function was rewritten.
190 virtual LogicalResult
191 rewriteFunctionDefinition(FunctionOpInterface funcOp,
192 const FunctionClassification &fc,
193 OpBuilder &builder) = 0;
194
195 /// Rewrite a call operation to match the callee's ABI-lowered
196 /// signature.
197 ///
198 /// This coerces arguments, handles indirect returns (sret), and
199 /// adapts the call result back to the original high-level type.
200 ///
201 /// \param callOp The call operation to rewrite.
202 /// \param fc The ABI classification for the callee.
203 /// \param builder The OpBuilder to use for modifications.
204 /// \returns success() if the call was rewritten.
205 virtual LogicalResult rewriteCallSite(Operation *callOp,
206 const FunctionClassification &fc,
207 OpBuilder &builder) = 0;
208
209 /// Return the dialect namespace this context handles (e.g. "cir").
210 virtual StringRef getDialectNamespace() const = 0;
211};
212
213} // namespace abi
214} // namespace mlir
215
216#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 ~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).
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