David Rivera 0b88ee12dd
[CIR] Infrastructure and MemorySpaceAttrInterface for Address Spaces (#179073)
Related: https://github.com/llvm/llvm-project/issues/175871,
https://github.com/issues/assigned?issue=llvm%7Cllvm-project%7C179278,
https://github.com/issues/assigned?issue=llvm%7Cllvm-project%7C160386

- Introducing the LangAddressSpace enum with offload address space kinds
(offload_private, offload_local, offload_global, offload_constant,
offload_generic) and the LangAddressSpaceAttr attribute.


- Generalizes CIR AS attributes as MemorySpaceAttrInterface and Attaches
it to `PointerType`. Includes test coverage for valid IR roundtrips and
invalid address space parsing.

This starts a series of patches with the purpose of bringing complete
address spaces support features for CIR. Most of the test coverage is
provided in subsequent patches further down the stack. note that most of
these patches are based on: https://github.com/llvm/clangir/pull/1986
2026-02-27 23:08:20 -05:00

166 lines
5.3 KiB
C++

//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
// This class provides a simple wrapper for a pair of a pointer and an
// alignment.
//
//===----------------------------------------------------------------------===//
#ifndef CLANG_LIB_CIR_ADDRESS_H
#define CLANG_LIB_CIR_ADDRESS_H
#include "mlir/Dialect/Ptr/IR/MemorySpaceInterfaces.h"
#include "mlir/IR/Value.h"
#include "clang/AST/CharUnits.h"
#include "clang/CIR/Dialect/IR/CIRAttrs.h"
#include "clang/CIR/Dialect/IR/CIRTypes.h"
#include "clang/CIR/MissingFeatures.h"
#include "llvm/ADT/PointerIntPair.h"
#include "llvm/Support/Casting.h"
namespace clang::CIRGen {
// Forward declaration to avoid a circular dependency
class CIRGenBuilderTy;
class Address {
// The boolean flag indicates whether the pointer is known to be non-null.
llvm::PointerIntPair<mlir::Value, 1, bool> pointerAndKnownNonNull;
/// The expected CIR type of the pointer. Carrying accurate element type
/// information in Address makes it more convenient to work with Address
/// values and allows frontend assertions to catch simple mistakes.
mlir::Type elementType;
clang::CharUnits alignment;
protected:
Address(std::nullptr_t) : elementType(nullptr) {}
public:
Address(mlir::Value pointer, mlir::Type elementType,
clang::CharUnits alignment)
: Address(pointer, elementType, alignment, false) {}
Address(mlir::Value pointer, mlir::Type elementType,
clang::CharUnits alignment, bool isKnownNonNull)
: pointerAndKnownNonNull(pointer, isKnownNonNull),
elementType(elementType), alignment(alignment) {
assert(pointer && "Pointer cannot be null");
assert(elementType && "Element type cannot be null");
assert(!alignment.isZero() && "Alignment cannot be zero");
assert(mlir::isa<cir::PointerType>(pointer.getType()) &&
"Expected cir.ptr type");
assert(mlir::cast<cir::PointerType>(pointer.getType()).getPointee() ==
elementType);
}
Address(mlir::Value pointer, clang::CharUnits alignment)
: Address(pointer,
mlir::cast<cir::PointerType>(pointer.getType()).getPointee(),
alignment) {
assert((!alignment.isZero() || pointer == nullptr) &&
"creating valid address with invalid alignment");
}
static Address invalid() { return Address(nullptr); }
bool isValid() const {
return pointerAndKnownNonNull.getPointer() != nullptr;
}
/// Return address with different pointer, but same element type and
/// alignment.
Address withPointer(mlir::Value newPtr) const {
return Address(newPtr, getElementType(), getAlignment());
}
/// Return address with different alignment, but same pointer and element
/// type.
Address withAlignment(clang::CharUnits newAlignment) const {
return Address(getPointer(), getElementType(), newAlignment,
isKnownNonNull());
}
/// Return address with different element type, a bitcast pointer, and
/// the same alignment.
Address withElementType(CIRGenBuilderTy &builder, mlir::Type ElemTy) const;
mlir::Value getPointer() const {
assert(isValid());
return pointerAndKnownNonNull.getPointer();
}
mlir::Value getBasePointer() const {
// TODO(cir): Remove the version above when we catchup with OG codegen on
// ptr auth.
assert(isValid() && "pointer isn't valid");
return getPointer();
}
/// Return the pointer contained in this class after authenticating it and
/// adding offset to it if necessary.
mlir::Value emitRawPointer() const {
assert(!cir::MissingFeatures::addressPointerAuthInfo());
return getBasePointer();
}
mlir::Type getType() const {
assert(mlir::cast<cir::PointerType>(
pointerAndKnownNonNull.getPointer().getType())
.getPointee() == elementType);
return mlir::cast<cir::PointerType>(getPointer().getType());
}
mlir::Type getElementType() const {
assert(isValid());
assert(mlir::cast<cir::PointerType>(
pointerAndKnownNonNull.getPointer().getType())
.getPointee() == elementType);
return elementType;
}
mlir::ptr::MemorySpaceAttrInterface getAddressSpace() const {
auto ptrTy = mlir::dyn_cast<cir::PointerType>(getType());
return ptrTy.getAddrSpace();
}
clang::CharUnits getAlignment() const { return alignment; }
/// Get the operation which defines this address.
mlir::Operation *getDefiningOp() const {
if (!isValid())
return nullptr;
return getPointer().getDefiningOp();
}
template <typename OpTy> OpTy getDefiningOp() const {
return mlir::dyn_cast_or_null<OpTy>(getDefiningOp());
}
/// Whether the pointer is known not to be null.
bool isKnownNonNull() const {
assert(isValid() && "Invalid address");
return static_cast<bool>(pointerAndKnownNonNull.getInt());
}
/// Set the non-null bit.
Address setKnownNonNull() {
assert(isValid() && "Invalid address");
pointerAndKnownNonNull.setInt(true);
return *this;
}
};
} // namespace clang::CIRGen
#endif // CLANG_LIB_CIR_ADDRESS_H