Index: include/clang/Basic/Attr.td =================================================================== --- include/clang/Basic/Attr.td +++ include/clang/Basic/Attr.td @@ -3131,3 +3131,12 @@ let Subjects = SubjectList<[Function, ObjCMethod], ErrorDiag>; let Documentation = [SpeculativeLoadHardeningDocs]; } + +def TrivialAutoInit : InheritableAttr { + let Spellings = [Clang<"trivial_auto_init", 1>]; + let Args = [EnumArgument<"TrivialAutoVarInit", "TrivialAutoVarInitKind", + ["uninitialized", "zero", "pattern"], + ["Uninitialized", "Zero", "Pattern"]>]; + let Subjects = SubjectList<[LocalVar]>; + let Documentation = [TrivialAutoInitDocs]; +} Index: include/clang/Basic/AttrDocs.td =================================================================== --- include/clang/Basic/AttrDocs.td +++ include/clang/Basic/AttrDocs.td @@ -3704,6 +3704,17 @@ }]; } +def TrivialAutoInitDocs : Documentation { + let Category = DocCatVariable; + let Content = [{ +The command-line parameter ``-ftrivial-auto-var-init=*`` can be used to +automatically initialize trivial automatic stack variables. By default, trivial +automatic stack variables are uninitialized. This attribute is used to override +the command-line parameter, and accepts a single parameter that must be one of +the following: ``uninitialized``, ``zero``, or ``pattern``. + }]; +} + def GnuInlineDocs : Documentation { let Category = DocCatFunction; let Content = [{ Index: include/clang/Basic/DiagnosticDriverKinds.td =================================================================== --- include/clang/Basic/DiagnosticDriverKinds.td +++ include/clang/Basic/DiagnosticDriverKinds.td @@ -405,4 +405,10 @@ def warn_drv_moutline_unsupported_opt : Warning< "The '%0' architecture does not support -moutline; flag ignored">, InGroup; + +def warn_drv_trivial_auto_var_init_zero_disabled : Warning< + "-ftrivial-auto-var-init=zero hasn't been enabled, using pattern initialization instead. " + "Enable it at your own peril for benchmarking purpose only with " + "-enable-trivial-auto-var-init-zero-knowing-it-will-be-removed-from-clang">; + } Index: include/clang/Basic/LangOptions.h =================================================================== --- include/clang/Basic/LangOptions.h +++ include/clang/Basic/LangOptions.h @@ -54,6 +54,11 @@ enum GCMode { NonGC, GCOnly, HybridGC }; enum StackProtectorMode { SSPOff, SSPOn, SSPStrong, SSPReq }; + // Automatic variables live on the stack, and when trivial they're usually + // uninitialized because it's undefined behavior to use them without + // initializing them. + enum class TrivialAutoVarInitKind { Uninitialized, Zero, Pattern }; + enum SignedOverflowBehaviorTy { // Default C standard behavior. SOB_Undefined, Index: include/clang/Basic/LangOptions.def =================================================================== --- include/clang/Basic/LangOptions.def +++ include/clang/Basic/LangOptions.def @@ -263,6 +263,8 @@ "type symbol visibility") ENUM_LANGOPT(StackProtector, StackProtectorMode, 2, SSPOff, "stack protector mode") +ENUM_LANGOPT(TrivialAutoVarInit, TrivialAutoVarInitKind, 2, TrivialAutoVarInitKind::Uninitialized, + "trivial automatic variable initialization") ENUM_LANGOPT(SignedOverflowBehavior, SignedOverflowBehaviorTy, 2, SOB_Undefined, "signed integer overflow handling") Index: include/clang/Driver/Options.td =================================================================== --- include/clang/Driver/Options.td +++ include/clang/Driver/Options.td @@ -1638,6 +1638,12 @@ HelpText<"Use a strong heuristic to apply stack protectors to functions">; def fstack_protector : Flag<["-"], "fstack-protector">, Group, HelpText<"Enable stack protectors for functions potentially vulnerable to stack smashing">; +def ftrivial_auto_var_init : Joined<["-"], "ftrivial-auto-var-init=">, Group, + Flags<[CC1Option]>, HelpText<"Initialize trivial automatic stack variables: uninitialized (default)" + " | pattern">, Values<"uninitialized,pattern">; +def enable_trivial_var_init_zero : Joined<["-"], "enable-trivial-auto-var-init-zero-knowing-it-will-be-removed-from-clang">, + Flags<[CC1Option]>, + HelpText<"Trivial automatic variable initialization to zero is only here for benchmarks, it'll eventually be removed, and I'm OK with that because I'm only using it to benchmark">; def fstandalone_debug : Flag<["-"], "fstandalone-debug">, Group, Flags<[CoreOption]>, HelpText<"Emit full debug info for all types used by the program">; def fno_standalone_debug : Flag<["-"], "fno-standalone-debug">, Group, Flags<[CoreOption]>, Index: include/clang/Driver/ToolChain.h =================================================================== --- include/clang/Driver/ToolChain.h +++ include/clang/Driver/ToolChain.h @@ -10,9 +10,10 @@ #ifndef LLVM_CLANG_DRIVER_TOOLCHAIN_H #define LLVM_CLANG_DRIVER_TOOLCHAIN_H +#include "clang/Basic/DebugInfoOptions.h" #include "clang/Basic/LLVM.h" +#include "clang/Basic/LangOptions.h" #include "clang/Basic/Sanitizers.h" -#include "clang/Basic/DebugInfoOptions.h" #include "clang/Driver/Action.h" #include "clang/Driver/Multilib.h" #include "clang/Driver/Types.h" @@ -349,6 +350,12 @@ return 0; } + /// Get the default trivial automatic variable initialization. + virtual LangOptions::TrivialAutoVarInitKind + GetDefaultTrivialAutoVarInit() const { + return LangOptions::TrivialAutoVarInitKind::Uninitialized; + } + /// GetDefaultLinker - Get the default linker to use. virtual const char *getDefaultLinker() const { return "ld"; } Index: lib/CodeGen/CGDecl.cpp =================================================================== --- lib/CodeGen/CGDecl.cpp +++ lib/CodeGen/CGDecl.cpp @@ -963,6 +963,83 @@ return llvm::isBytewiseValue(Init); } +static llvm::Constant *patternFor(CodeGenModule &CGM, llvm::Type *Ty) { + // The following value is a guaranteed unmappable pointer value and has a + // repeated byte-pattern which makes it easier to synthesize. We use it for + // pointers as well as integers so that aggregates are likely to be + // initialized with this repeated value. + constexpr uint64_t LargeValue = 0xAAAAAAAAAAAAAAAAull; + // For 32-bit platforms it's a bit trickier because, across systems, only the + // zero page can reasonably be expected to be unmapped, and even then we need + // a very low address. We use a smaller value, and that value sadly doesn't + // have a repeated byte-pattern. We don't use it for integers. + constexpr uint32_t SmallValue = 0x000000AA; + // Floating-point values are initialized as NaNs because they propagate. Using + // a repeated byte pattern means that it will be easier to initialize + // all-floating-point aggregates and arrays with memset. Further, aggregates + // which mix integral and a few floats might also initialize with memset + // followed by a handful of stores for the floats. Using fairly unique NaNs + // also means they'll be easier to distinguish in a crash. + constexpr bool NegativeNaN = true; + constexpr uint64_t NaNPayload = 0xFFFFFFFFFFFFFFFFull; + if (Ty->isIntOrIntVectorTy()) { + unsigned BitWidth = cast( + Ty->isVectorTy() ? Ty->getVectorElementType() : Ty) + ->getBitWidth(); + if (BitWidth <= 64) + return llvm::ConstantInt::get(Ty, LargeValue); + return llvm::ConstantInt::get( + Ty, llvm::APInt::getSplat(BitWidth, llvm::APInt(64, LargeValue))); + } else if (Ty->isPtrOrPtrVectorTy()) { + auto *PtrTy = cast( + Ty->isVectorTy() ? Ty->getVectorElementType() : Ty); + unsigned PtrWidth = CGM.getContext().getTargetInfo().getPointerWidth( + PtrTy->getAddressSpace()); + llvm::Type *IntTy = llvm::IntegerType::get(CGM.getLLVMContext(), PtrWidth); + uint64_t IntValue; + switch (PtrWidth) { + default: + llvm_unreachable("pattern initialization of unsupported pointer width"); + case 64: + IntValue = LargeValue; + break; + case 32: + IntValue = SmallValue; + break; + } + auto *Int = llvm::ConstantInt::get(IntTy, IntValue); + return llvm::ConstantExpr::getIntToPtr(Int, PtrTy); + } else if (Ty->isFPOrFPVectorTy()) { + unsigned BitWidth = llvm::APFloat::semanticsSizeInBits( + (Ty->isVectorTy() ? Ty->getVectorElementType() : Ty) + ->getFltSemantics()); + llvm::APInt Payload(64, NaNPayload); + if (BitWidth >= 64) + Payload = llvm::APInt::getSplat(BitWidth, Payload); + return llvm::ConstantFP::getQNaN(Ty, NegativeNaN, &Payload); + } else if (Ty->isArrayTy()) { + // Note: this doesn't touch tail padding (at the end of an object, before + // the next array object). It is instead handled by replaceUndef. + auto *ArrTy = cast(Ty); + llvm::SmallVector Element( + ArrTy->getNumElements(), patternFor(CGM, ArrTy->getElementType())); + return llvm::ConstantArray::get(ArrTy, Element); + } + + // Note: this doesn't touch struct padding. It will initialize as much union + // padding as is required for the largest type in the union. Padding is + // instead handled by replaceUndef. Stores to structs with volatile members + // don't have a volatile qualifier when initialized according to C++. This is + // fine because stack-based volatiles don't really have volatile semantics + // anyways, and the initialization shouldn't be observable. + auto *StructTy = cast(Ty); + llvm::SmallVector Struct(StructTy->getNumElements()); + for (unsigned El = 0; El != Struct.size(); ++El) { + Struct[El] = patternFor(CGM, StructTy->getElementType(El)); + } + return llvm::ConstantStruct::get(StructTy, Struct); +} + static Address createUnnamedGlobalFrom(CodeGenModule &CGM, const VarDecl &D, CGBuilderTy &Builder, llvm::Constant *Constant, @@ -1010,13 +1087,20 @@ Address Loc, bool isVolatile, CGBuilderTy &Builder, llvm::Constant *constant) { + auto *Ty = constant->getType(); + bool isScalar = Ty->isIntOrIntVectorTy() || Ty->isPtrOrPtrVectorTy() || + Ty->isFPOrFPVectorTy(); + if (isScalar) { + Builder.CreateStore(constant, Loc, isVolatile); + return; + } + auto *Int8Ty = llvm::IntegerType::getInt8Ty(CGM.getLLVMContext()); auto *IntPtrTy = CGM.getDataLayout().getIntPtrType(CGM.getLLVMContext()); // If the initializer is all or mostly the same, codegen with bzero / memset // then do a few stores afterward. - uint64_t ConstantSize = - CGM.getDataLayout().getTypeAllocSize(constant->getType()); + uint64_t ConstantSize = CGM.getDataLayout().getTypeAllocSize(Ty); auto *SizeVal = llvm::ConstantInt::get(IntPtrTy, ConstantSize); if (shouldUseBZeroPlusStoresToInitialize(constant, ConstantSize)) { Builder.CreateMemSet(Loc, llvm::ConstantInt::get(Int8Ty, 0), SizeVal, @@ -1025,8 +1109,7 @@ bool valueAlreadyCorrect = constant->isNullValue() || isa(constant); if (!valueAlreadyCorrect) { - Loc = Builder.CreateBitCast( - Loc, constant->getType()->getPointerTo(Loc.getAddressSpace())); + Loc = Builder.CreateBitCast(Loc, Ty->getPointerTo(Loc.getAddressSpace())); emitStoresForInitAfterBZero(CGM, constant, Loc, isVolatile, Builder); } return; @@ -1051,6 +1134,59 @@ SizeVal, isVolatile); } +static void emitStoresForZeroInit(CodeGenModule &CGM, const VarDecl &D, + Address Loc, bool isVolatile, + CGBuilderTy &Builder) { + llvm::Type *ElTy = Loc.getElementType(); + llvm::Constant *constant = llvm::Constant::getNullValue(ElTy); + emitStoresForConstant(CGM, D, Loc, isVolatile, Builder, constant); +} + +static void emitStoresForPatternInit(CodeGenModule &CGM, const VarDecl &D, + Address Loc, bool isVolatile, + CGBuilderTy &Builder) { + llvm::Type *ElTy = Loc.getElementType(); + llvm::Constant *constant = patternFor(CGM, ElTy); + assert(!isa(constant)); + emitStoresForConstant(CGM, D, Loc, isVolatile, Builder, constant); +} + +static bool containsUndef(llvm::Constant *constant) { + auto *Ty = constant->getType(); + if (isa(constant)) + return true; + if (Ty->isStructTy() || Ty->isArrayTy() || Ty->isVectorTy()) + for (llvm::Use &Op : constant->operands()) + if (containsUndef(cast(Op))) + return true; + return false; +} + +static llvm::Constant *replaceUndef(llvm::Constant *constant) { + // FIXME: when doing pattern initialization, replace undef with 0xAA instead. + // FIXME: also replace padding between values by creating a new struct type + // which has no padding. + auto *Ty = constant->getType(); + if (isa(constant)) + return llvm::Constant::getNullValue(Ty); + if (!(Ty->isStructTy() || Ty->isArrayTy() || Ty->isVectorTy())) + return constant; + if (!containsUndef(constant)) + return constant; + llvm::SmallVector Values(constant->getNumOperands()); + for (unsigned Op = 0, NumOp = constant->getNumOperands(); Op != NumOp; ++Op) { + auto *OpValue = cast(constant->getOperand(Op)); + Values[Op] = replaceUndef(OpValue); + } + if (Ty->isStructTy()) { + return llvm::ConstantStruct::get(cast(Ty), Values); + } else if (Ty->isArrayTy()) { + return llvm::ConstantArray::get(cast(Ty), Values); + } + assert(Ty->isVectorTy()); + return llvm::ConstantVector::get(Values); +} + /// EmitAutoVarDecl - Emit code and set up an entry in LocalDeclMap for a /// variable declaration with auto, register, or no storage class specifier. /// These turn into simple stack objects, or GlobalValues depending on target. @@ -1442,6 +1578,8 @@ auto DL = ApplyDebugLocation::CreateDefaultArtificial(*this, D.getLocation()); QualType type = D.getType(); + bool isVolatile = type.isVolatileQualified(); + // If this local has an initializer, emit it now. const Expr *Init = D.getInit(); @@ -1469,24 +1607,133 @@ return; } - if (isTrivialInitializer(Init)) - return; - // Check whether this is a byref variable that's potentially // captured and moved by its own initializer. If so, we'll need to // emit the initializer first, then copy into the variable. - bool capturedByInit = emission.IsEscapingByRef && isCapturedBy(D, Init); + bool capturedByInit = + Init && emission.IsEscapingByRef && isCapturedBy(D, Init); Address Loc = - capturedByInit ? emission.Addr : emission.getObjectAddress(*this); + capturedByInit ? emission.Addr : emission.getObjectAddress(*this); + + // Note: constexpr already initializes everything correctly. + LangOptions::TrivialAutoVarInitKind trivialAutoVarInit; + if (D.isConstexpr()) { + trivialAutoVarInit = LangOptions::TrivialAutoVarInitKind::Uninitialized; + } else if (const TrivialAutoInitAttr *Attr = + D.getAttr()) { + switch (Attr->getTrivialAutoVarInit()) { + case TrivialAutoInitAttr::Uninitialized: + trivialAutoVarInit = LangOptions::TrivialAutoVarInitKind::Uninitialized; + break; + case TrivialAutoInitAttr::Zero: + trivialAutoVarInit = LangOptions::TrivialAutoVarInitKind::Zero; + break; + case TrivialAutoInitAttr::Pattern: + trivialAutoVarInit = LangOptions::TrivialAutoVarInitKind::Pattern; + break; + } + } else { + trivialAutoVarInit = getContext().getLangOpts().getTrivialAutoVarInit(); + } + + auto initializeWhatIsTechnicallyUninitialized = [&]() { + if (trivialAutoVarInit == + LangOptions::TrivialAutoVarInitKind::Uninitialized) + return; + + CharUnits Size = getContext().getTypeSizeInChars(type); + if (!Size.isZero()) { + switch (trivialAutoVarInit) { + case LangOptions::TrivialAutoVarInitKind::Uninitialized: + llvm_unreachable("Uninitialized handled above"); + case LangOptions::TrivialAutoVarInitKind::Zero: + emitStoresForZeroInit(CGM, D, Loc, isVolatile, Builder); + break; + case LangOptions::TrivialAutoVarInitKind::Pattern: + emitStoresForPatternInit(CGM, D, Loc, isVolatile, Builder); + break; + } + return; + } + + // VLAs look zero-sized to getTypeInfo. We can't emit constant stores to + // them, so emit a memcpy with the VLA size to initialize each element. + // Technically zero-sized or negative-sized VLAs are undefined, and UBSan + // will catch that code, but there exists code which generates zero-sized + // VLAs. Be nice and initialize whatever they requested. + const VariableArrayType *VlaType = + dyn_cast_or_null(getContext().getAsArrayType(type)); + if (!VlaType) + return; + auto VlaSize = getVLASize(VlaType); + auto SizeVal = VlaSize.NumElts; + CharUnits EltSize = getContext().getTypeSizeInChars(VlaSize.Type); + switch (trivialAutoVarInit) { + case LangOptions::TrivialAutoVarInitKind::Uninitialized: + llvm_unreachable("Uninitialized handled above"); + + case LangOptions::TrivialAutoVarInitKind::Zero: + if (!EltSize.isOne()) + SizeVal = Builder.CreateNUWMul(SizeVal, CGM.getSize(EltSize)); + Builder.CreateMemSet(Loc, llvm::ConstantInt::get(Int8Ty, 0), SizeVal, + isVolatile); + break; + + case LangOptions::TrivialAutoVarInitKind::Pattern: { + llvm::Type *ElTy = Loc.getElementType(); + llvm::Constant *Constant = patternFor(CGM, ElTy); + CharUnits ConstantAlign = getContext().getTypeAlignInChars(VlaSize.Type); + llvm::BasicBlock *SetupBB = createBasicBlock("vla-setup.loop"); + llvm::BasicBlock *LoopBB = createBasicBlock("vla-init.loop"); + llvm::BasicBlock *ContBB = createBasicBlock("vla-init.cont"); + llvm::Value *IsZeroSizedVLA = Builder.CreateICmpEQ( + SizeVal, llvm::ConstantInt::get(SizeVal->getType(), 0), + "vla.iszerosized"); + Builder.CreateCondBr(IsZeroSizedVLA, ContBB, SetupBB); + EmitBlock(SetupBB); + if (!EltSize.isOne()) + SizeVal = Builder.CreateNUWMul(SizeVal, CGM.getSize(EltSize)); + llvm::Value *BaseSizeInChars = + llvm::ConstantInt::get(IntPtrTy, EltSize.getQuantity()); + Address Begin = Builder.CreateElementBitCast(Loc, Int8Ty, "vla.begin"); + llvm::Value *End = + Builder.CreateInBoundsGEP(Begin.getPointer(), SizeVal, "vla.end"); + llvm::BasicBlock *OriginBB = Builder.GetInsertBlock(); + EmitBlock(LoopBB); + llvm::PHINode *Cur = Builder.CreatePHI(Begin.getType(), 2, "vla.cur"); + Cur->addIncoming(Begin.getPointer(), OriginBB); + CharUnits CurAlign = Loc.getAlignment().alignmentOfArrayElement(EltSize); + Builder.CreateMemCpy( + Address(Cur, CurAlign), + createUnnamedGlobalFrom(CGM, D, Builder, Constant, ConstantAlign), + BaseSizeInChars, isVolatile); + llvm::Value *Next = + Builder.CreateInBoundsGEP(Int8Ty, Cur, BaseSizeInChars, "vla.next"); + llvm::Value *Done = Builder.CreateICmpEQ(Next, End, "vla-init.isdone"); + Builder.CreateCondBr(Done, ContBB, LoopBB); + Cur->addIncoming(Next, LoopBB); + EmitBlock(ContBB); + } break; + } + }; + + if (isTrivialInitializer(Init)) { + initializeWhatIsTechnicallyUninitialized(); + return; + } llvm::Constant *constant = nullptr; if (emission.IsConstantAggregate || D.isConstexpr()) { assert(!capturedByInit && "constant init contains a capturing block?"); constant = ConstantEmitter(*this).tryEmitAbstractForInitializer(D); + if (constant && trivialAutoVarInit != + LangOptions::TrivialAutoVarInitKind::Uninitialized) + constant = replaceUndef(constant); } if (!constant) { + initializeWhatIsTechnicallyUninitialized(); LValue lv = MakeAddrLValue(Loc, type); lv.setNonGC(true); return EmitExprAsInit(Init, &D, lv, capturedByInit); @@ -1499,10 +1746,6 @@ return EmitStoreThroughLValue(RValue::get(constant), lv, true); } - // If this is a simple aggregate initialization, we can optimize it - // in various ways. - bool isVolatile = type.isVolatileQualified(); - llvm::Type *BP = CGM.Int8Ty->getPointerTo(Loc.getAddressSpace()); if (Loc.getType() != BP) Loc = Builder.CreateBitCast(Loc, BP); Index: lib/Driver/ToolChains/Clang.cpp =================================================================== --- lib/Driver/ToolChains/Clang.cpp +++ lib/Driver/ToolChains/Clang.cpp @@ -2467,6 +2467,52 @@ } } +static void RenderTrivialAutoVarInitOptions(const Driver &D, + const ToolChain &TC, + const ArgList &Args, + ArgStringList &CmdArgs) { + auto DefaultTrivialAutoVarInit = TC.GetDefaultTrivialAutoVarInit(); + StringRef TrivialAutoVarInit = ""; + + for (const Arg *A : Args) { + switch (A->getOption().getID()) { + default: + continue; + case options::OPT_ftrivial_auto_var_init: { + A->claim(); + StringRef Val = A->getValue(); + if (Val == "uninitialized" || Val == "zero" || Val == "pattern") + TrivialAutoVarInit = Val; + else + D.Diag(diag::err_drv_unsupported_option_argument) + << A->getOption().getName() << Val; + break; + } + } + } + + if (TrivialAutoVarInit.empty()) + switch (DefaultTrivialAutoVarInit) { + case LangOptions::TrivialAutoVarInitKind::Uninitialized: + break; + case LangOptions::TrivialAutoVarInitKind::Pattern: + TrivialAutoVarInit = "pattern"; + break; + case LangOptions::TrivialAutoVarInitKind::Zero: + TrivialAutoVarInit = "zero"; + break; + } + + if (!TrivialAutoVarInit.empty()) { + if (TrivialAutoVarInit == "zero" && !Args.hasArg(options::OPT_enable_trivial_var_init_zero)) { + TrivialAutoVarInit = "pattern"; + D.Diag(diag::warn_drv_trivial_auto_var_init_zero_disabled); + } + CmdArgs.push_back( + Args.MakeArgString("-ftrivial-auto-var-init=" + TrivialAutoVarInit)); + } +} + static void RenderOpenCLOptions(const ArgList &Args, ArgStringList &CmdArgs) { const unsigned ForwardedArguments[] = { options::OPT_cl_opt_disable, @@ -4455,6 +4501,7 @@ CmdArgs.push_back(Args.MakeArgString("-mspeculative-load-hardening")); RenderSSPOptions(TC, Args, CmdArgs, KernelOrKext); + RenderTrivialAutoVarInitOptions(D, TC, Args, CmdArgs); // Translate -mstackrealign if (Args.hasFlag(options::OPT_mstackrealign, options::OPT_mno_stackrealign, Index: lib/Frontend/CompilerInvocation.cpp =================================================================== --- lib/Frontend/CompilerInvocation.cpp +++ lib/Frontend/CompilerInvocation.cpp @@ -2937,6 +2937,19 @@ case 3: Opts.setStackProtector(LangOptions::SSPReq); break; } + if (Arg *A = Args.getLastArg(OPT_ftrivial_auto_var_init)) { + StringRef Val = A->getValue(); + if (Val == "uninitialized") + Opts.setTrivialAutoVarInit( + LangOptions::TrivialAutoVarInitKind::Uninitialized); + else if (Val == "zero") + Opts.setTrivialAutoVarInit(LangOptions::TrivialAutoVarInitKind::Zero); + else if (Val == "pattern") + Opts.setTrivialAutoVarInit(LangOptions::TrivialAutoVarInitKind::Pattern); + else + Diags.Report(diag::err_drv_invalid_value) << A->getAsString(Args) << Val; + } + // Parse -fsanitize= arguments. parseSanitizerKinds("-fsanitize=", Args.getAllArgValues(OPT_fsanitize_EQ), Diags, Opts.Sanitize); Index: lib/Sema/SemaDeclAttr.cpp =================================================================== --- lib/Sema/SemaDeclAttr.cpp +++ lib/Sema/SemaDeclAttr.cpp @@ -6080,6 +6080,28 @@ handleSimpleAttributeWithExclusions(S, D, A); } +static void handleTrivialAutoInitAttr(Sema &S, Decl *D, const ParsedAttr &AL) { + if (AL.getNumArgs() != 1) { + S.Diag(AL.getLoc(), diag::err_attribute_wrong_number_arguments) << AL << 1; + return; + } + StringRef Str; + SourceLocation ArgLoc; + if (!S.checkStringLiteralArgumentAttr(AL, 0, Str, &ArgLoc)) + return; + TrivialAutoInitAttr::TrivialAutoVarInitKind Kind; + if (!TrivialAutoInitAttr::ConvertStrToTrivialAutoVarInitKind(Str, Kind)) { + S.Diag(AL.getLoc(), diag::warn_attribute_type_not_supported) + << AL << Str << ArgLoc; + return; + } + assert(cast(D)->getStorageDuration() == SD_Automatic && + "trivial_auto_init is only valid on automatic duration variables"); + unsigned Index = AL.getAttributeSpellingListIndex(); + D->addAttr(::new (S.Context) + TrivialAutoInitAttr(AL.getLoc(), S.Context, Kind, Index)); +} + //===----------------------------------------------------------------------===// // Top Level Sema Entry Points //===----------------------------------------------------------------------===// @@ -6770,6 +6792,10 @@ case ParsedAttr::AT_NoDestroy: handleDestroyAttr(S, D, AL); break; + + case ParsedAttr::AT_TrivialAutoInit: + handleTrivialAutoInitAttr(S, D, AL); + break; } } Index: test/CodeGenCXX/auto-var-init.cpp =================================================================== --- test/CodeGenCXX/auto-var-init.cpp +++ test/CodeGenCXX/auto-var-init.cpp @@ -1,4 +1,6 @@ // RUN: %clang_cc1 -triple x86_64-unknown-unknown -fblocks %s -emit-llvm -o - | FileCheck %s +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fblocks -ftrivial-auto-var-init=pattern %s -emit-llvm -o - | FileCheck %s -check-prefix=PATTERN +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fblocks -ftrivial-auto-var-init=zero %s -emit-llvm -o - | FileCheck %s -check-prefix=ZERO template void used(T &) noexcept; @@ -24,28 +26,108 @@ used(custom); \ } +// None of the synthesized globals should contain `undef`. +// PATTERN-NOT: undef +// ZERO-NOT: undef + +// PATTERN: @__const.test_empty_uninit.uninit = private unnamed_addr constant %struct.empty { i8 -86 }, align 1 struct empty {}; +// PATTERN: @__const.test_small_uninit.uninit = private unnamed_addr constant %struct.small { i8 -86 }, align 1 +// PATTERN: @__const.test_small_custom.custom = private unnamed_addr constant %struct.small { i8 42 }, align 1 +// ZERO: @__const.test_small_custom.custom = private unnamed_addr constant %struct.small { i8 42 }, align 1 struct small { char c; }; +// PATTERN: @__const.test_smallinit_uninit.uninit = private unnamed_addr constant %struct.smallinit { i8 -86 }, align 1 +// PATTERN: @__const.test_smallinit_braces.braces = private unnamed_addr constant %struct.smallinit { i8 -86 }, align 1 +// PATTERN: @__const.test_smallinit_custom.custom = private unnamed_addr constant %struct.smallinit { i8 -86 }, align 1 struct smallinit { char c = 42; }; +// PATTERN: @__const.test_smallpartinit_uninit.uninit = private unnamed_addr constant %struct.smallpartinit { i8 -86, i8 -86 }, align 1 +// PATTERN: @__const.test_smallpartinit_braces.braces = private unnamed_addr constant %struct.smallpartinit { i8 -86, i8 -86 }, align 1 +// PATTERN: @__const.test_smallpartinit_custom.custom = private unnamed_addr constant %struct.smallpartinit { i8 -86, i8 -86 }, align 1 struct smallpartinit { char c = 42, d; }; +// PATTERN: @__const.test_nullinit_uninit.uninit = private unnamed_addr constant %struct.nullinit { i8* inttoptr (i64 -6148914691236517206 to i8*) }, align 8 +// PATTERN: @__const.test_nullinit_braces.braces = private unnamed_addr constant %struct.nullinit { i8* inttoptr (i64 -6148914691236517206 to i8*) }, align 8 +// PATTERN: @__const.test_nullinit_custom.custom = private unnamed_addr constant %struct.nullinit { i8* inttoptr (i64 -6148914691236517206 to i8*) }, align 8 struct nullinit { char* null = nullptr; }; +// PATTERN: @__const.test_padded_uninit.uninit = private unnamed_addr constant %struct.padded { i8 -86, i32 -1431655766 }, align 4 +// PATTERN: @__const.test_padded_custom.custom = private unnamed_addr constant %struct.padded { i8 42, i32 13371337 }, align 4 +// ZERO: @__const.test_padded_custom.custom = private unnamed_addr constant %struct.padded { i8 42, i32 13371337 }, align 4 struct padded { char c; int i; }; +// PATTERN: @__const.test_paddednullinit_uninit.uninit = private unnamed_addr constant %struct.paddednullinit { i8 -86, i32 -1431655766 }, align 4 +// PATTERN: @__const.test_paddednullinit_braces.braces = private unnamed_addr constant %struct.paddednullinit { i8 -86, i32 -1431655766 }, align 4 +// PATTERN: @__const.test_paddednullinit_custom.custom = private unnamed_addr constant %struct.paddednullinit { i8 -86, i32 -1431655766 }, align 4 struct paddednullinit { char c = 0; int i = 0; }; +// PATTERN: @__const.test_bitfield_uninit.uninit = private unnamed_addr constant %struct.bitfield { i8 -86, [3 x i8] c"\AA\AA\AA" }, align 4 +// PATTERN: @__const.test_bitfield_custom.custom = private unnamed_addr constant %struct.bitfield { i8 20, [3 x i8] zeroinitializer }, align 4 +// ZERO: @__const.test_bitfield_custom.custom = private unnamed_addr constant %struct.bitfield { i8 20, [3 x i8] zeroinitializer }, align 4 struct bitfield { int i : 4; int j : 2; }; +// PATTERN: @__const.test_bitfieldaligned_uninit.uninit = private unnamed_addr constant %struct.bitfieldaligned { i8 -86, [3 x i8] c"\AA\AA\AA", i8 -86, [3 x i8] c"\AA\AA\AA" }, align 4 +// PATTERN: @__const.test_bitfieldaligned_custom.custom = private unnamed_addr constant %struct.bitfieldaligned { i8 4, [3 x i8] zeroinitializer, i8 1, [3 x i8] zeroinitializer }, align 4 +// ZERO: @__const.test_bitfieldaligned_custom.custom = private unnamed_addr constant %struct.bitfieldaligned { i8 4, [3 x i8] zeroinitializer, i8 1, [3 x i8] zeroinitializer }, align 4 struct bitfieldaligned { int i : 4; int : 0; int j : 2; }; struct big { unsigned a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z; }; +// PATTERN: @__const.test_arraytail_uninit.uninit = private unnamed_addr constant %struct.arraytail { i32 -1431655766, [0 x i32] zeroinitializer }, align 4 +// PATTERN: @__const.test_arraytail_custom.custom = private unnamed_addr constant %struct.arraytail { i32 57005, [0 x i32] zeroinitializer }, align 4 +// ZERO: @__const.test_arraytail_custom.custom = private unnamed_addr constant %struct.arraytail { i32 57005, [0 x i32] zeroinitializer }, align 4 struct arraytail { int i; int arr[]; }; +// PATTERN: @__const.test_int1_uninit.uninit = private unnamed_addr constant [1 x i32] [i32 -1431655766], align 4 +// PATTERN: @__const.test_int1_custom.custom = private unnamed_addr constant [1 x i32] [i32 858993459], align 4 +// ZERO: @__const.test_int1_custom.custom = private unnamed_addr constant [1 x i32] [i32 858993459], align 4 +// PATTERN: @__const.test_bool4_uninit.uninit = private unnamed_addr constant [4 x i8] c"\AA\AA\AA\AA", align 1 +// PATTERN: @__const.test_bool4_custom.custom = private unnamed_addr constant [4 x i8] c"\01\01\01\01", align 1 +// ZERO: @__const.test_bool4_custom.custom = private unnamed_addr constant [4 x i8] c"\01\01\01\01", align 1 +// PATTERN: @__const.test_intptr4_uninit.uninit = private unnamed_addr constant [4 x i32*] [i32* inttoptr (i64 -6148914691236517206 to i32*), i32* inttoptr (i64 -6148914691236517206 to i32*), i32* inttoptr (i64 -6148914691236517206 to i32*), i32* inttoptr (i64 -6148914691236517206 to i32*)], align 16 +// PATTERN: @__const.test_intptr4_custom.custom = private unnamed_addr constant [4 x i32*] [i32* inttoptr (i64 572662306 to i32*), i32* inttoptr (i64 572662306 to i32*), i32* inttoptr (i64 572662306 to i32*), i32* inttoptr (i64 572662306 to i32*)], align 16 +// ZERO: @__const.test_intptr4_custom.custom = private unnamed_addr constant [4 x i32*] [i32* inttoptr (i64 572662306 to i32*), i32* inttoptr (i64 572662306 to i32*), i32* inttoptr (i64 572662306 to i32*), i32* inttoptr (i64 572662306 to i32*)], align 16 +// PATTERN: @__const.test_tailpad4_uninit.uninit = private unnamed_addr constant [4 x %struct.tailpad] [%struct.tailpad { i16 -21846, i8 -86 }, %struct.tailpad { i16 -21846, i8 -86 }, %struct.tailpad { i16 -21846, i8 -86 }, %struct.tailpad { i16 -21846, i8 -86 }], align 16 +// PATTERN: @__const.test_tailpad4_custom.custom = private unnamed_addr constant [4 x %struct.tailpad] [%struct.tailpad { i16 257, i8 1 }, %struct.tailpad { i16 257, i8 1 }, %struct.tailpad { i16 257, i8 1 }, %struct.tailpad { i16 257, i8 1 }], align 16 +// ZERO: @__const.test_tailpad4_custom.custom = private unnamed_addr constant [4 x %struct.tailpad] [%struct.tailpad { i16 257, i8 1 }, %struct.tailpad { i16 257, i8 1 }, %struct.tailpad { i16 257, i8 1 }, %struct.tailpad { i16 257, i8 1 }], align 16 struct tailpad { short s; char c; }; +// PATTERN: @__const.test_atomicnotlockfree_uninit.uninit = private unnamed_addr constant %struct.notlockfree { [4 x i64] [i64 -6148914691236517206, i64 -6148914691236517206, i64 -6148914691236517206, i64 -6148914691236517206] }, align 8 struct notlockfree { long long a[4]; }; +// PATTERN: @__const.test_atomicpadded_uninit.uninit = private unnamed_addr constant %struct.padded { i8 -86, i32 -1431655766 }, align 8 +// PATTERN: @__const.test_atomictailpad_uninit.uninit = private unnamed_addr constant %struct.tailpad { i16 -21846, i8 -86 }, align 4 +// PATTERN: @__const.test_complexfloat_uninit.uninit = private unnamed_addr constant { float, float } { float 0xFFFFFFFFE0000000, float 0xFFFFFFFFE0000000 }, align 4 +// PATTERN: @__const.test_complexfloat_braces.braces = private unnamed_addr constant { float, float } { float 0xFFFFFFFFE0000000, float 0xFFFFFFFFE0000000 }, align 4 +// PATTERN: @__const.test_complexfloat_custom.custom = private unnamed_addr constant { float, float } { float 0xFFFFFFFFE0000000, float 0xFFFFFFFFE0000000 }, align 4 +// PATTERN: @__const.test_complexdouble_uninit.uninit = private unnamed_addr constant { double, double } { double 0xFFFFFFFFFFFFFFFF, double 0xFFFFFFFFFFFFFFFF }, align 8 +// PATTERN: @__const.test_complexdouble_braces.braces = private unnamed_addr constant { double, double } { double 0xFFFFFFFFFFFFFFFF, double 0xFFFFFFFFFFFFFFFF }, align 8 +// PATTERN: @__const.test_complexdouble_custom.custom = private unnamed_addr constant { double, double } { double 0xFFFFFFFFFFFFFFFF, double 0xFFFFFFFFFFFFFFFF }, align 8 +// PATTERN: @__const.test_semivolatile_uninit.uninit = private unnamed_addr constant %struct.semivolatile { i32 -1431655766, i32 -1431655766 }, align 4 +// PATTERN: @__const.test_semivolatile_custom.custom = private unnamed_addr constant %struct.semivolatile { i32 1145324612, i32 1145324612 }, align 4 struct semivolatile { int i; volatile int vi; }; +// PATTERN: @__const.test_semivolatileinit_uninit.uninit = private unnamed_addr constant %struct.semivolatileinit { i32 -1431655766, i32 -1431655766 }, align 4 +// PATTERN: @__const.test_semivolatileinit_braces.braces = private unnamed_addr constant %struct.semivolatileinit { i32 -1431655766, i32 -1431655766 }, align 4 +// PATTERN: @__const.test_semivolatileinit_custom.custom = private unnamed_addr constant %struct.semivolatileinit { i32 -1431655766, i32 -1431655766 }, align 4 +// ZERO: @__const.test_semivolatile_custom.custom = private unnamed_addr constant %struct.semivolatile { i32 1145324612, i32 1145324612 }, align 4 struct semivolatileinit { int i = 0x11111111; volatile int vi = 0x11111111; }; +// PATTERN: @__const.test_base_uninit.uninit = private unnamed_addr constant %struct.base { i32 (...)** inttoptr (i64 -6148914691236517206 to i32 (...)**) }, align 8 +// PATTERN: @__const.test_base_braces.braces = private unnamed_addr constant %struct.base { i32 (...)** inttoptr (i64 -6148914691236517206 to i32 (...)**) }, align 8 struct base { virtual ~base(); }; +// PATTERN: @__const.test_derived_uninit.uninit = private unnamed_addr constant %struct.derived { %struct.base { i32 (...)** inttoptr (i64 -6148914691236517206 to i32 (...)**) } }, align 8 +// PATTERN: @__const.test_derived_braces.braces = private unnamed_addr constant %struct.derived { %struct.base { i32 (...)** inttoptr (i64 -6148914691236517206 to i32 (...)**) } }, align 8 struct derived : public base {}; +// PATTERN: @__const.test_virtualderived_uninit.uninit = private unnamed_addr constant %struct.virtualderived { %struct.base { i32 (...)** inttoptr (i64 -6148914691236517206 to i32 (...)**) }, %struct.derived { %struct.base { i32 (...)** inttoptr (i64 -6148914691236517206 to i32 (...)**) } } }, align 8 +// PATTERN: @__const.test_virtualderived_braces.braces = private unnamed_addr constant %struct.virtualderived { %struct.base { i32 (...)** inttoptr (i64 -6148914691236517206 to i32 (...)**) }, %struct.derived { %struct.base { i32 (...)** inttoptr (i64 -6148914691236517206 to i32 (...)**) } } }, align 8 struct virtualderived : public virtual base, public virtual derived {}; +// PATTERN: @__const.test_matching_uninit.uninit = private unnamed_addr constant %union.matching { i32 -1431655766 }, align 4 +// PATTERN: @__const.test_matching_custom.custom = private unnamed_addr constant { float } { float 6.145500e+04 }, align 4 union matching { int i; float f; }; +// PATTERN: @__const.test_matchingreverse_uninit.uninit = private unnamed_addr constant %union.matchingreverse { float 0xFFFFFFFFE0000000 }, align 4 +// PATTERN: @__const.test_matchingreverse_custom.custom = private unnamed_addr constant { i32 } { i32 61455 }, align 4 +// ZERO: @__const.test_matching_custom.custom = private unnamed_addr constant { float } { float 6.145500e+04 }, align 4 union matchingreverse { float f; int i; }; +// PATTERN: @__const.test_unmatched_uninit.uninit = private unnamed_addr constant %union.unmatched { i32 -1431655766 }, align 4 +// PATTERN: @__const.test_unmatched_custom.custom = private unnamed_addr constant %union.unmatched { i32 1001242351 }, align 4 +// ZERO: @__const.test_matchingreverse_custom.custom = private unnamed_addr constant { i32 } { i32 61455 }, align 4 union unmatched { char c; int i; }; +// PATTERN: @__const.test_unmatchedreverse_uninit.uninit = private unnamed_addr constant %union.unmatchedreverse { i32 -1431655766 }, align 4 +// PATTERN: @__const.test_unmatchedreverse_custom.custom = private unnamed_addr constant { i8, [3 x i8] } { i8 42, [3 x i8] zeroinitializer }, align 4 +// ZERO: @__const.test_unmatched_custom.custom = private unnamed_addr constant %union.unmatched { i32 1001242351 }, align 4 union unmatchedreverse { int i; char c; }; +// PATTERN: @__const.test_unmatchedfp_uninit.uninit = private unnamed_addr constant %union.unmatchedfp { double 0xFFFFFFFFFFFFFFFF }, align 8 +// PATTERN: @__const.test_unmatchedfp_custom.custom = private unnamed_addr constant %union.unmatchedfp { double 0x400921FB54442D18 }, align 8 +// ZERO: @__const.test_unmatchedreverse_custom.custom = private unnamed_addr constant { i8, [3 x i8] } { i8 42, [3 x i8] zeroinitializer }, align 4 +// ZERO: @__const.test_unmatchedfp_custom.custom = private unnamed_addr constant %union.unmatchedfp { double 0x400921FB54442D18 }, align 8 union unmatchedfp { float f; double d; }; enum emptyenum {}; enum smallenum { VALUE }; @@ -56,6 +138,10 @@ // CHECK-LABEL: @test_char_uninit() // CHECK: %uninit = alloca i8, align // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_char_uninit() +// PATTERN: store i8 -86, i8* %uninit, align 1 +// ZERO-LABEL: @test_char_uninit() +// ZERO: store i8 0, i8* %uninit, align 1 TEST_BRACES(char, char); // CHECK-LABEL: @test_char_braces() @@ -67,6 +153,10 @@ // CHECK-LABEL: @test_uchar_uninit() // CHECK: %uninit = alloca i8, align // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_uchar_uninit() +// PATTERN: store i8 -86, i8* %uninit, align 1 +// ZERO-LABEL: @test_uchar_uninit() +// ZERO: store i8 0, i8* %uninit, align 1 TEST_BRACES(uchar, unsigned char); // CHECK-LABEL: @test_uchar_braces() @@ -78,6 +168,10 @@ // CHECK-LABEL: @test_schar_uninit() // CHECK: %uninit = alloca i8, align // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_schar_uninit() +// PATTERN: store i8 -86, i8* %uninit, align 1 +// ZERO-LABEL: @test_schar_uninit() +// ZERO: store i8 0, i8* %uninit, align 1 TEST_BRACES(schar, signed char); // CHECK-LABEL: @test_schar_braces() @@ -89,6 +183,10 @@ // CHECK-LABEL: @test_wchar_t_uninit() // CHECK: %uninit = alloca i32, align // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_wchar_t_uninit() +// PATTERN: store i32 -1431655766, i32* %uninit, align +// ZERO-LABEL: @test_wchar_t_uninit() +// ZERO: store i32 0, i32* %uninit, align TEST_BRACES(wchar_t, wchar_t); // CHECK-LABEL: @test_wchar_t_braces() @@ -100,6 +198,10 @@ // CHECK-LABEL: @test_short_uninit() // CHECK: %uninit = alloca i16, align // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_short_uninit() +// PATTERN: store i16 -21846, i16* %uninit, align +// ZERO-LABEL: @test_short_uninit() +// ZERO: store i16 0, i16* %uninit, align TEST_BRACES(short, short); // CHECK-LABEL: @test_short_braces() @@ -111,6 +213,10 @@ // CHECK-LABEL: @test_ushort_uninit() // CHECK: %uninit = alloca i16, align // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_ushort_uninit() +// PATTERN: store i16 -21846, i16* %uninit, align +// ZERO-LABEL: @test_ushort_uninit() +// ZERO: store i16 0, i16* %uninit, align TEST_BRACES(ushort, unsigned short); // CHECK-LABEL: @test_ushort_braces() @@ -122,6 +228,10 @@ // CHECK-LABEL: @test_int_uninit() // CHECK: %uninit = alloca i32, align // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_int_uninit() +// PATTERN: store i32 -1431655766, i32* %uninit, align +// ZERO-LABEL: @test_int_uninit() +// ZERO: store i32 0, i32* %uninit, align TEST_BRACES(int, int); // CHECK-LABEL: @test_int_braces() @@ -133,6 +243,10 @@ // CHECK-LABEL: @test_unsigned_uninit() // CHECK: %uninit = alloca i32, align // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_unsigned_uninit() +// PATTERN: store i32 -1431655766, i32* %uninit, align +// ZERO-LABEL: @test_unsigned_uninit() +// ZERO: store i32 0, i32* %uninit, align TEST_BRACES(unsigned, unsigned); // CHECK-LABEL: @test_unsigned_braces() @@ -144,6 +258,10 @@ // CHECK-LABEL: @test_long_uninit() // CHECK: %uninit = alloca i64, align // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_long_uninit() +// PATTERN: store i64 -6148914691236517206, i64* %uninit, align +// ZERO-LABEL: @test_long_uninit() +// ZERO: store i64 0, i64* %uninit, align TEST_BRACES(long, long); // CHECK-LABEL: @test_long_braces() @@ -155,6 +273,10 @@ // CHECK-LABEL: @test_ulong_uninit() // CHECK: %uninit = alloca i64, align // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_ulong_uninit() +// PATTERN: store i64 -6148914691236517206, i64* %uninit, align +// ZERO-LABEL: @test_ulong_uninit() +// ZERO: store i64 0, i64* %uninit, align TEST_BRACES(ulong, unsigned long); // CHECK-LABEL: @test_ulong_braces() @@ -166,6 +288,10 @@ // CHECK-LABEL: @test_longlong_uninit() // CHECK: %uninit = alloca i64, align // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_longlong_uninit() +// PATTERN: store i64 -6148914691236517206, i64* %uninit, align +// ZERO-LABEL: @test_longlong_uninit() +// ZERO: store i64 0, i64* %uninit, align TEST_BRACES(longlong, long long); // CHECK-LABEL: @test_longlong_braces() @@ -177,6 +303,10 @@ // CHECK-LABEL: @test_ulonglong_uninit() // CHECK: %uninit = alloca i64, align // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_ulonglong_uninit() +// PATTERN: store i64 -6148914691236517206, i64* %uninit, align +// ZERO-LABEL: @test_ulonglong_uninit() +// ZERO: store i64 0, i64* %uninit, align TEST_BRACES(ulonglong, unsigned long long); // CHECK-LABEL: @test_ulonglong_braces() @@ -188,6 +318,10 @@ // CHECK-LABEL: @test_int128_uninit() // CHECK: %uninit = alloca i128, align // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_int128_uninit() +// PATTERN: store i128 -113427455640312821154458202477256070486, i128* %uninit, align +// ZERO-LABEL: @test_int128_uninit() +// ZERO: store i128 0, i128* %uninit, align TEST_BRACES(int128, __int128); // CHECK-LABEL: @test_int128_braces() @@ -199,6 +333,10 @@ // CHECK-LABEL: @test_uint128_uninit() // CHECK: %uninit = alloca i128, align // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_uint128_uninit() +// PATTERN: store i128 -113427455640312821154458202477256070486, i128* %uninit, align +// ZERO-LABEL: @test_uint128_uninit() +// ZERO: store i128 0, i128* %uninit, align TEST_BRACES(uint128, unsigned __int128); // CHECK-LABEL: @test_uint128_braces() @@ -211,6 +349,10 @@ // CHECK-LABEL: @test_fp16_uninit() // CHECK: %uninit = alloca half, align // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_fp16_uninit() +// PATTERN: store half 0xHFFFF, half* %uninit, align +// ZERO-LABEL: @test_fp16_uninit() +// ZERO: store half 0xH0000, half* %uninit, align TEST_BRACES(fp16, __fp16); // CHECK-LABEL: @test_fp16_braces() @@ -222,6 +364,10 @@ // CHECK-LABEL: @test_float_uninit() // CHECK: %uninit = alloca float, align // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_float_uninit() +// PATTERN: store float 0xFFFFFFFFE0000000, float* %uninit, align +// ZERO-LABEL: @test_float_uninit() +// ZERO: store float 0.000000e+00, float* %uninit, align TEST_BRACES(float, float); // CHECK-LABEL: @test_float_braces() @@ -233,6 +379,10 @@ // CHECK-LABEL: @test_double_uninit() // CHECK: %uninit = alloca double, align // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_double_uninit() +// PATTERN: store double 0xFFFFFFFFFFFFFFFF, double* %uninit, align +// ZERO-LABEL: @test_double_uninit() +// ZERO: store double 0.000000e+00, double* %uninit, align TEST_BRACES(double, double); // CHECK-LABEL: @test_double_braces() @@ -244,6 +394,10 @@ // CHECK-LABEL: @test_longdouble_uninit() // CHECK: %uninit = alloca x86_fp80, align // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_longdouble_uninit() +// PATTERN: store x86_fp80 0xKFFFFFFFFFFFFFFFFFFFF, x86_fp80* %uninit, align +// ZERO-LABEL: @test_longdouble_uninit() +// ZERO: store x86_fp80 0xK00000000000000000000, x86_fp80* %uninit, align TEST_BRACES(longdouble, long double); // CHECK-LABEL: @test_longdouble_braces() @@ -256,6 +410,10 @@ // CHECK-LABEL: @test_intptr_uninit() // CHECK: %uninit = alloca i32*, align // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_intptr_uninit() +// PATTERN: store i32* inttoptr (i64 -6148914691236517206 to i32*), i32** %uninit, align +// ZERO-LABEL: @test_intptr_uninit() +// ZERO: store i32* null, i32** %uninit, align TEST_BRACES(intptr, int*); // CHECK-LABEL: @test_intptr_braces() @@ -267,6 +425,10 @@ // CHECK-LABEL: @test_intptrptr_uninit() // CHECK: %uninit = alloca i32**, align // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_intptrptr_uninit() +// PATTERN: store i32** inttoptr (i64 -6148914691236517206 to i32**), i32*** %uninit, align +// ZERO-LABEL: @test_intptrptr_uninit() +// ZERO: store i32** null, i32*** %uninit, align TEST_BRACES(intptrptr, int**); // CHECK-LABEL: @test_intptrptr_braces() @@ -278,6 +440,10 @@ // CHECK-LABEL: @test_function_uninit() // CHECK: %uninit = alloca void ()*, align // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_function_uninit() +// PATTERN: store void ()* inttoptr (i64 -6148914691236517206 to void ()*), void ()** %uninit, align +// ZERO-LABEL: @test_function_uninit() +// ZERO: store void ()* null, void ()** %uninit, align TEST_BRACES(function, void(*)()); // CHECK-LABEL: @test_function_braces() @@ -289,6 +455,10 @@ // CHECK-LABEL: @test_bool_uninit() // CHECK: %uninit = alloca i8, align // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_bool_uninit() +// PATTERN: store i8 -86, i8* %uninit, align 1 +// ZERO-LABEL: @test_bool_uninit() +// ZERO: store i8 0, i8* %uninit, align 1 TEST_BRACES(bool, bool); // CHECK-LABEL: @test_bool_braces() @@ -301,6 +471,10 @@ // CHECK-LABEL: @test_empty_uninit() // CHECK: %uninit = alloca %struct.empty, align // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_empty_uninit() +// PATTERN: call void @llvm.memcpy{{.*}} @__const.test_empty_uninit.uninit +// ZERO-LABEL: @test_empty_uninit() +// ZERO: call void @llvm.memset{{.*}}, i8 0, TEST_BRACES(empty, empty); // CHECK-LABEL: @test_empty_braces() @@ -313,6 +487,10 @@ // CHECK-LABEL: @test_small_uninit() // CHECK: %uninit = alloca %struct.small, align // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_small_uninit() +// PATTERN: call void @llvm.memcpy{{.*}} @__const.test_small_uninit.uninit +// ZERO-LABEL: @test_small_uninit() +// ZERO: call void @llvm.memset{{.*}}, i8 0, TEST_BRACES(small, small); // CHECK-LABEL: @test_small_braces() @@ -321,7 +499,7 @@ // CHECK-NEXT: call void @llvm.memset{{.*}}(i8* align [[ALIGN]] %{{.*}}, i8 0, i64 1, i1 false) // CHECK-NEXT: call void @{{.*}}used{{.*}}%braces) - TEST_CUSTOM(small, small, { 42 }); +TEST_CUSTOM(small, small, { 42 }); // CHECK-LABEL: @test_small_custom() // CHECK: %custom = alloca %struct.small, align // CHECK-NEXT: bitcast @@ -353,6 +531,10 @@ // CHECK: %uninit = alloca %struct.smallpartinit, align // CHECK-NEXT: call void @{{.*}}smallpartinit{{.*}}%uninit) // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_smallpartinit_uninit() +// PATTERN: call void @llvm.memcpy{{.*}} @__const.test_smallpartinit_uninit.uninit +// ZERO-LABEL: @test_smallpartinit_uninit() +// ZERO: call void @llvm.memset{{.*}}, i8 0, TEST_BRACES(smallpartinit, smallpartinit); // CHECK-LABEL: @test_smallpartinit_braces() @@ -396,6 +578,10 @@ // CHECK-LABEL: @test_padded_uninit() // CHECK: %uninit = alloca %struct.padded, align // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_padded_uninit() +// PATTERN: call void @llvm.memcpy{{.*}} @__const.test_padded_uninit.uninit +// ZERO-LABEL: @test_padded_uninit() +// ZERO: call void @llvm.memset{{.*}}, i8 0, TEST_BRACES(padded, padded); // CHECK-LABEL: @test_padded_braces() @@ -416,6 +602,10 @@ // CHECK: %uninit = alloca %struct.paddednullinit, align // CHECK-NEXT: call void @{{.*}}paddednullinit{{.*}}%uninit) // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_paddednullinit_uninit() +// PATTERN: call void @llvm.memcpy{{.*}} @__const.test_paddednullinit_uninit.uninit +// ZERO-LABEL: @test_paddednullinit_uninit() +// ZERO: call void @llvm.memset{{.*}}, i8 0, TEST_BRACES(paddednullinit, paddednullinit); // CHECK-LABEL: @test_paddednullinit_braces() @@ -439,6 +629,10 @@ // CHECK-LABEL: @test_bitfield_uninit() // CHECK: %uninit = alloca %struct.bitfield, align // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_bitfield_uninit() +// PATTERN: call void @llvm.memcpy{{.*}} @__const.test_bitfield_uninit.uninit +// ZERO-LABEL: @test_bitfield_uninit() +// ZERO: call void @llvm.memset{{.*}}, i8 0, TEST_BRACES(bitfield, bitfield); // CHECK-LABEL: @test_bitfield_braces() @@ -458,6 +652,10 @@ // CHECK-LABEL: @test_bitfieldaligned_uninit() // CHECK: %uninit = alloca %struct.bitfieldaligned, align // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_bitfieldaligned_uninit() +// PATTERN: call void @llvm.memcpy{{.*}} @__const.test_bitfieldaligned_uninit.uninit +// ZERO-LABEL: @test_bitfieldaligned_uninit() +// ZERO: call void @llvm.memset{{.*}}, i8 0, TEST_BRACES(bitfieldaligned, bitfieldaligned); // CHECK-LABEL: @test_bitfieldaligned_braces() @@ -477,6 +675,10 @@ // CHECK-LABEL: @test_big_uninit() // CHECK: %uninit = alloca %struct.big, align // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_big_uninit() +// PATTERN: call void @llvm.memset{{.*}}, i8 -86, +// ZERO-LABEL: @test_big_uninit() +// ZERO: call void @llvm.memset{{.*}}, i8 0, TEST_BRACES(big, big); // CHECK-LABEL: @test_big_braces() @@ -496,6 +698,10 @@ // CHECK-LABEL: @test_arraytail_uninit() // CHECK: %uninit = alloca %struct.arraytail, align // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_arraytail_uninit() +// PATTERN: call void @llvm.memcpy{{.*}} @__const.test_arraytail_uninit.uninit +// ZERO-LABEL: @test_arraytail_uninit() +// ZERO: call void @llvm.memset{{.*}}, i8 0, TEST_BRACES(arraytail, arraytail); // CHECK-LABEL: @test_arraytail_braces() @@ -516,6 +722,12 @@ // CHECK-LABEL: @test_int0_uninit() // CHECK: %uninit = alloca [0 x i32], align // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_int0_uninit() +// PATTERN: %uninit = alloca [0 x i32], align +// PATTERN-NEXT: call void @{{.*}}used{{.*}}%uninit) +// ZERO-LABEL: @test_int0_uninit() +// ZERO: %uninit = alloca [0 x i32], align +// ZERO-NEXT: call void @{{.*}}used{{.*}}%uninit) TEST_BRACES(int0, int[0]); // CHECK-LABEL: @test_int0_braces() @@ -528,6 +740,10 @@ // CHECK-LABEL: @test_int1_uninit() // CHECK: %uninit = alloca [1 x i32], align [[ALIGN:[0-9]*]] // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_int1_uninit() +// PATTERN: call void @llvm.memcpy{{.*}} @__const.test_int1_uninit.uninit +// ZERO-LABEL: @test_int1_uninit() +// ZERO: call void @llvm.memset{{.*}}, i8 0, TEST_BRACES(int1, int[1]); // CHECK-LABEL: @test_int1_braces() @@ -547,6 +763,10 @@ // CHECK-LABEL: @test_int64_uninit() // CHECK: %uninit = alloca [64 x i32], align // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_int64_uninit() +// PATTERN: call void @llvm.memset{{.*}}, i8 -86, +// ZERO-LABEL: @test_int64_uninit() +// ZERO: call void @llvm.memset{{.*}}, i8 0, TEST_BRACES(int64, int[64]); // CHECK-LABEL: @test_int64_braces() @@ -566,6 +786,10 @@ // CHECK-LABEL: @test_bool4_uninit() // CHECK: %uninit = alloca [4 x i8], align // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_bool4_uninit() +// PATTERN: call void @llvm.memcpy{{.*}} @__const.test_bool4_uninit.uninit +// ZERO-LABEL: @test_bool4_uninit() +// ZERO: call void @llvm.memset{{.*}}, i8 0, TEST_BRACES(bool4, bool[4]); // CHECK-LABEL: @test_bool4_braces() @@ -585,6 +809,10 @@ // CHECK-LABEL: @test_intptr4_uninit() // CHECK: %uninit = alloca [4 x i32*], align // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_intptr4_uninit() +// PATTERN: call void @llvm.memcpy{{.*}} @__const.test_intptr4_uninit.uninit +// ZERO-LABEL: @test_intptr4_uninit() +// ZERO: call void @llvm.memset{{.*}}, i8 0, TEST_BRACES(intptr4, int*[4]); // CHECK-LABEL: @test_intptr4_braces() @@ -604,6 +832,10 @@ // CHECK-LABEL: @test_tailpad4_uninit() // CHECK: %uninit = alloca [4 x %struct.tailpad], align // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_tailpad4_uninit() +// PATTERN: call void @llvm.memcpy{{.*}} @__const.test_tailpad4_uninit.uninit +// ZERO-LABEL: @test_tailpad4_uninit() +// ZERO: call void @llvm.memset{{.*}}, i8 0, TEST_BRACES(tailpad4, tailpad[4]); // CHECK-LABEL: @test_tailpad4_braces() @@ -623,6 +855,10 @@ // CHECK-LABEL: @test_tailpad9_uninit() // CHECK: %uninit = alloca [9 x %struct.tailpad], align // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_tailpad9_uninit() +// PATTERN: call void @llvm.memset{{.*}}, i8 -86, +// ZERO-LABEL: @test_tailpad9_uninit() +// ZERO: call void @llvm.memset{{.*}}, i8 0, TEST_BRACES(tailpad9, tailpad[9]); // CHECK-LABEL: @test_tailpad9_braces() @@ -643,37 +879,65 @@ // CHECK-LABEL: @test_atomicbool_uninit() // CHECK: %uninit = alloca i8, align // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_atomicbool_uninit() +// PATTERN: store i8 -86, i8* %uninit, align 1 +// ZERO-LABEL: @test_atomicbool_uninit() +// ZERO: store i8 0, i8* %uninit, align 1 TEST_UNINIT(atomicint, _Atomic(int)); // CHECK-LABEL: @test_atomicint_uninit() // CHECK: %uninit = alloca i32, align // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_atomicint_uninit() +// PATTERN: store i32 -1431655766, i32* %uninit, align 4 +// ZERO-LABEL: @test_atomicint_uninit() +// ZERO: store i32 0, i32* %uninit, align 4 TEST_UNINIT(atomicdouble, _Atomic(double)); // CHECK-LABEL: @test_atomicdouble_uninit() // CHECK: %uninit = alloca double, align // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_atomicdouble_uninit() +// PATTERN: store double 0xFFFFFFFFFFFFFFFF, double* %uninit, align 8 +// ZERO-LABEL: @test_atomicdouble_uninit() +// ZERO: store double 0.000000e+00, double* %uninit, align 8 TEST_UNINIT(atomicnotlockfree, _Atomic(notlockfree)); // CHECK-LABEL: @test_atomicnotlockfree_uninit() // CHECK: %uninit = alloca %struct.notlockfree, align // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_atomicnotlockfree_uninit() +// PATTERN: call void @llvm.memcpy{{.*}} @__const.test_atomicnotlockfree_uninit.uninit +// ZERO-LABEL: @test_atomicnotlockfree_uninit() +// ZERO: call void @llvm.memset{{.*}}, i8 0, TEST_UNINIT(atomicpadded, _Atomic(padded)); // CHECK-LABEL: @test_atomicpadded_uninit() // CHECK: %uninit = alloca %struct.padded, align // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_atomicpadded_uninit() +// PATTERN: call void @llvm.memcpy{{.*}} @__const.test_atomicpadded_uninit.uninit +// ZERO-LABEL: @test_atomicpadded_uninit() +// ZERO: call void @llvm.memset{{.*}}, i8 0, TEST_UNINIT(atomictailpad, _Atomic(tailpad)); // CHECK-LABEL: @test_atomictailpad_uninit() // CHECK: %uninit = alloca %struct.tailpad, align // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_atomictailpad_uninit() +// PATTERN: call void @llvm.memcpy{{.*}} @__const.test_atomictailpad_uninit.uninit +// ZERO-LABEL: @test_atomictailpad_uninit() +// ZERO: call void @llvm.memset{{.*}}, i8 0, TEST_UNINIT(complexfloat, _Complex float); // CHECK-LABEL: @test_complexfloat_uninit() // CHECK: %uninit = alloca { float, float }, align // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_complexfloat_uninit() +// PATTERN: call void @llvm.memcpy{{.*}} @__const.test_complexfloat_uninit.uninit +// ZERO-LABEL: @test_complexfloat_uninit() +// ZERO: call void @llvm.memset{{.*}}, i8 0, TEST_BRACES(complexfloat, _Complex float); // CHECK-LABEL: @test_complexfloat_braces() @@ -697,6 +961,10 @@ // CHECK-LABEL: @test_complexdouble_uninit() // CHECK: %uninit = alloca { double, double }, align // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_complexdouble_uninit() +// PATTERN: call void @llvm.memcpy{{.*}} @__const.test_complexdouble_uninit.uninit +// ZERO-LABEL: @test_complexdouble_uninit() +// ZERO: call void @llvm.memset{{.*}}, i8 0, TEST_BRACES(complexdouble, _Complex double); // CHECK-LABEL: @test_complexdouble_braces() @@ -721,6 +989,10 @@ // CHECK-LABEL: @test_volatileint_uninit() // CHECK: %uninit = alloca i32, align // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_volatileint_uninit() +// PATTERN: store volatile i32 -1431655766, i32* %uninit, align 4 +// ZERO-LABEL: @test_volatileint_uninit() +// ZERO: store volatile i32 0, i32* %uninit, align 4 TEST_BRACES(volatileint, volatile int); // CHECK-LABEL: @test_volatileint_braces() @@ -732,6 +1004,10 @@ // CHECK-LABEL: @test_semivolatile_uninit() // CHECK: %uninit = alloca %struct.semivolatile, align // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_semivolatile_uninit() +// PATTERN: call void @llvm.memcpy{{.*}} @__const.test_semivolatile_uninit.uninit +// ZERO-LABEL: @test_semivolatile_uninit() +// ZERO: call void @llvm.memset{{.*}}, i8 0, TEST_BRACES(semivolatile, semivolatile); // CHECK-LABEL: @test_semivolatile_braces() @@ -777,6 +1053,10 @@ // CHECK: %uninit = alloca %struct.base, align // CHECK-NEXT: call void @{{.*}}base{{.*}}%uninit) // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_base_uninit() +// PATTERN: call void @llvm.memcpy{{.*}} @__const.test_base_uninit.uninit +// ZERO-LABEL: @test_base_uninit() +// ZERO: call void @llvm.memset{{.*}}, i8 0, TEST_BRACES(base, base); // CHECK-LABEL: @test_base_braces() @@ -791,6 +1071,10 @@ // CHECK: %uninit = alloca %struct.derived, align // CHECK-NEXT: call void @{{.*}}derived{{.*}}%uninit) // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_derived_uninit() +// PATTERN: call void @llvm.memcpy{{.*}} @__const.test_derived_uninit.uninit +// ZERO-LABEL: @test_derived_uninit() +// ZERO: call void @llvm.memset{{.*}}, i8 0, TEST_BRACES(derived, derived); // CHECK-LABEL: @test_derived_braces() @@ -805,6 +1089,10 @@ // CHECK: %uninit = alloca %struct.virtualderived, align // CHECK-NEXT: call void @{{.*}}virtualderived{{.*}}%uninit) // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_virtualderived_uninit() +// PATTERN: call void @llvm.memcpy{{.*}} @__const.test_virtualderived_uninit.uninit +// ZERO-LABEL: @test_virtualderived_uninit() +// ZERO: call void @llvm.memset{{.*}}, i8 0, TEST_BRACES(virtualderived, virtualderived); // CHECK-LABEL: @test_virtualderived_braces() @@ -819,6 +1107,10 @@ // CHECK-LABEL: @test_matching_uninit() // CHECK: %uninit = alloca %union.matching, align // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_matching_uninit() +// PATTERN: call void @llvm.memcpy{{.*}} @__const.test_matching_uninit.uninit +// ZERO-LABEL: @test_matching_uninit() +// ZERO: call void @llvm.memset{{.*}}, i8 0, TEST_BRACES(matching, matching); // CHECK-LABEL: @test_matching_braces() @@ -838,6 +1130,10 @@ // CHECK-LABEL: @test_matchingreverse_uninit() // CHECK: %uninit = alloca %union.matchingreverse, align // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_matchingreverse_uninit() +// PATTERN: call void @llvm.memcpy{{.*}} @__const.test_matchingreverse_uninit.uninit +// ZERO-LABEL: @test_matchingreverse_uninit() +// ZERO: call void @llvm.memset{{.*}}, i8 0, TEST_BRACES(matchingreverse, matchingreverse); // CHECK-LABEL: @test_matchingreverse_braces() @@ -857,6 +1153,10 @@ // CHECK-LABEL: @test_unmatched_uninit() // CHECK: %uninit = alloca %union.unmatched, align // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_unmatched_uninit() +// PATTERN: call void @llvm.memcpy{{.*}} @__const.test_unmatched_uninit.uninit +// ZERO-LABEL: @test_unmatched_uninit() +// ZERO: call void @llvm.memset{{.*}}, i8 0, TEST_BRACES(unmatched, unmatched); // CHECK-LABEL: @test_unmatched_braces() @@ -876,6 +1176,10 @@ // CHECK-LABEL: @test_unmatchedreverse_uninit() // CHECK: %uninit = alloca %union.unmatchedreverse, align // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_unmatchedreverse_uninit() +// PATTERN: call void @llvm.memcpy{{.*}} @__const.test_unmatchedreverse_uninit.uninit +// ZERO-LABEL: @test_unmatchedreverse_uninit() +// ZERO: call void @llvm.memset{{.*}}, i8 0, TEST_BRACES(unmatchedreverse, unmatchedreverse); // CHECK-LABEL: @test_unmatchedreverse_braces() @@ -895,6 +1199,10 @@ // CHECK-LABEL: @test_unmatchedfp_uninit() // CHECK: %uninit = alloca %union.unmatchedfp, align // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_unmatchedfp_uninit() +// PATTERN: call void @llvm.memcpy{{.*}} @__const.test_unmatchedfp_uninit.uninit +// ZERO-LABEL: @test_unmatchedfp_uninit() +// ZERO: call void @llvm.memset{{.*}}, i8 0, TEST_BRACES(unmatchedfp, unmatchedfp); // CHECK-LABEL: @test_unmatchedfp_braces() @@ -915,6 +1223,10 @@ // CHECK-LABEL: @test_emptyenum_uninit() // CHECK: %uninit = alloca i32, align // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_emptyenum_uninit() +// PATTERN: store i32 -1431655766, i32* %braces, align 4 +// ZERO-LABEL: @test_emptyenum_uninit() +// ZERO: store i32 0, i32* %braces, align 4 TEST_BRACES(emptyenum, emptyenum); // CHECK-LABEL: @test_emptyenum_braces() @@ -932,6 +1244,10 @@ // CHECK-LABEL: @test_smallenum_uninit() // CHECK: %uninit = alloca i32, align // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_smallenum_uninit() +// PATTERN: store i32 -1431655766, i32* %braces, align 4 +// ZERO-LABEL: @test_smallenum_uninit() +// ZERO: store i32 0, i32* %braces, align 4 TEST_BRACES(smallenum, smallenum); // CHECK-LABEL: @test_smallenum_braces() @@ -950,6 +1266,10 @@ // CHECK-LABEL: @test_intvec16_uninit() // CHECK: %uninit = alloca <4 x i32>, align // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_intvec16_uninit() +// PATTERN: store <4 x i32> , <4 x i32>* %uninit, align 16 +// ZERO-LABEL: @test_intvec16_uninit() +// ZERO: store <4 x i32> zeroinitializer, <4 x i32>* %uninit, align 16 TEST_BRACES(intvec16, int __attribute__((vector_size(16)))); // CHECK-LABEL: @test_intvec16_braces() @@ -957,7 +1277,7 @@ // CHECK-NEXT: store <4 x i32> zeroinitializer, <4 x i32>* %braces, align [[ALIGN]] // CHECK-NEXT: call void @{{.*}}used{{.*}}%braces) - TEST_CUSTOM(intvec16, int __attribute__((vector_size(16))), { 0x44444444, 0x44444444, 0x44444444, 0x44444444 }); +TEST_CUSTOM(intvec16, int __attribute__((vector_size(16))), { 0x44444444, 0x44444444, 0x44444444, 0x44444444 }); // CHECK-LABEL: @test_intvec16_custom() // CHECK: %custom = alloca <4 x i32>, align [[ALIGN:[0-9]*]] // CHECK-NEXT: store <4 x i32> , <4 x i32>* %custom, align [[ALIGN]] @@ -967,6 +1287,10 @@ // CHECK-LABEL: @test_longlongvec32_uninit() // CHECK: %uninit = alloca <4 x i64>, align // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_longlongvec32_uninit() +// PATTERN: store <4 x i64> , <4 x i64>* %uninit, align 32 +// ZERO-LABEL: @test_longlongvec32_uninit() +// ZERO: store <4 x i64> zeroinitializer, <4 x i64>* %uninit, align 32 TEST_BRACES(longlongvec32, long long __attribute__((vector_size(32)))); // CHECK-LABEL: @test_longlongvec32_braces() @@ -984,6 +1308,10 @@ // CHECK-LABEL: @test_floatvec16_uninit() // CHECK: %uninit = alloca <4 x float>, align // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_floatvec16_uninit() +// PATTERN: store <4 x float> , <4 x float>* %uninit, align 16 +// ZERO-LABEL: @test_floatvec16_uninit() +// ZERO: store <4 x float> zeroinitializer, <4 x float>* %uninit, align 16 TEST_BRACES(floatvec16, float __attribute__((vector_size(16)))); // CHECK-LABEL: @test_floatvec16_braces() @@ -1001,6 +1329,10 @@ // CHECK-LABEL: @test_doublevec32_uninit() // CHECK: %uninit = alloca <4 x double>, align // CHECK-NEXT: call void @{{.*}}used{{.*}}%uninit) +// PATTERN-LABEL: @test_doublevec32_uninit() +// PATTERN: store <4 x double> , <4 x double>* %uninit, align 32 +// ZERO-LABEL: @test_doublevec32_uninit() +// ZERO: store <4 x double> zeroinitializer, <4 x double>* %uninit, align 32 TEST_BRACES(doublevec32, double __attribute__((vector_size(32)))); // CHECK-LABEL: @test_doublevec32_braces() Index: test/CodeGenCXX/trivial-auto-var-init-attribute.cpp =================================================================== --- /dev/null +++ test/CodeGenCXX/trivial-auto-var-init-attribute.cpp @@ -0,0 +1,33 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fblocks %s -emit-llvm -o - | FileCheck %s -check-prefix=UNINIT +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fblocks -ftrivial-auto-var-init=pattern %s -emit-llvm -o - | FileCheck %s -check-prefix=PATTERN +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fblocks -ftrivial-auto-var-init=zero %s -emit-llvm -o - | FileCheck %s -check-prefix=ZERO + +template void used(T &) noexcept; + +extern "C" { + +// UNINIT-LABEL: test_attribute_uninitialized +// ZERO-LABEL: test_attribute_uninitialized +// PATTERN-LABEL: test_attribute_uninitialized +void test_attribute_uninitialized() { + [[clang::trivial_auto_init("uninitialized")]] int i; + used(i); +} + +// UNINIT-LABEL: test_attribute_zero +// ZERO-LABEL: test_attribute_zero +// PATTERN-LABEL: test_attribute_zero +void test_attribute_zero() { + [[clang::trivial_auto_init("zero")]] int i; + used(i); +} + +// UNINIT-LABEL: test_attribute_pattern +// ZERO-LABEL: test_attribute_pattern +// PATTERN-LABEL: test_attribute_pattern +void test_attribute_pattern() { + [[clang::trivial_auto_init("pattern")]] int i; + used(i); +} + +} // extern "C" Index: test/CodeGenCXX/trivial-auto-var-init.cpp =================================================================== --- /dev/null +++ test/CodeGenCXX/trivial-auto-var-init.cpp @@ -0,0 +1,216 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fblocks %s -emit-llvm -o - | FileCheck %s -check-prefix=UNINIT +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fblocks -ftrivial-auto-var-init=pattern %s -emit-llvm -o - | FileCheck %s -check-prefix=PATTERN +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -fblocks -ftrivial-auto-var-init=zero %s -emit-llvm -o - | FileCheck %s -check-prefix=ZERO + +// None of the synthesized globals should contain `undef`. +// PATTERN-NOT: undef +// ZERO-NOT: undef + +template void used(T &) noexcept; + +extern "C" { + +// UNINIT-LABEL: test_selfinit( +// ZERO-LABEL: test_selfinit( +// ZERO: store i32 0, i32* %self, align 4 +// PATTERN-LABEL: test_selfinit( +// PATTERN: store i32 -1431655766, i32* %self, align 4 +void test_selfinit() { + int self = self + 1; + used(self); +} + +// UNINIT-LABEL: test_block( +// ZERO-LABEL: test_block( +// ZERO: store i32 0, i32* %block, align 4 +// PATTERN-LABEL: test_block( +// PATTERN: store i32 -1431655766, i32* %block, align 4 +void test_block() { + __block int block; + used(block); +} + +// This type of code is currently not handled by zero / pattern initialization. +// The test will break when that is fixed. +// UNINIT-LABEL: test_goto_unreachable_value( +// ZERO-LABEL: test_goto_unreachable_value( +// ZERO-NOT: store {{.*}}%oops +// PATTERN-LABEL: test_goto_unreachable_value( +// PATTERN-NOT: store {{.*}}%oops +void test_goto_unreachable_value() { + goto jump; + int oops; + jump: + used(oops); +} + +// This type of code is currently not handled by zero / pattern initialization. +// The test will break when that is fixed. +// UNINIT-LABEL: test_goto( +// ZERO-LABEL: test_goto( +// ZERO: if.then: +// ZERO: br label %jump +// ZERO: store i32 0, i32* %oops, align 4 +// ZERO: br label %jump +// ZERO: jump: +// PATTERN-LABEL: test_goto( +// PATTERN: if.then: +// PATTERN: br label %jump +// PATTERN: store i32 -1431655766, i32* %oops, align 4 +// PATTERN: br label %jump +// PATTERN: jump: +void test_goto(int i) { + if (i) + goto jump; + int oops; + jump: + used(oops); +} + +// This type of code is currently not handled by zero / pattern initialization. +// The test will break when that is fixed. +// UNINIT-LABEL: test_switch( +// ZERO-LABEL: test_switch( +// ZERO: sw.bb: +// ZERO-NEXT: store i32 0, i32* %oops, align 4 +// ZERO: sw.bb1: +// ZERO-NEXT: call void @{{.*}}used +// PATTERN-LABEL: test_switch( +// PATTERN: sw.bb: +// PATTERN-NEXT: store i32 -1431655766, i32* %oops, align 4 +// PATTERN: sw.bb1: +// PATTERN-NEXT: call void @{{.*}}used +void test_switch(int i) { + switch (i) { + case 0: + int oops; + break; + case 1: + used(oops); + } +} + +// UNINIT-LABEL: test_vla( +// ZERO-LABEL: test_vla( +// ZERO: %[[SIZE:[0-9]+]] = mul nuw i64 %{{.*}}, 4 +// ZERO: call void @llvm.memset{{.*}}(i8* align 16 %{{.*}}, i8 0, i64 %[[SIZE]], i1 false) +// PATTERN-LABEL: test_vla( +// PATTERN: %vla.iszerosized = icmp eq i64 %{{.*}}, 0 +// PATTERN: br i1 %vla.iszerosized, label %vla-init.cont, label %vla-setup.loop +// PATTERN: vla-setup.loop: +// PATTERN: %[[SIZE:[0-9]+]] = mul nuw i64 %{{.*}}, 4 +// PATTERN: %vla.begin = bitcast i32* %vla to i8* +// PATTERN: %vla.end = getelementptr inbounds i8, i8* %vla.begin, i64 %[[SIZE]] +// PATTERN: br label %vla-init.loop +// PATTERN: vla-init.loop: +// PATTERN: %vla.cur = phi i8* [ %vla.begin, %vla-setup.loop ], [ %vla.next, %vla-init.loop ] +// PATTERN: call void @llvm.memcpy{{.*}} %vla.cur, {{.*}}@__const.test_vla.vla +// PATTERN: %vla.next = getelementptr inbounds i8, i8* %vla.cur, i64 4 +// PATTERN: %vla-init.isdone = icmp eq i8* %vla.next, %vla.end +// PATTERN: br i1 %vla-init.isdone, label %vla-init.cont, label %vla-init.loop +// PATTERN: vla-init.cont: +// PATTERN: call void @{{.*}}used +void test_vla(int size) { + // Variable-length arrays can't have a zero size according to C11 6.7.6.2/5. + // Neither can they be negative-sized. + // + // We don't use the former fact because some code creates zero-sized VLAs and + // doesn't use them. clang makes these share locations with other stack + // values, which leads to initialization of the wrong values. + // + // We rely on the later fact because it generates better code. + // + // Both cases are caught by UBSan. + int vla[size]; + int *ptr = vla; + used(ptr); +} + +// UNINIT-LABEL: test_struct_vla( +// ZERO-LABEL: test_struct_vla( +// ZERO: %[[SIZE:[0-9]+]] = mul nuw i64 %{{.*}}, 16 +// ZERO: call void @llvm.memset{{.*}}(i8* align 16 %{{.*}}, i8 0, i64 %[[SIZE]], i1 false) +// PATTERN-LABEL: test_struct_vla( +// PATTERN: %vla.iszerosized = icmp eq i64 %{{.*}}, 0 +// PATTERN: br i1 %vla.iszerosized, label %vla-init.cont, label %vla-setup.loop +// PATTERN: vla-setup.loop: +// PATTERN: %[[SIZE:[0-9]+]] = mul nuw i64 %{{.*}}, 16 +// PATTERN: %vla.begin = bitcast %struct.anon* %vla to i8* +// PATTERN: %vla.end = getelementptr inbounds i8, i8* %vla.begin, i64 %[[SIZE]] +// PATTERN: br label %vla-init.loop +// PATTERN: vla-init.loop: +// PATTERN: %vla.cur = phi i8* [ %vla.begin, %vla-setup.loop ], [ %vla.next, %vla-init.loop ] +// PATTERN: call void @llvm.memcpy{{.*}} %vla.cur, {{.*}}@__const.test_struct_vla.vla +// PATTERN: %vla.next = getelementptr inbounds i8, i8* %vla.cur, i64 16 +// PATTERN: %vla-init.isdone = icmp eq i8* %vla.next, %vla.end +// PATTERN: br i1 %vla-init.isdone, label %vla-init.cont, label %vla-init.loop +// PATTERN: vla-init.cont: +// PATTERN: call void @{{.*}}used +void test_struct_vla(int size) { + // Same as above, but with a struct that doesn't just memcpy. + struct { + float f; + char c; + void *ptr; + } vla[size]; + void *ptr = static_cast(vla); + used(ptr); +} + +// UNINIT-LABEL: test_zsa( +// ZERO-LABEL: test_zsa( +// ZERO: %zsa = alloca [0 x i32], align 4 +// ZERO-NOT: %zsa +// ZERO: call void @{{.*}}used +// PATTERN-LABEL: test_zsa( +// PATTERN: %zsa = alloca [0 x i32], align 4 +// PATTERN-NOT: %zsa +// PATTERN: call void @{{.*}}used +void test_zsa(int size) { + // Technically not valid, but as long as clang accepts them we should do + // something sensible (i.e. not store to the zero-size array). + int zsa[0]; + used(zsa); +} + +// UNINIT-LABEL: test_huge_uninit( +// ZERO-LABEL: test_huge_uninit( +// ZERO: call void @llvm.memset{{.*}}, i8 0, i64 65536, +// PATTERN-LABEL: test_huge_uninit( +// PATTTERN: call void @llvm.memset{{.*}}, i8 -86, i64 65536, +void test_huge_uninit() { + // We can't emit this as an inline constant to a store instruction because + // SDNode hits an internal size limit. + char big[65536]; + used(big); +} + +// UNINIT-LABEL: test_huge_small_init( +// ZERO-LABEL: test_huge_small_init( +// ZERO: call void @llvm.memset{{.*}}, i8 0, i64 65536, +// ZERO: store i8 97, +// ZERO: store i8 98, +// ZERO: store i8 99, +// ZERO: store i8 100, +// PATTERN-LABEL: test_huge_small_init( +// PATTTERN: call void @llvm.memset{{.*}}, i8 -86, i64 65536, +// PATTERN: store i8 97, +// PATTERN: store i8 98, +// PATTERN: store i8 99, +// PATTERN: store i8 100, +void test_huge_small_init() { + char big[65536] = { 'a', 'b', 'c', 'd' }; + used(big); +} + +// UNINIT-LABEL: test_huge_larger_init( +// ZERO-LABEL: test_huge_larger_init( +// ZERO: call void @llvm.memcpy{{.*}} @__const.test_huge_larger_init.big, {{.*}}, i64 65536, +// PATTERN-LABEL: test_huge_larger_init( +// PATTERN: call void @llvm.memcpy{{.*}} @__const.test_huge_larger_init.big, {{.*}}, i64 65536, +void test_huge_larger_init() { + char big[65536] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; + used(big); +} + +} // extern "C" Index: test/Driver/clang_f_opts.c =================================================================== --- test/Driver/clang_f_opts.c +++ test/Driver/clang_f_opts.c @@ -542,3 +542,12 @@ // RUN: %clang -### -S -fomit-frame-pointer -fno-omit-frame-pointer -pg %s 2>&1 | FileCheck -check-prefix=CHECK-MIX-NO-OMIT-FP-PG %s // CHECK-NO-MIX-OMIT-FP-PG: '-fomit-frame-pointer' not allowed with '-pg' // CHECK-MIX-NO-OMIT-FP-PG-NOT: '-fomit-frame-pointer' not allowed with '-pg' + +// RUN: %clang -### -S -ftrivial-auto-var-init=uninitialized %s 2>&1 | FileCheck -check-prefix=CHECK-TRIVIAL-UNINIT %s +// RUN: %clang -### -S -ftrivial-auto-var-init=pattern %s 2>&1 | FileCheck -check-prefix=CHECK-TRIVIAL-PATTERN %s +// RUN: %clang -### -S -ftrivial-auto-var-init=zero -enable-trivial-auto-var-init-zero-knowing-it-will-be-removed-from-clang %s 2>&1 | FileCheck -check-prefix=CHECK-TRIVIAL-ZERO-GOOD %s +// RUN: %clang -### -S -ftrivial-auto-var-init=zero %s 2>&1 | FileCheck -check-prefix=CHECK-TRIVIAL-ZERO-BAD %s +// CHECK-TRIVIAL-UNINIT-NOT: hasn't been enabled +// CHECK-TRIVIAL-PATTERN-NOT: hasn't been enabled +// CHECK-TRIVIAL-ZERO-GOOD-NOT: hasn't been enabled +// CHECK-TRIVIAL-ZERO-BAD: hasn't been enabled Index: test/Misc/warning-flags.c =================================================================== --- test/Misc/warning-flags.c +++ test/Misc/warning-flags.c @@ -18,7 +18,7 @@ The list of warnings below should NEVER grow. It should gradually shrink to 0. -CHECK: Warnings without flags (75): +CHECK: Warnings without flags (76): CHECK-NEXT: ext_excess_initializers CHECK-NEXT: ext_excess_initializers_in_char_array_initializer CHECK-NEXT: ext_expected_semi_decl_list @@ -53,6 +53,7 @@ CHECK-NEXT: warn_drv_assuming_mfloat_abi_is CHECK-NEXT: warn_drv_clang_unsupported CHECK-NEXT: warn_drv_pch_not_first_include +CHECK-NEXT: warn_drv_trivial_auto_var_init_zero_disabled CHECK-NEXT: warn_dup_category_def CHECK-NEXT: warn_enum_value_overflow CHECK-NEXT: warn_expected_qualified_after_typename Index: test/Sema/attr-trivial_auto_init.c =================================================================== --- /dev/null +++ test/Sema/attr-trivial_auto_init.c @@ -0,0 +1,26 @@ +// RUN: %clang_cc1 %s -verify -fsyntax-only + +void good() { + int dont_initialize_me __attribute((trivial_auto_init("uninitialized"))); + int zero_me __attribute((trivial_auto_init("zero"))); + int pattern_me __attribute((trivial_auto_init("pattern"))); +} + +void bad() { + int im_bad __attribute((trivial_auto_init)); // expected-error {{'trivial_auto_init' attribute takes one argument}} + int im_baaad __attribute((trivial_auto_init("uninitialized", "zero"))); // expected-error {{'trivial_auto_init' attribute takes one argument}} + static int come_on __attribute((trivial_auto_init("uninitialized"))); // expected-warning {{'trivial_auto_init' attribute only applies to local variables}} + int you_know __attribute((trivial_auto_init("pony"))); // expected-warning {{'trivial_auto_init' attribute argument not supported: pony}} + int and_the_whole_world_has_to __attribute((trivial_auto_init(uninitialized))); // expected-error {{'trivial_auto_init' attribute requires a string}} +} + +extern int answer_right_now __attribute((trivial_auto_init("uninitialized"))); // expected-warning {{'trivial_auto_init' attribute only applies to local variables}} +int just_to_tell_you_once_again __attribute((trivial_auto_init("uninitialized"))); // expected-warning {{'trivial_auto_init' attribute only applies to local variables}} +static int whos_bad __attribute((trivial_auto_init("uninitialized"))); // expected-warning {{'trivial_auto_init' attribute only applies to local variables}} + +void the_word_is_out() __attribute((trivial_auto_init("uninitialized"))) {} // expected-warning {{'trivial_auto_init' attribute only applies to local variables}} +void youre_doin_wrong(__attribute((trivial_auto_init("uninitialized"))) int a) {} // expected-warning {{'trivial_auto_init' attribute only applies to local variables}} + +struct GonnaLockYouUp { + __attribute((trivial_auto_init("uninitialized"))) int before_too_long; // expected-warning {{'trivial_auto_init' attribute only applies to local variables}} +} __attribute((trivial_auto_init("uninitialized"))); // expected-warning {{'trivial_auto_init' attribute only applies to local variables}} Index: test/Sema/uninit-variables.c =================================================================== --- test/Sema/uninit-variables.c +++ test/Sema/uninit-variables.c @@ -1,4 +1,5 @@ // RUN: %clang_cc1 -fsyntax-only -Wuninitialized -Wconditional-uninitialized -fsyntax-only -fblocks %s -verify +// RUN: %clang_cc1 -fsyntax-only -Wuninitialized -Wconditional-uninitialized -ftrivial-auto-var-init=pattern -fsyntax-only -fblocks %s -verify typedef __typeof(sizeof(int)) size_t; void *malloc(size_t);