[clang] [CIR] Propagate type conversion failures in DirectToLLVM (PR #224237)
Akimasa Watanuki via cfe-commits
cfe-commits at lists.llvm.org
Thu Sep 17 02:13:15 PDT 2026
https://github.com/Men-cotton created https://github.com/llvm/llvm-project/pull/224237
Propagate failed type and constant conversions through DirectToLLVM so unsupported types produce legalization failures instead of invalid LLVM operations or a void function result.
Assisted-by: Codex / GPT-6
>From d3cc1d7440fb9e544a93e95131a23f2ba398fbf9 Mon Sep 17 00:00:00 2001
From: mencotton <mencotton0410 at gmail.com>
Date: Tue, 15 Sep 2026 20:47:50 +0900
Subject: [PATCH] [CIR] Propagate type conversion failures in DirectToLLVM
Propagate failed type and constant conversions through DirectToLLVM so unsupported types produce legalization failures instead of invalid LLVM operations or a void function result.
Assisted-by: Codex / GPT-6
---
.../CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp | 62 ++++--
.../CIR/Lowering/DirectToLLVM/LowerToLLVM.h | 7 +-
.../Lowering/unsupported-type-conversions.cir | 178 ++++++++++++++++++
3 files changed, 231 insertions(+), 16 deletions(-)
create mode 100644 clang/test/CIR/Lowering/unsupported-type-conversions.cir
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
index 596c0d72264a0..6280b853d6e79 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
@@ -424,10 +424,7 @@ mlir::Value lowerCirAttrAsValue(mlir::Operation *parentOp,
LLVMBlockAddressInfo *blockInfoAddr) {
CIRAttrToValue valueConverter(parentOp, rewriter, symbolTables, converter,
blockInfoAddr);
- mlir::Value value = valueConverter.visit(attr);
- if (!value)
- llvm_unreachable("unhandled attribute type");
- return value;
+ return valueConverter.visit(attr);
}
void convertSideEffectForCall(mlir::Operation *callOp, bool isNothrow,
@@ -783,6 +780,8 @@ mlir::Value CIRAttrToValue::visitCirAttr(cir::ConstArrayAttr attr) {
if (auto arrayAttr = mlir::dyn_cast<mlir::ArrayAttr>(attr.getElts())) {
for (auto [idx, elt] : llvm::enumerate(arrayAttr)) {
mlir::Value init = visit(elt);
+ if (!init)
+ return {};
result =
mlir::LLVM::InsertValueOp::create(rewriter, loc, result, init, idx);
}
@@ -827,8 +826,11 @@ mlir::Value CIRAttrToValue::visitCirAttr(cir::ConstRecordAttr constRecord) {
++paddingItr;
}
- result = mlir::LLVM::InsertValueOp::create(rewriter, loc, result,
- visit(elt), insertIdx);
+ mlir::Value init = visit(elt);
+ if (!init)
+ return {};
+ result = mlir::LLVM::InsertValueOp::create(rewriter, loc, result, init,
+ insertIdx);
++insertIdx;
}
@@ -957,6 +959,8 @@ mlir::Value CIRAttrToValue::visitCirAttr(cir::GlobalViewAttr globalAttr) {
mlir::Type sourceType = lowered.sourceType;
if (globalAttr.getIndices()) {
+ if (!sourceType)
+ return {};
llvm::SmallVector<mlir::LLVM::GEPArg> indices;
if (mlir::isa<mlir::LLVM::LLVMArrayType, mlir::LLVM::LLVMStructType>(
@@ -1004,6 +1008,8 @@ mlir::Value CIRAttrToValue::visitCirAttr(cir::TypeInfoAttr typeInfoAttr) {
for (auto [idx, elt] : llvm::enumerate(typeInfoAttr.getData())) {
mlir::Value init = visit(elt);
+ if (!init)
+ return {};
result =
mlir::LLVM::InsertValueOp::create(rewriter, loc, result, init, idx);
}
@@ -1037,6 +1043,8 @@ mlir::Value CIRAttrToValue::visitCirAttr(cir::VTableAttr vtableArr) {
for (auto [idx, elt] : llvm::enumerate(vtableArr.getData())) {
mlir::Value init = visit(elt);
+ if (!init)
+ return {};
result =
mlir::LLVM::InsertValueOp::create(rewriter, loc, result, init, idx);
}
@@ -1618,6 +1626,9 @@ mlir::LogicalResult CIRToLLVMCastOpLowering::matchAndRewrite(
// both individual scalars and entire vectors. This lowering pass handles
// both situations.
+ if (!getTypeConverter()->convertType(castOp.getType()))
+ return mlir::failure();
+
switch (castOp.getKind()) {
case cir::CastKind::array_to_ptrdecay: {
const auto ptrTy = mlir::cast<cir::PointerType>(castOp.getType());
@@ -2459,6 +2470,8 @@ mlir::LogicalResult CIRToLLVMConstantOpLowering::matchAndRewrite(
cir::ConstantOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const {
mlir::Attribute attr = op.getValue();
+ if (!getTypeConverter()->convertType(op.getType()))
+ return mlir::failure();
if (mlir::isa<cir::PoisonAttr>(attr)) {
rewriter.replaceOpWithNewOp<mlir::LLVM::PoisonOp>(
@@ -2511,6 +2524,8 @@ mlir::LogicalResult CIRToLLVMConstantOpLowering::matchAndRewrite(
if (mlir::isa<cir::GlobalViewAttr, cir::GlobalOffsetAttr>(op.getValue())) {
auto newOp = lowerCirAttrAsValue(op, op.getValue(), rewriter,
symbolTables, getTypeConverter());
+ if (!newOp)
+ return mlir::failure();
rewriter.replaceOp(op, newOp);
return mlir::success();
}
@@ -2527,6 +2542,8 @@ mlir::LogicalResult CIRToLLVMConstantOpLowering::matchAndRewrite(
} else {
const mlir::Value initVal = lowerCirAttrAsValue(
op, op.getValue(), rewriter, symbolTables, typeConverter);
+ if (!initVal)
+ return mlir::failure();
rewriter.replaceOp(op, initVal);
return mlir::success();
}
@@ -2534,17 +2551,23 @@ mlir::LogicalResult CIRToLLVMConstantOpLowering::matchAndRewrite(
mlir::dyn_cast<cir::ConstRecordAttr>(op.getValue())) {
auto initVal = lowerCirAttrAsValue(op, recordAttr, rewriter, symbolTables,
typeConverter);
+ if (!initVal)
+ return mlir::failure();
rewriter.replaceOp(op, initVal);
return mlir::success();
} else if (const auto vecTy = mlir::dyn_cast<cir::VectorType>(op.getType())) {
- rewriter.replaceOp(op,
- lowerCirAttrAsValue(op, op.getValue(), rewriter,
- symbolTables, getTypeConverter()));
+ mlir::Value initVal = lowerCirAttrAsValue(op, op.getValue(), rewriter,
+ symbolTables, getTypeConverter());
+ if (!initVal)
+ return mlir::failure();
+ rewriter.replaceOp(op, initVal);
return mlir::success();
} else if (mlir::isa<cir::RecordType>(op.getType())) {
if (mlir::isa<cir::ZeroAttr, cir::UndefAttr>(attr)) {
mlir::Value initVal =
lowerCirAttrAsValue(op, attr, rewriter, symbolTables, typeConverter);
+ if (!initVal)
+ return mlir::failure();
rewriter.replaceOp(op, initVal);
return mlir::success();
}
@@ -2777,11 +2800,12 @@ mlir::LogicalResult CIRToLLVMFuncOpLowering::matchAndRewrite(
mlir::Type resultType =
getTypeConverter()->convertType(fnType.getReturnType());
+ if (!resultType)
+ return mlir::failure();
// Create the LLVM function operation.
mlir::Type llvmFnTy = mlir::LLVM::LLVMFunctionType::get(
- resultType ? resultType : mlir::LLVM::LLVMVoidType::get(getContext()),
- signatureConversion.getConvertedTypes(),
+ resultType, signatureConversion.getConvertedTypes(),
/*isVarArg=*/fnType.isVarArg());
// If this is an alias, it needs to be lowered to llvm::AliasOp.
@@ -3000,6 +3024,8 @@ CIRToLLVMGlobalOpLowering::matchAndRewriteRegionInitializedGlobal(
CIRAttrToValue valueConverter(op, rewriter, symbolTables, typeConverter,
&blockInfoAddr);
mlir::Value value = valueConverter.visit(init);
+ if (!value)
+ return mlir::failure();
mlir::LLVM::ReturnOp::create(rewriter, loc, value);
return mlir::success();
}
@@ -3958,6 +3984,8 @@ static void prepareTypeConverter(mlir::LLVMTypeConverter &converter,
});
converter.addConversion([&](cir::FuncType type) -> std::optional<mlir::Type> {
auto result = converter.convertType(type.getReturnType());
+ if (!result)
+ return std::nullopt;
llvm::SmallVector<mlir::Type> arguments;
arguments.reserve(type.getNumInputs());
if (converter.convertTypes(type.getInputs(), arguments).failed())
@@ -4801,6 +4829,9 @@ getValueForVTableSymbol(mlir::Operation *op,
return {};
}
+ if (!eltType)
+ return {};
+
return mlir::LLVM::AddressOfOp::create(
rewriter, op->getLoc(),
mlir::LLVM::LLVMPointerType::get(op->getContext()), nameAttr.getValue());
@@ -4816,7 +4847,7 @@ mlir::LogicalResult CIRToLLVMVTableAddrPointOpLowering::matchAndRewrite(
mlir::Value symAddr = getValueForVTableSymbol(
op, rewriter, symbolTables, converter, op.getNameAttr(), eltType);
if (!symAddr)
- return op.emitError() << "Unable to get value for vtable symbol";
+ return mlir::failure();
offsets = llvm::SmallVector<mlir::LLVM::GEPArg>{
0, op.getAddressPointAttr().getIndex(),
@@ -4874,6 +4905,8 @@ mlir::LogicalResult CIRToLLVMVTTAddrPointOpLowering::matchAndRewrite(
llvmAddr =
getValueForVTableSymbol(op, rewriter, symbolTables, getTypeConverter(),
op.getNameAttr(), eltType);
+ if (!llvmAddr)
+ return mlir::failure();
assert(eltType && "Shouldn't ever be missing an eltType here");
offsets.push_back(0);
offsets.push_back(adaptor.getOffset());
@@ -5421,8 +5454,11 @@ mlir::LogicalResult CIRToLLVMInlineAsmOpLowering::matchAndRewrite(
cir::InlineAsmOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const {
mlir::Type llResTy;
- if (op.getNumResults())
+ if (op.getNumResults()) {
llResTy = getTypeConverter()->convertType(op.getType(0));
+ if (!llResTy)
+ return mlir::failure();
+ }
cir::AsmFlavor dialect = op.getAsmFlavor();
mlir::LLVM::AsmDialect llDialect = dialect == cir::AsmFlavor::x86_att
diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
index 146b31b907fcc..c48722fec38fc 100644
--- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
+++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.h
@@ -24,9 +24,10 @@ namespace direct {
struct LLVMBlockAddressInfo;
-/// Convert a CIR attribute to an LLVM attribute. May use the datalayout for
-/// lowering attributes to-be-stored in memory. When the attribute may contain
-/// block address attributes, `blockInfoAddr` is used to resolve them.
+/// Convert a CIR attribute to an LLVM value, or return null on failure.
+/// May use the datalayout for lowering attributes to-be-stored in memory. When
+/// the attribute may contain block address attributes, `blockInfoAddr` is used
+/// to resolve them.
mlir::Value lowerCirAttrAsValue(mlir::Operation *parentOp, mlir::Attribute attr,
mlir::ConversionPatternRewriter &rewriter,
mlir::SymbolTableCollection &symbolTables,
diff --git a/clang/test/CIR/Lowering/unsupported-type-conversions.cir b/clang/test/CIR/Lowering/unsupported-type-conversions.cir
new file mode 100644
index 0000000000000..d4d43bf49919e
--- /dev/null
+++ b/clang/test/CIR/Lowering/unsupported-type-conversions.cir
@@ -0,0 +1,178 @@
+// RUN: cir-opt %s --split-input-file --cir-to-llvm -verify-diagnostics
+
+// Vector-of-_BitInt conversion is unsupported; propagate its failure to the
+// enclosing operation instead of constructing invalid LLVM operations.
+
+!vec = !cir.vector<4 x !cir.int<s, 7, bitint>>
+
+module attributes {cir.triple = "x86_64-unknown-linux-gnu"} {
+ // expected-error @below {{failed to legalize operation 'cir.func'}}
+ cir.func private @function_result() -> !vec
+}
+
+// -----
+
+!vec = !cir.vector<4 x !cir.int<s, 7, bitint>>
+
+module attributes {cir.triple = "x86_64-unknown-linux-gnu"} {
+ // The initializer requests FuncType conversion before the function lowers.
+ cir.global external @function_view = #cir.global_view<@callee> : !cir.ptr<!cir.void>
+
+ // expected-error @below {{failed to legalize operation 'cir.func'}}
+ cir.func private @callee() -> !vec
+}
+
+// -----
+
+!vec = !cir.vector<4 x !cir.int<s, 7, bitint>>
+
+module attributes {cir.triple = "x86_64-unknown-linux-gnu"} {
+ cir.func @constant_result() {
+ // expected-error @below {{failed to legalize operation 'cir.const'}}
+ %0 = cir.const #cir.zero : !vec
+ cir.return
+ }
+}
+
+// -----
+
+!vec = !cir.vector<4 x !cir.int<s, 7, bitint>>
+
+module attributes {cir.triple = "x86_64-unknown-linux-gnu"} {
+ cir.func @cast_result() {
+ %0 = cir.const #cir.zero : !cir.vector<4 x !cir.int<s, 7>>
+ // expected-error @below {{failed to legalize operation 'cir.cast'}}
+ %1 = cir.cast bitcast %0 : !cir.vector<4 x !cir.int<s, 7>> -> !vec
+ cir.return
+ }
+}
+
+// -----
+
+!vec = !cir.vector<4 x !cir.int<s, 7, bitint>>
+
+module attributes {cir.triple = "x86_64-unknown-linux-gnu"} {
+ cir.func @asm_result() {
+ // expected-error @below {{failed to legalize operation 'cir.asm'}}
+ %0 = cir.asm(x86_att, out = [], in = [], in_out = [], {"" "=r"}) -> !vec
+ cir.return
+ }
+}
+
+// -----
+
+!vec = !cir.vector<4 x !cir.int<s, 7, bitint>>
+!ptr = !cir.ptr<!cir.void>
+
+module attributes {cir.triple = "x86_64-unknown-linux-gnu"} {
+ // Lower the constant before its target to exercise symbol-type failure.
+ cir.func @constant_global_view() {
+ // expected-error @below {{failed to legalize operation 'cir.const'}}
+ %0 = cir.const #cir.global_view<@target, [0 : i32]> : !ptr
+ cir.return
+ }
+
+ cir.global external @target = #cir.zero : !vec
+}
+
+// -----
+
+!vec = !cir.vector<4 x !cir.int<s, 7, bitint>>
+!ptr = !cir.ptr<!cir.void>
+
+module attributes {cir.triple = "x86_64-unknown-linux-gnu"} {
+ // Lower the constant before its target to exercise symbol-type failure.
+ cir.func @constant_array_view() {
+ // expected-error @below {{failed to legalize operation 'cir.const'}}
+ %0 = cir.const #cir.const_array<[#cir.global_view<@target, [0 : i32]> : !ptr]> : !cir.array<!ptr x 1>
+ cir.return
+ }
+
+ cir.global external @target = #cir.zero : !vec
+}
+
+// -----
+
+!vec = !cir.vector<4 x !cir.int<s, 7, bitint>>
+!ptr = !cir.ptr<!cir.void>
+
+module attributes {cir.triple = "x86_64-unknown-linux-gnu"} {
+ // Lower the constant before its target to exercise symbol-type failure.
+ cir.func @constant_record_view() {
+ // expected-error @below {{failed to legalize operation 'cir.const'}}
+ %0 = cir.const #cir.const_record<{#cir.global_view<@target, [0 : i32]> : !ptr}> : !cir.struct<{data !ptr}>
+ cir.return
+ }
+
+ cir.global external @target = #cir.zero : !vec
+}
+
+// -----
+
+!vec = !cir.vector<4 x !cir.int<s, 7, bitint>>
+!ptr = !cir.ptr<!cir.void>
+
+module attributes {cir.triple = "x86_64-unknown-linux-gnu"} {
+ // Propagate initializer failure before lowering the referenced global.
+ // expected-error @below {{failed to legalize operation 'cir.global'}}
+ cir.global external @global_view = #cir.global_view<@target, [0 : i32]> : !ptr
+
+ cir.global external @target = #cir.zero : !vec
+}
+
+// -----
+
+!vec = !cir.vector<4 x !cir.int<s, 7, bitint>>
+!ptr = !cir.ptr<!cir.void>
+
+module attributes {cir.triple = "x86_64-unknown-linux-gnu"} {
+ // Propagate initializer failure before lowering the referenced global.
+ // expected-error @below {{failed to legalize operation 'cir.global'}}
+ cir.global external @typeinfo_view = #cir.typeinfo<{#cir.global_view<@target, [0 : i32]> : !ptr}> : !cir.struct<{data !ptr}>
+
+ cir.global external @target = #cir.zero : !vec
+}
+
+// -----
+
+!vec = !cir.vector<4 x !cir.int<s, 7, bitint>>
+!ptr = !cir.ptr<!cir.void>
+
+module attributes {cir.triple = "x86_64-unknown-linux-gnu"} {
+ // Propagate initializer failure before lowering the referenced global.
+ // expected-error @below {{failed to legalize operation 'cir.global'}}
+ cir.global external @vtable_view = #cir.vtable<{#cir.const_array<[#cir.global_view<@target, [0 : i32]> : !ptr]> : !cir.array<!ptr x 1>}> : !cir.struct<{data !cir.array<!ptr x 1>}>
+
+ cir.global external @target = #cir.zero : !vec
+}
+
+// -----
+
+!vec = !cir.vector<4 x !cir.int<s, 7, bitint>>
+
+module attributes {cir.triple = "x86_64-unknown-linux-gnu"} {
+ // The address point requests the symbol type before the global lowers.
+ cir.func @vtable_symbol() {
+ // expected-error @below {{failed to legalize operation 'cir.vtable.address_point'}}
+ %0 = cir.vtable.address_point(@target, address_point = <index = 0, offset = 0>) : !cir.vptr
+ cir.return
+ }
+
+ cir.global "private" external @target : !cir.struct<{data !cir.array<!vec x 1>}>
+}
+
+// -----
+
+!vec = !cir.vector<4 x !cir.int<s, 7, bitint>>
+!ptr = !cir.ptr<!cir.void>
+
+module attributes {cir.triple = "x86_64-unknown-linux-gnu"} {
+ // The address point requests the symbol type before the global lowers.
+ cir.func @vtt_symbol() {
+ // expected-error @below {{failed to legalize operation 'cir.vtt.address_point'}}
+ %0 = cir.vtt.address_point @target, offset = 0 -> !cir.ptr<!ptr>
+ cir.return
+ }
+
+ cir.global "private" external @target : !cir.array<!vec x 1>
+}
More information about the cfe-commits
mailing list