Skip to content

Commit

Permalink
[CIR][CIRGen] Introduce cir.unreachable operation (llvm#447)
Browse files Browse the repository at this point in the history
In llvm#426 we confirmed that CIR needs a `cir.unreachable` operation to
mark unreachable program points
[(discussion)](llvm/clangir#426 (comment)).
This PR adds it.
  • Loading branch information
Lancern authored and lanza committed Jun 20, 2024
1 parent 41dc49b commit f3e4650
Show file tree
Hide file tree
Showing 13 changed files with 103 additions and 20 deletions.
16 changes: 16 additions & 0 deletions clang/include/clang/CIR/Dialect/IR/CIROps.td
Original file line number Diff line number Diff line change
Expand Up @@ -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
//
Expand Down
8 changes: 8 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
6 changes: 6 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenExpr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<mlir::cir::UnreachableOp>(getLoc(Loc));
}

//===----------------------------------------------------------------------===//
// CIR builder helpers
//===----------------------------------------------------------------------===//
Expand Down
4 changes: 3 additions & 1 deletion clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<mlir::cir::UnreachableOp>(getLoc(E->getSourceRange()))
.getOperation();
}

assert((allocSize == allocSizeWithoutCookie) ==
Expand Down
1 change: 1 addition & 0 deletions clang/lib/CIR/CodeGen/CIRGenFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
}
Expand Down
4 changes: 4 additions & 0 deletions clang/lib/CIR/CodeGen/CIRGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
/// --------
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/CIR/CodeGen/CIRGenItaniumCXXABI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<mlir::cir::UnreachableOp>(loc);
CGF.getBuilder().clearInsertionPoint();
}

static CharUnits computeOffsetHint(ASTContext &Context,
Expand Down
15 changes: 0 additions & 15 deletions clang/lib/CIR/CodeGen/CIRGenStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
17 changes: 15 additions & 2 deletions clang/lib/CIR/Lowering/DirectToLLVM/LowerToLLVM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2206,6 +2206,19 @@ class CIRStackRestoreLowering
}
};

class CIRUnreachableLowering
: public mlir::OpConversionPattern<mlir::cir::UnreachableOp> {
public:
using OpConversionPattern<mlir::cir::UnreachableOp>::OpConversionPattern;

mlir::LogicalResult
matchAndRewrite(mlir::cir::UnreachableOp op, OpAdaptor adaptor,
mlir::ConversionPatternRewriter &rewriter) const override {
rewriter.replaceOpWithNewOp<mlir::LLVM::UnreachableOp>(op);
return mlir::success();
}
};

void populateCIRToLLVMConversionPatterns(mlir::RewritePatternSet &patterns,
mlir::TypeConverter &converter) {
patterns.add<CIRReturnLowering>(patterns.getContext());
Expand All @@ -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 {
Expand Down
1 change: 1 addition & 0 deletions clang/test/CIR/CodeGen/dynamic-cast.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<!void>), !cir.ptr<!ty_22Derived22>

Expand Down
28 changes: 28 additions & 0 deletions clang/test/CIR/CodeGen/unreachable.cpp
Original file line number Diff line number Diff line change
@@ -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: }
9 changes: 9 additions & 0 deletions clang/test/CIR/IR/unreachable.cir
Original file line number Diff line number Diff line change
@@ -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
10 changes: 10 additions & 0 deletions clang/test/CIR/Lowering/intrinsics.cir
Original file line number Diff line number Diff line change
@@ -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
}

0 comments on commit f3e4650

Please sign in to comment.