diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -1856,6 +1856,19 @@ return; } + if (Volatile && isa(Addr.getPointer()) && + !llvm::NullPointerIsDefined(CurFn, Addr.getAddressSpace())) { + // If this is a simple volatile store to a null pointer + // (and said null pointer is not defined for this address space), + // then we have a common idiom "please crash now". + // However, that is not the semantics LLVM IR provides. Volatile operations + // are not defined as trapping in LLVM IR, so this store will be silently + // dropped by the optimization passes, and no trap will happen. + // Since this is a common idiom, for now, directly codegen it as trap. + EmitTrapCall(llvm::Intrinsic::trap); + return; + } + llvm::StoreInst *Store = Builder.CreateStore(Value, Addr, Volatile); if (isNontemporal) { llvm::MDNode *Node = diff --git a/clang/test/CodeGen/please-crash-now.c b/clang/test/CodeGen/please-crash-now.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/please-crash-now.c @@ -0,0 +1,62 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py +// RUN: %clang_cc1 -x c %s -triple x86_64-linux-gnu -emit-llvm -o - | FileCheck %s --check-prefixes=ALL,WITHOUT_NULL -implicit-check-not="call void @llvm.trap()" +// RUN: %clang_cc1 -x c++ %s -triple x86_64-linux-gnu -emit-llvm -o - | FileCheck %s --check-prefixes=ALL,WITHOUT_NULL -implicit-check-not="call void @llvm.trap()" +// RUN: %clang_cc1 -x c %s -triple x86_64-linux-gnu -emit-llvm -o - -fno-delete-null-pointer-checks | FileCheck %s --check-prefixes=ALL -implicit-check-not="call void @llvm.trap()" +// RUN: %clang_cc1 -x c++ %s -triple x86_64-linux-gnu -emit-llvm -o - -fno-delete-null-pointer-checks | FileCheck %s --check-prefixes=ALL -implicit-check-not="call void @llvm.trap()" + +// ALL-LABEL: define {{.*}}store_into_volatile_nullptr_via_dereference +// WITHOUT_NULL: call void @llvm.trap() +void store_into_volatile_nullptr_via_dereference() { + (*((volatile int *)(0))) = 0; +} + +// Not the pattern, must be a simple dereference. +// ALL-LABEL: define {{.*}}store_into_volatile_nullptr_via_subscript_operator +// WITHOUT_NULL: call void @llvm.trap() +void store_into_volatile_nullptr_via_subscript_operator() { + ((volatile int *)(0))[0] = 0; +} + +// For the rest of the tests, we don't emit trap. + +// Just dereferencing is not enough. +// ALL-LABEL: define {{.*}}n0 +void n0() { + *((int *)(0)); +} + +// Just dereferencing via subscript expression is not enough either. +// ALL-LABEL: define {{.*}}n1 +void n1() { + ((int *)(0))[0]; +} + +// Just dereferencing of a volatile pointer is still not enough. +// ALL-LABEL: define {{.*}}n2 +void n2() { + *((volatile int *)(0)); +} + +// Just dereferencing of a volatile pointer via subscript expression is not enough either. +// ALL-LABEL: define {{.*}}n3 +void n3() { + ((volatile int *)(0))[0]; +} + +// Almost the pattern, but the pointer is not volatile. +// ALL-LABEL: define {{.*}}n4 +void n4() { + (*((int *)(0))) = 0; +} + +// Pointer subscript is not the pattern we are looking for. +// ALL-LABEL: define {{.*}}n5 +void n5() { + ((int *)(0))[0] = 0; +} + +// A pointer with integral value of `0` is valid in this address space. +// ALL-LABEL: define {{.*}}n6 +void n6() { + (*((__attribute__((address_space(42))) volatile int *)(0))) = 0; +}