Skip to content

[CIR] Upstream CastOp and scalar conversions #130690

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Mar 18, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions clang/include/clang/CIR/Dialect/Builder/CIRBaseBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@
#define LLVM_CLANG_CIR_DIALECT_BUILDER_CIRBASEBUILDER_H

#include "clang/AST/CharUnits.h"
#include "clang/AST/Type.h"
#include "clang/CIR/Dialect/IR/CIRAttrs.h"
#include "clang/CIR/Dialect/IR/CIRDialect.h"
#include "clang/CIR/Dialect/IR/CIRTypes.h"
#include "llvm/Support/ErrorHandling.h"

#include "mlir/IR/Builders.h"
#include "mlir/IR/BuiltinTypes.h"
Expand Down Expand Up @@ -78,6 +80,60 @@ class CIRBaseBuilderTy : public mlir::OpBuilder {
return create<cir::StoreOp>(loc, val, dst);
}

mlir::Value createDummyValue(mlir::Location loc, mlir::Type type,
clang::CharUnits alignment) {
auto addr = createAlloca(loc, getPointerTo(type), type, {},
getSizeFromCharUnits(getContext(), alignment));
return createLoad(loc, addr);
}

//===--------------------------------------------------------------------===//
// Cast/Conversion Operators
//===--------------------------------------------------------------------===//

mlir::Value createCast(mlir::Location loc, cir::CastKind kind,
mlir::Value src, mlir::Type newTy) {
if (newTy == src.getType())
return src;
return create<cir::CastOp>(loc, newTy, kind, src);
}

mlir::Value createCast(cir::CastKind kind, mlir::Value src,
mlir::Type newTy) {
if (newTy == src.getType())
return src;
return createCast(src.getLoc(), kind, src, newTy);
}

mlir::Value createIntCast(mlir::Value src, mlir::Type newTy) {
return createCast(cir::CastKind::integral, src, newTy);
}

mlir::Value createIntToPtr(mlir::Value src, mlir::Type newTy) {
return createCast(cir::CastKind::int_to_ptr, src, newTy);
}

mlir::Value createPtrToInt(mlir::Value src, mlir::Type newTy) {
return createCast(cir::CastKind::ptr_to_int, src, newTy);
}

mlir::Value createPtrToBoolCast(mlir::Value v) {
return createCast(cir::CastKind::ptr_to_bool, v, getBoolTy());
}

mlir::Value createBoolToInt(mlir::Value src, mlir::Type newTy) {
return createCast(cir::CastKind::bool_to_int, src, newTy);
}

mlir::Value createBitcast(mlir::Value src, mlir::Type newTy) {
return createCast(cir::CastKind::bitcast, src, newTy);
}

mlir::Value createBitcast(mlir::Location loc, mlir::Value src,
mlir::Type newTy) {
return createCast(loc, cir::CastKind::bitcast, src, newTy);
}

//
// Block handling helpers
// ----------------------
Expand Down
150 changes: 150 additions & 0 deletions clang/include/clang/CIR/Dialect/IR/CIROps.td
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,156 @@ class LLVMLoweringInfo {
class CIR_Op<string mnemonic, list<Trait> traits = []> :
Op<CIR_Dialect, mnemonic, traits>, LLVMLoweringInfo;

//===----------------------------------------------------------------------===//
// CastOp
//===----------------------------------------------------------------------===//

// CK_Dependent
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Neat! Thank you!

def CK_BitCast : I32EnumAttrCase<"bitcast", 1>;
// CK_LValueBitCast
// CK_LValueToRValueBitCast
// CK_LValueToRValue
// CK_NoOp
// CK_BaseToDerived
// CK_DerivedToBase
// CK_UncheckedDerivedToBase
// CK_Dynamic
// CK_ToUnion
def CK_ArrayToPointerDecay : I32EnumAttrCase<"array_to_ptrdecay", 11>;
// CK_FunctionToPointerDecay
// CK_NullToPointer
// CK_NullToMemberPointer
// CK_BaseToDerivedMemberPointer
// CK_DerivedToBaseMemberPointer
def CK_MemberPointerToBoolean : I32EnumAttrCase<"member_ptr_to_bool", 17>;
// CK_ReinterpretMemberPointer
// CK_UserDefinedConversion
// CK_ConstructorConversion
def CK_IntegralToPointer : I32EnumAttrCase<"int_to_ptr", 21>;
def CK_PointerToIntegral : I32EnumAttrCase<"ptr_to_int", 22>;
def CK_PointerToBoolean : I32EnumAttrCase<"ptr_to_bool", 23>;
// CK_ToVoid
// CK_MatrixCast
// CK_VectorSplat
def CK_IntegralCast : I32EnumAttrCase<"integral", 27>;
def CK_IntegralToBoolean : I32EnumAttrCase<"int_to_bool", 28>;
def CK_IntegralToFloating : I32EnumAttrCase<"int_to_float", 29>;
// CK_FloatingToFixedPoint
// CK_FixedPointToFloating
// CK_FixedPointCast
// CK_FixedPointToIntegral
// CK_IntegralToFixedPoint
// CK_FixedPointToBoolean
def CK_FloatingToIntegral : I32EnumAttrCase<"float_to_int", 36>;
def CK_FloatingToBoolean : I32EnumAttrCase<"float_to_bool", 37>;
def CK_BooleanToSignedIntegral : I32EnumAttrCase<"bool_to_int", 38>;
def CK_FloatingCast : I32EnumAttrCase<"floating", 39>;
// CK_CPointerToObjCPointerCast
// CK_BlockPointerToObjCPointerCast
// CK_AnyPointerToBlockPointerCast
// CK_ObjCObjectLValueCast
// CK_FloatingRealToComplex
// CK_FloatingComplexToReal
// CK_FloatingComplexToBoolean
def CK_FloatingComplexCast : I32EnumAttrCase<"float_complex", 47>;
// CK_FloatingComplexToIntegralComplex
// CK_IntegralRealToComplex
def CK_IntegralComplexToReal : I32EnumAttrCase<"int_complex_to_real", 50>;
def CK_IntegralComplexToBoolean : I32EnumAttrCase<"int_complex_to_bool", 51>;
def CK_IntegralComplexCast : I32EnumAttrCase<"int_complex", 52>;
def CK_IntegralComplexToFloatingComplex
: I32EnumAttrCase<"int_complex_to_float_complex", 53>;
// CK_ARCProduceObject
// CK_ARCConsumeObject
// CK_ARCReclaimReturnedObject
// CK_ARCExtendBlockObject
// CK_AtomicToNonAtomic
// CK_NonAtomicToAtomic
// CK_CopyAndAutoreleaseBlockObject
// CK_BuiltinFnToFnPtr
// CK_ZeroToOCLOpaqueType
def CK_AddressSpaceConversion : I32EnumAttrCase<"address_space", 63>;
// CK_IntToOCLSampler
// CK_HLSLVectorTruncation
// CK_HLSLArrayRValue
// CK_HLSLElementwiseCast
// CK_HLSLAggregateSplatCast

// Enums below are specific to CIR and don't have a correspondence to classic
// codegen:
def CK_BooleanToFloat : I32EnumAttrCase<"bool_to_float", 1000>;

def CastKind : I32EnumAttr<
"CastKind",
"cast kind",
[CK_BitCast, CK_ArrayToPointerDecay, CK_MemberPointerToBoolean,
CK_IntegralToPointer, CK_PointerToIntegral, CK_PointerToBoolean,
CK_IntegralCast, CK_IntegralToBoolean, CK_IntegralToFloating,
CK_FloatingToIntegral, CK_FloatingToBoolean, CK_BooleanToSignedIntegral,
CK_FloatingCast, CK_FloatingComplexCast, CK_IntegralComplexToReal,
CK_IntegralComplexToBoolean, CK_IntegralComplexCast,
CK_IntegralComplexToFloatingComplex, CK_AddressSpaceConversion,
CK_BooleanToFloat]> {
let cppNamespace = "::cir";
}

def CastOp : CIR_Op<"cast",
[Pure,
DeclareOpInterfaceMethods<PromotableOpInterface>]> {
// FIXME: not all conversions are free of side effects.
let summary = "Conversion between values of different types";
let description = [{
Apply C/C++ usual conversions rules between values. Currently supported kinds:

- `array_to_ptrdecay`
- `bitcast`
- `integral`
- `int_to_bool`
- `int_to_float`
- `floating`
- `float_to_int`
- `float_to_bool`
- `ptr_to_int`
- `ptr_to_bool`
- `bool_to_int`
- `bool_to_float`
- `address_space`
- `float_to_complex`
- `int_to_complex`
- `float_complex_to_real`
- `int_complex_to_real`
- `float_complex_to_bool`
- `int_complex_to_bool`
- `float_complex`
- `float_complex_to_int_complex`
- `int_complex`
- `int_complex_to_float_complex`

This is effectively a subset of the rules from
`llvm-project/clang/include/clang/AST/OperationKinds.def`; but note that some
of the conversions aren't implemented in terms of `cir.cast`, `lvalue-to-rvalue`
for instance is modeled as a regular `cir.load`.

```mlir
%4 = cir.cast (int_to_bool, %3 : i32), !cir.bool
...
%x = cir.cast(array_to_ptrdecay, %0 : !cir.ptr<!cir.array<i32 x 10>>), !cir.ptr<i32>
```
}];

let arguments = (ins CastKind:$kind, CIR_AnyType:$src);
let results = (outs CIR_AnyType:$result);

let assemblyFormat = [{
`(` $kind `,` $src `:` type($src) `)`
`,` type($result) attr-dict
}];

// The input and output types should match the cast kind.
let hasVerifier = 1;
let hasFolder = 1;
}

//===----------------------------------------------------------------------===//
// ConstantOp
//===----------------------------------------------------------------------===//
Expand Down
12 changes: 10 additions & 2 deletions clang/include/clang/CIR/MissingFeatures.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,23 +77,31 @@ struct MissingFeatures {
static bool opUnaryPromotionType() { return false; }

// Misc
static bool scalarConversionOpts() { return false; }
static bool cxxABI() { return false; }
static bool tryEmitAsConstant() { return false; }
static bool constructABIArgDirectExtend() { return false; }
static bool opGlobalViewAttr() { return false; }
static bool lowerModeOptLevel() { return false; }
static bool opTBAA() { return false; }
static bool opCmp() { return false; }
static bool objCLifetime() { return false; }
static bool emitNullabilityCheck() { return false; }
static bool astVarDeclInterface() { return false; }
static bool stackSaveOp() { return false; }
static bool aggValueSlot() { return false; }

static bool unsizedTypes() { return false; }
static bool fpConstraints() { return false; }
static bool sanitizers() { return false; }
static bool addHeapAllocSiteMetadata() { return false; }
static bool targetCodeGenInfoGetNullPointer() { return false; }
static bool CGFPOptionsRAII() { return false; }

// Missing types
static bool dataMemberType() { return false; }
static bool matrixType() { return false; }
static bool methodType() { return false; }
static bool scalableVectors() { return false; }
static bool unsizedTypes() { return false; }
static bool vectorType() { return false; }
};

Expand Down
9 changes: 9 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#define LLVM_CLANG_LIB_CIR_CODEGEN_CIRGENBUILDER_H

#include "CIRGenTypeCache.h"
#include "clang/CIR/MissingFeatures.h"

#include "clang/CIR/Dialect/Builder/CIRBaseBuilder.h"
#include "clang/CIR/MissingFeatures.h"
Expand Down Expand Up @@ -43,6 +44,14 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
assert(!cir::MissingFeatures::unsizedTypes());
return false;
}

bool isInt(mlir::Type i) { return mlir::isa<cir::IntType>(i); }

// Creates constant nullptr for pointer type ty.
cir::ConstantOp getNullPtr(mlir::Type ty, mlir::Location loc) {
assert(!cir::MissingFeatures::targetCodeGenInfoGetNullPointer());
return create<cir::ConstantOp>(loc, ty, getConstPtrAttr(ty, 0));
}
};

} // namespace clang::CIRGen
Expand Down
7 changes: 7 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,13 @@ mlir::Value CIRGenFunction::emitAlloca(StringRef name, mlir::Type ty,
return addr;
}

mlir::Value CIRGenFunction::createDummyValue(mlir::Location loc,
clang::QualType qt) {
mlir::Type t = convertType(qt);
CharUnits alignment = getContext().getTypeAlignInChars(qt);
return builder.createDummyValue(loc, t, alignment);
}

/// This creates an alloca and inserts it at the current insertion point of the
/// builder.
Address CIRGenFunction::createTempAlloca(mlir::Type ty, CharUnits align,
Expand Down
Loading
Loading