diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td index 147e5c1cdd72..e282d3e8cc6b 100644 --- a/clang/include/clang/CIR/Dialect/IR/CIROps.td +++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td @@ -2844,6 +2844,22 @@ def CIR_InlineAsmOp : CIR_Op<"asm", [RecursiveMemoryEffects]> { }]; } +//===----------------------------------------------------------------------===// +// UnreachableOp +//===----------------------------------------------------------------------===// + +def UnreachableOp : CIR_Op<"unreachable", [Terminator]> { + let summary = "invoke immediate undefined behavior"; + let description = [{ + If the program control flow reaches a `cir.unreachable` operation, the + program exhibits undefined behavior immediately. This operation is useful + in cases where the unreachability of a program point needs to be explicitly + marked. + }]; + + let assemblyFormat = "attr-dict"; +} + //===----------------------------------------------------------------------===// // Operations Lowered Directly to LLVM IR // diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp index 16e9668b9345..0c351c7ea1b7 100644 --- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp @@ -451,6 +451,14 @@ RValue CIRGenFunction::buildBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID, return RValue::get(emitBuiltinObjectSize(E->getArg(0), Type, ResType, /*EmittedE=*/nullptr, IsDynamic)); } + case Builtin::BI__builtin_unreachable: { + buildUnreachable(E->getExprLoc()); + + // We do need to preserve an insertion point. + builder.createBlock(builder.getBlock()->getParent()); + + return RValue::get(nullptr); + } case Builtin::BImemcpy: case Builtin::BI__builtin_memcpy: case Builtin::BImempcpy: diff --git a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp index 9163d95a9635..982037a3c8b2 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExpr.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExpr.cpp @@ -2538,6 +2538,12 @@ LValue CIRGenFunction::buildLoadOfReferenceLValue(LValue RefLVal, PointeeBaseInfo); } +void CIRGenFunction::buildUnreachable(SourceLocation Loc) { + if (SanOpts.has(SanitizerKind::Unreachable)) + llvm_unreachable("NYI"); + builder.create(getLoc(Loc)); +} + //===----------------------------------------------------------------------===// // CIR builder helpers //===----------------------------------------------------------------------===// diff --git a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp index 22559ce36ad5..42b06cfe7337 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp @@ -757,7 +757,9 @@ mlir::Value CIRGenFunction::buildCXXNewExpr(const CXXNewExpr *E) { EnterNewDeleteCleanup(*this, E, allocation, allocSize, allocAlign, allocatorArgs); operatorDeleteCleanup = EHStack.stable_begin(); - // FIXME: cleanupDominator = Builder.CreateUnreachable(); + cleanupDominator = + builder.create(getLoc(E->getSourceRange())) + .getOperation(); } assert((allocSize == allocSizeWithoutCookie) == diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp index e7651a86add5..77c8d05f61c2 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp @@ -681,6 +681,7 @@ CIRGenFunction::generateCode(clang::GlobalDecl GD, mlir::cir::FuncOp Fn, } if (SanOpts.has(SanitizerKind::Return) || shouldEmitUnreachable) { // TODO: builder.createUnreachable(); + assert(!UnimplementedFeature::unreachableOp()); builder.clearInsertionPoint(); } } diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index ea4a3ffae685..e0dedde5ced2 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -1516,6 +1516,10 @@ class CIRGenFunction : public CIRGenTypeCache { AggValueSlot::Overlap_t MayOverlap, bool isVolatile = false); + /// Emit a reached-unreachable diagnostic if \p Loc is valid and runtime + /// checking is enabled. Otherwise, just emit an unreachable instruction. + void buildUnreachable(SourceLocation Loc); + /// /// Cleanups /// -------- diff --git a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp index 1e6e12b6e076..a14cbb03d98b 100644 --- a/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp @@ -2212,8 +2212,8 @@ void CIRGenItaniumCXXABI::buildBadCastCall(CIRGenFunction &CGF, assert(!UnimplementedFeature::setCallingConv()); CGF.buildRuntimeCall(loc, getBadCastFn(CGF)); - // TODO(cir): mark the current insertion point as unreachable. - assert(!UnimplementedFeature::unreachableOp()); + CGF.getBuilder().create(loc); + CGF.getBuilder().clearInsertionPoint(); } static CharUnits computeOffsetHint(ASTContext &Context, diff --git a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp index 8b9a4357dc46..b7bd43abc9ca 100644 --- a/clang/lib/CIR/CodeGen/CIRGenStmt.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenStmt.cpp @@ -136,21 +136,6 @@ mlir::LogicalResult CIRGenFunction::buildStmt(const Stmt *S, mlir::Block *outgoing = builder.getInsertionBlock(); assert(outgoing && "expression emission cleared block!"); - // FIXME: Should we mimic LLVM emission here? - // The expression emitters assume (reasonably!) that the insertion - // point is always set. To maintain that, the call-emission code - // for noreturn functions has to enter a new block with no - // predecessors. We want to kill that block and mark the current - // insertion point unreachable in the common case of a call like - // "exit();". Since expression emission doesn't otherwise create - // blocks with no predecessors, we can just test for that. - // However, we must be careful not to do this to our incoming - // block, because *statement* emission does sometimes create - // reachable blocks which will have no predecessors until later in - // the function. This occurs with, e.g., labels that are not - // reachable by fallthrough. - if (incoming != outgoing && outgoing->use_empty()) - assert(0 && "not implemented"); break; } diff --git a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp index 913d5ad4ca67..e6cc2664ccb7 100644 --- a/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp +++ b/clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp @@ -2206,6 +2206,19 @@ class CIRStackRestoreLowering } }; +class CIRUnreachableLowering + : public mlir::OpConversionPattern { +public: + using OpConversionPattern::OpConversionPattern; + + mlir::LogicalResult + matchAndRewrite(mlir::cir::UnreachableOp op, OpAdaptor adaptor, + mlir::ConversionPatternRewriter &rewriter) const override { + rewriter.replaceOpWithNewOp(op); + return mlir::success(); + } +}; + void populateCIRToLLVMConversionPatterns(mlir::RewritePatternSet &patterns, mlir::TypeConverter &converter) { patterns.add(patterns.getContext()); @@ -2221,8 +2234,8 @@ void populateCIRToLLVMConversionPatterns(mlir::RewritePatternSet &patterns, CIRPtrDiffOpLowering, CIRCopyOpLowering, CIRMemCpyOpLowering, CIRFAbsOpLowering, CIRVTableAddrPointOpLowering, CIRVectorCreateLowering, CIRVectorInsertLowering, CIRVectorExtractLowering, CIRVectorCmpOpLowering, - CIRStackSaveLowering, CIRStackRestoreLowering>(converter, - patterns.getContext()); + CIRStackSaveLowering, CIRStackRestoreLowering, CIRUnreachableLowering>( + converter, patterns.getContext()); } namespace { diff --git a/clang/test/CIR/CodeGen/dynamic-cast.cpp b/clang/test/CIR/CodeGen/dynamic-cast.cpp index 52e7a3cee3d0..f9648ff72f08 100644 --- a/clang/test/CIR/CodeGen/dynamic-cast.cpp +++ b/clang/test/CIR/CodeGen/dynamic-cast.cpp @@ -48,6 +48,7 @@ Derived &ref_cast(Base &b) { // CHECK-NEXT: %[[#V18:]] = cir.unary(not, %[[#V17]]) : !cir.bool, !cir.bool // CHECK-NEXT: cir.if %[[#V18]] { // CHECK-NEXT: cir.call @__cxa_bad_cast() : () -> () +// CHECK-NEXT: cir.unreachable // CHECK-NEXT: } // CHECK-NEXT: %{{.+}} = cir.cast(bitcast, %[[#V16]] : !cir.ptr), !cir.ptr diff --git a/clang/test/CIR/CodeGen/unreachable.cpp b/clang/test/CIR/CodeGen/unreachable.cpp new file mode 100644 index 000000000000..c617fe8c6212 --- /dev/null +++ b/clang/test/CIR/CodeGen/unreachable.cpp @@ -0,0 +1,28 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s + +void foo(); + +void basic() { + foo(); + __builtin_unreachable(); +} + +// CHECK: cir.func @_Z5basicv() +// CHECK-NEXT: cir.call @_Z3foov() : () -> () +// CHECK-NEXT: cir.unreachable +// CHECK-NEXT: } + +void code_after_unreachable() { + foo(); + __builtin_unreachable(); + foo(); +} + +// CHECK: cir.func @_Z22code_after_unreachablev() +// CHECK: cir.call @_Z3foov() : () -> () +// CHECK: cir.unreachable +// CHECK: ^{{.+}}: +// CHECK: cir.call @_Z3foov() : () -> () +// CHECK: cir.return +// CHECK: } diff --git a/clang/test/CIR/IR/unreachable.cir b/clang/test/CIR/IR/unreachable.cir new file mode 100644 index 000000000000..d057f47ee2b3 --- /dev/null +++ b/clang/test/CIR/IR/unreachable.cir @@ -0,0 +1,9 @@ +// RUN: cir-opt %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s + +cir.func @test() { + cir.unreachable +} + +// CHECK: cir.func @test +// CHECK-NEXT: cir.unreachable diff --git a/clang/test/CIR/Lowering/intrinsics.cir b/clang/test/CIR/Lowering/intrinsics.cir new file mode 100644 index 000000000000..f3bcf9fba492 --- /dev/null +++ b/clang/test/CIR/Lowering/intrinsics.cir @@ -0,0 +1,10 @@ +// RUN: cir-opt %s -cir-to-llvm -o - | FileCheck %s -check-prefix=MLIR + +module { + cir.func @test_unreachable() { + cir.unreachable + } + + // MLIR: llvm.func @test_unreachable() + // MLIR-NEXT: llvm.unreachable +}