Files
TheRock/compiler/amd-llvm/mlir/lib/ABI/Targets/Test/TestTarget.cpp
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

261 lines
9.2 KiB
C++

//===- TestTarget.cpp - Predictable test ABI target ----------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// **NOT A REAL ABI TARGET.**
//
// This file implements a predictable, dialect-agnostic ABI classifier for
// testing the MLIR ABIRewriteContext infrastructure. The rules approximate
// x86_64 SysV thresholds (Direct / Extend / Indirect / Ignore / Expand) so
// the generated classifications are familiar to reviewers, but they are
// NOT a substitute for testing against the real x86_64 ABIInfo. Real
// ABI targets live alongside the LLVM ABI library in `llvm/lib/ABI/Targets/`.
//
// Real-ABI-shaped tests use the classification-injection driver via
// `parseClassificationAttr`, which lets tests construct any
// FunctionClassification (including shapes the test target itself does
// not produce) by attaching a DictionaryAttr to the function.
//
// Rules:
// - mlir::NoneType → Ignore
// - IntegerType with width < 32 → Extend (zero-extend by
// default; tests using the
// injection driver can
// override to signed)
// - IntegerType with width >= 32 → Direct
// - FloatType, VectorType, MemRefType → Direct
// - Anything else with DataLayout size 0 → Ignore
// - Anything else with DataLayout size <= 16 → Direct (coerced to the
// same type — no actual
// coercion in the test
// target; PR C handles
// non-trivial coercion)
// - Anything else with DataLayout size > 16 → Indirect with byval=true
// (sret on returns) and
// alignment from
// DataLayout
//
//===----------------------------------------------------------------------===//
#include "mlir/ABI/Targets/Test/TestTarget.h"
#include "mlir/IR/BuiltinTypes.h"
#include "llvm/Support/Alignment.h"
using namespace mlir;
using namespace mlir::abi;
using namespace mlir::abi::test;
namespace {
/// Indirect-vs-direct cutoff in bytes. Chosen to match x86_64 SysV's
/// 16-byte register-passing window for reviewer familiarity.
constexpr uint64_t IndirectCutoffBytes = 16;
/// Below this width (in bits) integers get an extension attribute.
/// Chosen to match x86_64 SysV (32-bit register width) for reviewer
/// familiarity.
constexpr unsigned ExtendBelowBits = 32;
ArgClassification classifyOne(Type type, const DataLayout &dl) {
if (isa<NoneType>(type))
return ArgClassification::getIgnore();
if (auto intTy = dyn_cast<IntegerType>(type)) {
if (intTy.getWidth() < ExtendBelowBits) {
Type i32Ty = IntegerType::get(type.getContext(), ExtendBelowBits);
return ArgClassification::getExtend(i32Ty, /*signExt=*/intTy.isSigned());
}
return ArgClassification::getDirect();
}
if (auto indexTy = dyn_cast<IndexType>(type)) {
llvm::TypeSize sizeInBits = dl.getTypeSizeInBits(indexTy);
if (sizeInBits.getFixedValue() < ExtendBelowBits) {
Type i32Ty = IntegerType::get(type.getContext(), ExtendBelowBits);
return ArgClassification::getExtend(i32Ty, /*signExt=*/true);
}
return ArgClassification::getDirect();
}
if (isa<FloatType, VectorType, MemRefType>(type))
return ArgClassification::getDirect();
// For dialect-specific types: query DataLayout via
// DataLayoutTypeInterface. Types that don't implement the interface
// (e.g. dialect-specific void / unit-style sentinel types used as a
// function's "no return value" marker) are treated as Ignore so that
// the test target degrades gracefully rather than crashing on unknown
// types.
if (!isa<DataLayoutTypeInterface>(type))
return ArgClassification::getIgnore();
llvm::TypeSize sizeInBits = dl.getTypeSizeInBits(type);
if (sizeInBits.isZero())
return ArgClassification::getIgnore();
uint64_t sizeInBytes = (sizeInBits.getFixedValue() + 7) / 8;
if (sizeInBytes <= IndirectCutoffBytes)
return ArgClassification::getDirect();
uint64_t alignBytes = dl.getTypeABIAlignment(type);
return ArgClassification::getIndirect(llvm::Align(alignBytes),
/*byVal=*/true);
}
} // namespace
FunctionClassification mlir::abi::test::classify(ArrayRef<Type> argTypes,
Type returnType,
const DataLayout &dl) {
FunctionClassification fc;
fc.returnInfo = classifyOne(returnType, dl);
fc.argInfos.reserve(argTypes.size());
for (Type t : argTypes)
fc.argInfos.push_back(classifyOne(t, dl));
return fc;
}
namespace {
/// Set of dictionary keys this parser knows about. Any key not in this
/// set causes a parse error (no silent ignore). Updated when new
/// optional keys are added to the schema.
constexpr StringRef knownArgKeys[] = {
"kind", "coerced_type", "sign_extend",
"can_flatten", "indirect_align", "byval",
};
bool isKnownArgKey(StringRef key) {
for (StringRef k : knownArgKeys)
if (k == key)
return true;
return false;
}
/// Parse a single ArgClassification dictionary. Returns std::nullopt on
/// any error (with the diagnostic emitted via \p emitError).
std::optional<ArgClassification>
parseOne(DictionaryAttr argDict, function_ref<InFlightDiagnostic()> emitError) {
StringAttr kindAttr = argDict.getAs<StringAttr>("kind");
if (!kindAttr) {
emitError() << "missing required 'kind' StringAttr";
return std::nullopt;
}
for (NamedAttribute na : argDict)
if (!isKnownArgKey(na.getName().getValue())) {
emitError() << "unknown key '" << na.getName().getValue()
<< "' in classification dictionary; allowed keys are "
<< "kind, coerced_type, sign_extend, can_flatten, "
<< "indirect_align, byval";
return std::nullopt;
}
StringRef kind = kindAttr.getValue();
if (kind == "direct") {
Type coerced;
if (auto t = argDict.getAs<TypeAttr>("coerced_type"))
coerced = t.getValue();
auto c = ArgClassification::getDirect(coerced);
if (auto cf = argDict.getAs<BoolAttr>("can_flatten"))
c.canFlatten = cf.getValue();
return c;
}
if (kind == "extend") {
auto coerced = argDict.getAs<TypeAttr>("coerced_type");
if (!coerced) {
emitError() << "kind='extend' requires 'coerced_type' TypeAttr";
return std::nullopt;
}
bool signExt = false;
if (auto se = argDict.getAs<BoolAttr>("sign_extend"))
signExt = se.getValue();
return ArgClassification::getExtend(coerced.getValue(), signExt);
}
if (kind == "indirect") {
auto align = argDict.getAs<IntegerAttr>("indirect_align");
if (!align) {
emitError() << "kind='indirect' requires 'indirect_align' IntegerAttr";
return std::nullopt;
}
if (align.getInt() <= 0 || !llvm::isPowerOf2_64(align.getInt())) {
emitError() << "'indirect_align' must be a positive power of 2; got "
<< align.getInt();
return std::nullopt;
}
bool byVal = true;
if (auto bv = argDict.getAs<BoolAttr>("byval"))
byVal = bv.getValue();
return ArgClassification::getIndirect(llvm::Align(align.getInt()), byVal);
}
if (kind == "ignore") {
return ArgClassification::getIgnore();
}
if (kind == "expand") {
ArgClassification c;
c.kind = ArgKind::Expand;
return c;
}
emitError() << "unknown kind='" << kind
<< "'; expected one of direct, extend, indirect, ignore, expand";
return std::nullopt;
}
} // namespace
std::optional<FunctionClassification> mlir::abi::test::parseClassificationAttr(
DictionaryAttr attr, function_ref<InFlightDiagnostic()> emitError) {
auto returnDict = attr.getAs<DictionaryAttr>("return");
if (!returnDict) {
emitError() << "missing required 'return' DictionaryAttr";
return std::nullopt;
}
auto argsArr = attr.getAs<ArrayAttr>("args");
if (!argsArr) {
emitError() << "missing required 'args' ArrayAttr";
return std::nullopt;
}
for (NamedAttribute na : attr) {
StringRef k = na.getName().getValue();
if (k != "return" && k != "args") {
emitError() << "unknown top-level key '" << k
<< "'; only 'return' and 'args' are allowed";
return std::nullopt;
}
}
FunctionClassification fc;
std::optional<ArgClassification> ret = parseOne(returnDict, emitError);
if (!ret)
return std::nullopt;
fc.returnInfo = *ret;
fc.argInfos.reserve(argsArr.size());
for (Attribute a : argsArr) {
auto d = dyn_cast<DictionaryAttr>(a);
if (!d) {
emitError() << "'args' entries must be DictionaryAttrs";
return std::nullopt;
}
std::optional<ArgClassification> ac = parseOne(d, emitError);
if (!ac)
return std::nullopt;
fc.argInfos.push_back(*ac);
}
return fc;
}