MLIR 23.0.0git
TestTarget.cpp
Go to the documentation of this file.
1//===- TestTarget.cpp - Predictable test ABI target ----------------------===//
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// **NOT A REAL ABI TARGET.**
10//
11// This file implements a predictable, dialect-agnostic ABI classifier for
12// testing the MLIR ABIRewriteContext infrastructure. The rules approximate
13// x86_64 SysV thresholds (Direct / Extend / Indirect / Ignore / Expand) so
14// the generated classifications are familiar to reviewers, but they are
15// NOT a substitute for testing against the real x86_64 ABIInfo. Real
16// ABI targets live alongside the LLVM ABI library in `llvm/lib/ABI/Targets/`.
17//
18// Real-ABI-shaped tests use the classification-injection driver via
19// `parseClassificationAttr`, which lets tests construct any
20// FunctionClassification (including shapes the test target itself does
21// not produce) by attaching a DictionaryAttr to the function.
22//
23// Rules:
24// - mlir::NoneType → Ignore
25// - IntegerType with width < 32 → Extend (zero-extend by
26// default; tests using the
27// injection driver can
28// override to signed)
29// - IntegerType with width >= 32 → Direct
30// - FloatType, VectorType, MemRefType → Direct
31// - Anything else with DataLayout size 0 → Ignore
32// - Anything else with DataLayout size <= 16 → Direct (coerced to the
33// same type — no actual
34// coercion in the test
35// target; PR C handles
36// non-trivial coercion)
37// - Anything else with DataLayout size > 16 → Indirect with byval=true
38// (sret on returns) and
39// alignment from
40// DataLayout
41//
42//===----------------------------------------------------------------------===//
43
46#include "llvm/Support/Alignment.h"
47
48using namespace mlir;
49using namespace mlir::abi;
50using namespace mlir::abi::test;
51
52namespace {
53
54/// Indirect-vs-direct cutoff in bytes. Chosen to match x86_64 SysV's
55/// 16-byte register-passing window for reviewer familiarity.
56constexpr uint64_t IndirectCutoffBytes = 16;
57
58/// Below this width (in bits) integers get an extension attribute.
59/// Chosen to match x86_64 SysV (32-bit register width) for reviewer
60/// familiarity.
61constexpr unsigned ExtendBelowBits = 32;
62
63ArgClassification classifyOne(Type type, const DataLayout &dl) {
64 if (isa<NoneType>(type))
66
67 if (auto intTy = dyn_cast<IntegerType>(type)) {
68 if (intTy.getWidth() < ExtendBelowBits) {
69 Type i32Ty = IntegerType::get(type.getContext(), ExtendBelowBits);
70 return ArgClassification::getExtend(i32Ty, /*signExt=*/intTy.isSigned());
71 }
73 }
74
75 if (auto indexTy = dyn_cast<IndexType>(type)) {
76 llvm::TypeSize sizeInBits = dl.getTypeSizeInBits(indexTy);
77 if (sizeInBits.getFixedValue() < ExtendBelowBits) {
78 Type i32Ty = IntegerType::get(type.getContext(), ExtendBelowBits);
79 return ArgClassification::getExtend(i32Ty, /*signExt=*/true);
80 }
82 }
83
84 if (isa<FloatType, VectorType, MemRefType>(type))
86
87 // For dialect-specific types: query DataLayout via
88 // DataLayoutTypeInterface. Types that don't implement the interface
89 // (e.g. dialect-specific void / unit-style sentinel types used as a
90 // function's "no return value" marker) are treated as Ignore so that
91 // the test target degrades gracefully rather than crashing on unknown
92 // types.
93 if (!isa<DataLayoutTypeInterface>(type))
95
96 llvm::TypeSize sizeInBits = dl.getTypeSizeInBits(type);
97 if (sizeInBits.isZero())
99
100 uint64_t sizeInBytes = (sizeInBits.getFixedValue() + 7) / 8;
101 if (sizeInBytes <= IndirectCutoffBytes)
103
104 uint64_t alignBytes = dl.getTypeABIAlignment(type);
105 return ArgClassification::getIndirect(llvm::Align(alignBytes),
106 /*byVal=*/true);
107}
108
109} // namespace
110
112 Type returnType,
113 const DataLayout &dl) {
115 fc.returnInfo = classifyOne(returnType, dl);
116 fc.argInfos.reserve(argTypes.size());
117 for (Type t : argTypes)
118 fc.argInfos.push_back(classifyOne(t, dl));
119 return fc;
120}
121
122namespace {
123
124/// Set of dictionary keys this parser knows about. Any key not in this
125/// set causes a parse error (no silent ignore). Updated when new
126/// optional keys are added to the schema.
127constexpr StringRef knownArgKeys[] = {
128 "kind", "coerced_type", "sign_extend",
129 "can_flatten", "indirect_align", "byval",
130};
131
132bool isKnownArgKey(StringRef key) {
133 for (StringRef k : knownArgKeys)
134 if (k == key)
135 return true;
136 return false;
137}
138
139/// Parse a single ArgClassification dictionary. Returns std::nullopt on
140/// any error (with the diagnostic emitted via \p emitError).
141std::optional<ArgClassification>
142parseOne(DictionaryAttr argDict, function_ref<InFlightDiagnostic()> emitError) {
143 StringAttr kindAttr = argDict.getAs<StringAttr>("kind");
144 if (!kindAttr) {
145 emitError() << "missing required 'kind' StringAttr";
146 return std::nullopt;
147 }
148
149 for (NamedAttribute na : argDict)
150 if (!isKnownArgKey(na.getName().getValue())) {
151 emitError() << "unknown key '" << na.getName().getValue()
152 << "' in classification dictionary; allowed keys are "
153 << "kind, coerced_type, sign_extend, can_flatten, "
154 << "indirect_align, byval";
155 return std::nullopt;
156 }
157
158 StringRef kind = kindAttr.getValue();
159
160 if (kind == "direct") {
161 Type coerced;
162 if (auto t = argDict.getAs<TypeAttr>("coerced_type"))
163 coerced = t.getValue();
164 auto c = ArgClassification::getDirect(coerced);
165 if (auto cf = argDict.getAs<BoolAttr>("can_flatten"))
166 c.canFlatten = cf.getValue();
167 return c;
168 }
169
170 if (kind == "extend") {
171 auto coerced = argDict.getAs<TypeAttr>("coerced_type");
172 if (!coerced) {
173 emitError() << "kind='extend' requires 'coerced_type' TypeAttr";
174 return std::nullopt;
175 }
176 bool signExt = false;
177 if (auto se = argDict.getAs<BoolAttr>("sign_extend"))
178 signExt = se.getValue();
179 return ArgClassification::getExtend(coerced.getValue(), signExt);
180 }
181
182 if (kind == "indirect") {
183 auto align = argDict.getAs<IntegerAttr>("indirect_align");
184 if (!align) {
185 emitError() << "kind='indirect' requires 'indirect_align' IntegerAttr";
186 return std::nullopt;
187 }
188 if (align.getInt() <= 0 || !llvm::isPowerOf2_64(align.getInt())) {
189 emitError() << "'indirect_align' must be a positive power of 2; got "
190 << align.getInt();
191 return std::nullopt;
192 }
193 bool byVal = true;
194 if (auto bv = argDict.getAs<BoolAttr>("byval"))
195 byVal = bv.getValue();
196 return ArgClassification::getIndirect(llvm::Align(align.getInt()), byVal);
197 }
198
199 if (kind == "ignore") {
201 }
202
203 if (kind == "expand") {
206 return c;
207 }
208
209 emitError() << "unknown kind='" << kind
210 << "'; expected one of direct, extend, indirect, ignore, expand";
211 return std::nullopt;
212}
213
214} // namespace
215
216std::optional<FunctionClassification> mlir::abi::test::parseClassificationAttr(
217 DictionaryAttr attr, function_ref<InFlightDiagnostic()> emitError) {
218 auto returnDict = attr.getAs<DictionaryAttr>("return");
219 if (!returnDict) {
220 emitError() << "missing required 'return' DictionaryAttr";
221 return std::nullopt;
222 }
223
224 auto argsArr = attr.getAs<ArrayAttr>("args");
225 if (!argsArr) {
226 emitError() << "missing required 'args' ArrayAttr";
227 return std::nullopt;
228 }
229
230 for (NamedAttribute na : attr) {
231 StringRef k = na.getName().getValue();
232 if (k != "return" && k != "args") {
233 emitError() << "unknown top-level key '" << k
234 << "'; only 'return' and 'args' are allowed";
235 return std::nullopt;
236 }
237 }
238
240
241 std::optional<ArgClassification> ret = parseOne(returnDict, emitError);
242 if (!ret)
243 return std::nullopt;
244 fc.returnInfo = *ret;
245
246 fc.argInfos.reserve(argsArr.size());
247 for (Attribute a : argsArr) {
248 auto d = dyn_cast<DictionaryAttr>(a);
249 if (!d) {
250 emitError() << "'args' entries must be DictionaryAttrs";
251 return std::nullopt;
252 }
253 std::optional<ArgClassification> ac = parseOne(d, emitError);
254 if (!ac)
255 return std::nullopt;
256 fc.argInfos.push_back(*ac);
257 }
258
259 return fc;
260}
Attributes are known-constant values of operations.
Definition Attributes.h:25
Special case of IntegerAttr to represent boolean integers, i.e., signless i1 integers.
The main mechanism for performing data layout queries.
uint64_t getTypeABIAlignment(Type t) const
Returns the required alignment of the given type in the current scope.
llvm::TypeSize getTypeSizeInBits(Type t) const
Returns the size in bits of the given type in the current scope.
This class represents a diagnostic that is inflight and set to be reported.
NamedAttribute represents a combination of a name and an Attribute value.
Definition Attributes.h:164
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
MLIRContext * getContext() const
Return the MLIRContext in which this type was uniqued.
Definition Types.cpp:35
std::optional< FunctionClassification > parseClassificationAttr(DictionaryAttr attr, function_ref< InFlightDiagnostic()> emitError)
Parse a FunctionClassification from a plain MLIR DictionaryAttr.
FunctionClassification classify(ArrayRef< Type > argTypes, Type returnType, const DataLayout &dl)
Classify a function signature using the test target's predictable rules.
@ Expand
Expand an aggregate into its constituent scalar fields.
Include the generated interface declarations.
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.
llvm::function_ref< Fn > function_ref
Definition LLVM.h:147
Describes how a single argument or return value is passed after ABI lowering.
static ArgClassification getIgnore()
static ArgClassification getDirect(Type coerced=nullptr)
static ArgClassification getIndirect(llvm::Align align, bool byVal=true)
static ArgClassification getExtend(Type coerced, bool signExt)
Holds the full ABI classification for a function: return type and all arguments.
SmallVector< ArgClassification > argInfos