diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/clang/include/clang/Basic/CodeGenOptions.def @@ -52,6 +52,7 @@ CODEGENOPT(DisableFree , 1, 0) ///< Don't free memory. CODEGENOPT(DiscardValueNames , 1, 0) ///< Discard Value Names from the IR (LLVMContext flag) +CODEGENOPT(DisableNoundefArgs , 1, 0) ///< Disable emitting `noundef` attributes on IR call arguments CODEGENOPT(DisableGCov , 1, 0) ///< Don't run the GCov pass, for testing. CODEGENOPT(DisableLLVMPasses , 1, 0) ///< Don't run any LLVM IR passes to get ///< the pristine IR generated by the diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -3953,6 +3953,8 @@ HelpText<"Disable freeing of memory on exit">; def discard_value_names : Flag<["-"], "discard-value-names">, HelpText<"Discard value names in LLVM IR">; +def disable_noundef_args : Flag<["-"], "disable-noundef-args">, + HelpText<"Disable emitting noundef attribute in LLVM IR">; def load : Separate<["-"], "load">, MetaVarName<"">, HelpText<"Load the named plugin (dynamic shared object)">; def plugin : Separate<["-"], "plugin">, MetaVarName<"">, diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -1876,6 +1876,54 @@ llvm::for_each(NBA->builtinNames(), AddNoBuiltinAttr); } +static bool DetermineNoUndef(QualType QTy, CodeGenTypes &Types, + const llvm::DataLayout &DL, const ABIArgInfo &AI, + bool CheckCoerce = true) { + llvm::Type *Ty = Types.ConvertTypeForMem(QTy); + if (AI.getKind() == ABIArgInfo::Indirect) + return true; + if (AI.getKind() == ABIArgInfo::Extend) + return true; + if (!DL.typeSizeEqualsStoreSize(Ty)) + // TODO: This will result in a modest amount of values not marked noundef + // when they could be. We care about values that *invisibly* contain undef + // bits from the perspective of LLVM IR. + return false; + if (CheckCoerce && AI.canHaveCoerceToType()) { + llvm::Type *CoerceTy = AI.getCoerceToType(); + if (DL.getTypeSizeInBits(CoerceTy) > DL.getTypeSizeInBits(Ty)) + // If we're coercing to a type with a greater size than the canonical one, + // we're introducing new undef bits. + // Coercing to a type of smaller or equal size is ok, as we know that + // there's no internal padding (typeSizeEqualsStoreSize). + return false; + } + if (QTy->isExtIntType()) + return true; + if (QTy->isReferenceType()) + return true; + if (QTy->isNullPtrType()) + return false; + if (QTy->isMemberPointerType()) + // TODO: Some member pointers are `noundef`, but it depends on the ABI. For + // now, never mark them. + return false; + if (QTy->isScalarType()) { + if (const ComplexType *Complex = dyn_cast(QTy)) + return DetermineNoUndef(Complex->getElementType(), Types, DL, AI, false); + return true; + } + if (const VectorType *Vector = dyn_cast(QTy)) + return DetermineNoUndef(Vector->getElementType(), Types, DL, AI, false); + if (const MatrixType *Matrix = dyn_cast(QTy)) + return DetermineNoUndef(Matrix->getElementType(), Types, DL, AI, false); + if (const ArrayType *Array = dyn_cast(QTy)) + return DetermineNoUndef(Array->getElementType(), Types, DL, AI, false); + + // TODO: Some structs may be `noundef`, in specific situations. + return false; +} + /// Construct the IR attribute list of a function or call. /// /// When adding an attribute, please consider where it should be handled: @@ -2075,6 +2123,22 @@ QualType RetTy = FI.getReturnType(); const ABIArgInfo &RetAI = FI.getReturnInfo(); + const llvm::DataLayout &DL = getDataLayout(); + + bool HasCppLinkage = getLangOpts().CPlusPlus || getLangOpts().OpenCLCPlusPlus; + const FunctionDecl *FDecl; + if (TargetDecl && (FDecl = dyn_cast(TargetDecl))) { + HasCppLinkage &= !FDecl->isExternC(); + } + + // Determine if the return type could be partially undef + if (HasCppLinkage) { + if (!RetTy->isVoidType() && RetAI.getKind() != ABIArgInfo::Indirect && + DetermineNoUndef(RetTy, getTypes(), DL, RetAI)) { + RetAttrs.addAttribute(llvm::Attribute::NoUndef); + } + } + switch (RetAI.getKind()) { case ABIArgInfo::Extend: if (RetAI.isSignExt()) @@ -2160,6 +2224,12 @@ } } + // Decide whether the argument we're handling could be partially undef + bool ArgNoUndef = DetermineNoUndef(ParamType, getTypes(), DL, AI); + if (!CodeGenOpts.DisableNoundefArgs && ArgNoUndef) { + Attrs.addAttribute(llvm::Attribute::NoUndef); + } + // 'restrict' -> 'noalias' is done in EmitFunctionProlog when we // have the corresponding parameter variable. It doesn't make // sense to do it here because parameters are so messed up. diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -924,6 +924,7 @@ Opts.DisableFree = Args.hasArg(OPT_disable_free); Opts.DiscardValueNames = Args.hasArg(OPT_discard_value_names); + Opts.DisableNoundefArgs = Args.hasArg(OPT_disable_noundef_args); Opts.DisableTailCalls = Args.hasArg(OPT_mdisable_tail_calls); Opts.NoEscapingBlockTailCalls = Args.hasArg(OPT_fno_escaping_block_tail_calls); diff --git a/clang/test/CodeGen/indirect-noundef.cpp b/clang/test/CodeGen/indirect-noundef.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/indirect-noundef.cpp @@ -0,0 +1,30 @@ +// RUN: %clang_cc1 -x c++ -triple x86_64-unknown-unknown -O0 -emit-llvm -o - %s | FileCheck %s + +union u1 { int val; }; + +// CHECK: @indirect_callee_int_ptr = global i32 (i32)* +int (*indirect_callee_int_ptr)(int); +// CHECK: @indirect_callee_union_ptr = global i32 (i32)* +union u1 (*indirect_callee_union_ptr)(union u1); + +// CHECK-LABEL: define noundef i32 @{{.*}}indirect_callee_int{{.*}}(i32 noundef % +int indirect_callee_int(int a) { return a; } +// CHECK-LABEL: define i32 @{{.*}}indirect_callee_union{{.*}}(i32 % +union u1 indirect_callee_union(union u1 a) { return a; } + +int main() { + // CHECK: call noundef i32 @{{.*}}indirect_callee_int{{.*}}(i32 noundef 0) + indirect_callee_int(0); + // CHECK: call i32 @{{.*}}indirect_callee_union{{.*}}(i32 % + indirect_callee_union((union u1){0}); + + indirect_callee_int_ptr = indirect_callee_int; + indirect_callee_union_ptr = indirect_callee_union; + + // CHECK: call noundef i32 %{{.*}}(i32 noundef 0) + indirect_callee_int_ptr(0); + // CHECK: call i32 %{{.*}}(i32 % + indirect_callee_union_ptr((union u1){}); + + return 0; +}