Index: include/clang/AST/APValue.h =================================================================== --- include/clang/AST/APValue.h +++ include/clang/AST/APValue.h @@ -191,6 +191,7 @@ bool isUnion() const { return Kind == Union; } bool isMemberPointer() const { return Kind == MemberPointer; } bool isAddrLabelDiff() const { return Kind == AddrLabelDiff; } + bool isAllZeros() const; void dump() const; void dump(raw_ostream &OS) const; Index: include/clang/AST/Expr.h =================================================================== --- include/clang/AST/Expr.h +++ include/clang/AST/Expr.h @@ -533,6 +533,10 @@ bool isConstantInitializer(ASTContext &Ctx, bool ForRef, const Expr **Culprit = nullptr) const; + /// isZeroInitializer - Returns true if this initializer is all zeros. + bool isZeroInitializer(ASTContext &Ctx, bool IsForRef, + const Expr **Culprit = nullptr) const; + /// EvalStatus is a struct with detailed info about an evaluation in progress. struct EvalStatus { /// \brief Whether the evaluated expression has side effects. Index: include/clang/Basic/LangOptions.def =================================================================== --- include/clang/Basic/LangOptions.def +++ include/clang/Basic/LangOptions.def @@ -179,6 +179,7 @@ ENUM_LANGOPT(DefaultCallingConv, DefaultCallingConvention, 3, DCC_None, "default calling convention") LANGOPT(ShortEnums , 1, 0, "short enum types") +LANGOPT(AlwaysUseBSS, 1, 0, "place variables initialized to 0 in pragma bss_seg") LANGOPT(OpenCL , 1, 0, "OpenCL") LANGOPT(OpenCLVersion , 32, 0, "OpenCL version") Index: include/clang/Driver/Options.td =================================================================== --- include/clang/Driver/Options.td +++ include/clang/Driver/Options.td @@ -1440,6 +1440,12 @@ def fno_data_sections : Flag <["-"], "fno-data-sections">, Group, Flags<[CC1Option]>; +def falways_use_bss : Flag<["-"], "falways-use-bss">,Group, + Flags<[CC1Option]>, + HelpText<"Place variables initialized to 0 in pragma bss_seg">; +def fno_always_use_bss : Flag<["-"], "fno-always-use-bss">,Group, + Flags<[CC1Option]>; + def funique_section_names : Flag <["-"], "funique-section-names">, Group, Flags<[CC1Option]>, HelpText<"Use unique names for text and data sections (ELF Only)">; Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -1763,6 +1763,7 @@ bool DeduceVariableDeclarationType(VarDecl *VDecl, bool DirectInit, Expr *Init); void CheckCompleteVariableDeclaration(VarDecl *VD); + bool DeclarationIsZeroInitialized(VarDecl *VD); void CheckCompleteDecompositionDeclaration(DecompositionDecl *DD); void MaybeSuggestAddingStaticToDecl(const FunctionDecl *D); Index: lib/AST/APValue.cpp =================================================================== --- lib/AST/APValue.cpp +++ lib/AST/APValue.cpp @@ -656,3 +656,21 @@ MPD->resizePath(Path.size()); memcpy(MPD->getPath(), Path.data(), Path.size()*sizeof(const CXXRecordDecl*)); } + +bool APValue::isAllZeros() const { + switch (getKind()) { + case Uninitialized: + return false; + case Int: + return !getInt().getBoolValue(); + case Float: + return getFloat().isPosZero(); + case ComplexInt: + return !getComplexIntReal().getBoolValue() && !getComplexIntImag().getBoolValue(); + case ComplexFloat: + return getComplexFloatReal().isPosZero() && getComplexFloatImag().isPosZero(); + default: + return false; + } + return false; +} Index: lib/AST/Expr.cpp =================================================================== --- lib/AST/Expr.cpp +++ lib/AST/Expr.cpp @@ -2837,6 +2837,136 @@ return false; } +/// Returns true if the initializer is all zeros. +bool Expr::isZeroInitializer(ASTContext &Ctx, bool IsForRef, + const Expr **Culprit) const { + if (IsForRef) + return false; + + switch (getStmtClass()) { + default: break; + case CompoundLiteralExprClass: { + const Expr *Exp = cast(this)->getInitializer(); + return Exp->isZeroInitializer(Ctx, false, Culprit); + } + case DesignatedInitUpdateExprClass: { + const DesignatedInitUpdateExpr *DIUE = cast(this); + return DIUE->getBase()->isZeroInitializer(Ctx, false, Culprit) && + DIUE->getUpdater()->isZeroInitializer(Ctx, false, Culprit); + } + case InitListExprClass: { + const InitListExpr *ILE = cast(this); + // array initializer + if (ILE->getType()->isArrayType()) { + unsigned numInits = ILE->getNumInits(); + for (unsigned i = 0; i < numInits; i++) { + if (!ILE->getInit(i)->isZeroInitializer(Ctx, false, Culprit)) + return false; + } + return true; + } + + // record initializer + if (ILE->getType()->isRecordType()) { + unsigned ElementNo = 0; + RecordDecl *RD = ILE->getType()->getAs()->getDecl(); + for (const auto *Field : RD->fields()) { + // If this is a union, skip all the fields that aren't being initialized. + if (RD->isUnion() && ILE->getInitializedFieldInUnion() != Field) + continue; + + // Don't emit anonymous bitfields, they just affect layout. + if (Field->isUnnamedBitfield()) + continue; + + if (ElementNo < ILE->getNumInits()) { + const Expr *Elt = ILE->getInit(ElementNo++); + if (Field->isBitField()) { + // Bitfields have to evaluate to an integer. + llvm::APSInt ResultTmp; + if (!Elt->EvaluateAsInt(ResultTmp, Ctx)) { + if (Culprit) + *Culprit = Elt; + return false; + } + if (ResultTmp != 0) + return false; + } else { + //bool RefType = Field->getType()->isReferenceType(); + if (!Elt->isZeroInitializer(Ctx, false, Culprit)) + return false; + } + } + } + return true; + } + break; + } + case ImplicitValueInitExprClass: + case NoInitExprClass: + return true; + case ParenExprClass: + return cast(this)->getSubExpr() + ->isZeroInitializer(Ctx, false, Culprit); + case GenericSelectionExprClass: + return cast(this)->getResultExpr() + ->isZeroInitializer(Ctx, false, Culprit); + case ChooseExprClass: + if (cast(this)->isConditionDependent()) { + if (Culprit) + *Culprit = this; + return false; + } + return cast(this)->getChosenSubExpr() + ->isZeroInitializer(Ctx, false, Culprit); + case UnaryOperatorClass: { + const UnaryOperator* Exp = cast(this); + if (Exp->getOpcode() == UO_Extension) + return Exp->getSubExpr()->isZeroInitializer(Ctx, false, Culprit); + break; + } + case CXXFunctionalCastExprClass: + case CXXStaticCastExprClass: + case ImplicitCastExprClass: + case CStyleCastExprClass: + case ObjCBridgedCastExprClass: + case CXXDynamicCastExprClass: + case CXXReinterpretCastExprClass: + case CXXConstCastExprClass: { + const CastExpr *CE = cast(this); + + if (CE->getCastKind() == CK_NoOp || + CE->getCastKind() == CK_LValueToRValue || + CE->getCastKind() == CK_ToUnion || + CE->getCastKind() == CK_ConstructorConversion || + CE->getCastKind() == CK_NonAtomicToAtomic || + CE->getCastKind() == CK_AtomicToNonAtomic || + CE->getCastKind() == CK_IntToOCLSampler) + return CE->getSubExpr()->isZeroInitializer(Ctx, false, Culprit); + + break; + } + case MaterializeTemporaryExprClass: + return cast(this)->GetTemporaryExpr() + ->isZeroInitializer(Ctx, false, Culprit); + + case SubstNonTypeTemplateParmExprClass: + return cast(this)->getReplacement() + ->isZeroInitializer(Ctx, false, Culprit); + case CXXDefaultArgExprClass: + return cast(this)->getExpr() + ->isZeroInitializer(Ctx, false, Culprit); + case CXXDefaultInitExprClass: + return cast(this)->getExpr() + ->isZeroInitializer(Ctx, false, Culprit); + } + + EvalResult Result; + if (EvaluateAsRValue(Result, Ctx)) + return Result.Val.isAllZeros(); + return false; +} + namespace { /// \brief Look for any side effects within a Stmt. class SideEffectFinder : public ConstEvaluatedExprVisitor { Index: lib/Driver/Tools.cpp =================================================================== --- lib/Driver/Tools.cpp +++ lib/Driver/Tools.cpp @@ -5935,6 +5935,10 @@ IsWindowsMSVC)) CmdArgs.push_back("-fms-extensions"); + if (Args.hasFlag(options::OPT_falways_use_bss, + options::OPT_fno_always_use_bss, false)) + CmdArgs.push_back("-always-use-bss"); + // -fno-use-line-directives is default. if (Args.hasFlag(options::OPT_fuse_line_directives, options::OPT_fno_use_line_directives, false)) Index: lib/Frontend/CompilerInvocation.cpp =================================================================== --- lib/Frontend/CompilerInvocation.cpp +++ lib/Frontend/CompilerInvocation.cpp @@ -1917,6 +1917,8 @@ VT.getSubminor().getValueOr(0); } + Opts.AlwaysUseBSS = Args.hasArg(OPT_falways_use_bss); + // Mimicing gcc's behavior, trigraphs are only enabled if -trigraphs // is specified, or -std is set to a conforming mode. // Trigraphs are disabled by default in c++1z onwards. Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -10736,6 +10736,18 @@ AttrEnd.isValid() ? AttrEnd : IdentLoc); } +/// Check if initialization is to zero +bool Sema::DeclarationIsZeroInitialized(VarDecl *var) { + Expr* InitExpr = var->getInit(); + if (!InitExpr) + return var->hasGlobalStorage(); + + const Expr *Culprit; + if (!InitExpr->isConstantInitializer(Context, false, &Culprit)) + return false; + return InitExpr->isZeroInitializer(Context, false, &Culprit); +} + void Sema::CheckCompleteVariableDeclaration(VarDecl *var) { if (var->isInvalidDecl()) return; @@ -10827,7 +10839,9 @@ int SectionFlags = ASTContext::PSF_Implicit | ASTContext::PSF_Read; if (var->getType().isConstQualified()) Stack = &ConstSegStack; - else if (!var->getInit()) { + else if (!var->getInit() || + (getLangOpts().AlwaysUseBSS && + DeclarationIsZeroInitialized(var))) { Stack = &BSSSegStack; SectionFlags |= ASTContext::PSF_Write; } else { Index: test/CodeGenCXX/zi-sections.cpp =================================================================== --- /dev/null +++ test/CodeGenCXX/zi-sections.cpp @@ -0,0 +1,75 @@ +// RUN: %clang_cc1 -emit-llvm -triple i686-pc-win32 -fms-extensions -o - %s | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-ZI-DATA +// RUN: %clang_cc1 -emit-llvm -triple i686-pc-win32 -fms-extensions -falways-use-bss -o - %s | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-ZI-BSS +// Test that when '-falways-use-bss' is specified, variables initialized with zero are assigned to bss_seg. +#pragma bss_seg(".bss.1") +#pragma data_seg(".data.1") +extern "C" { +short ShortZero = 0; +short ShortTwo = 2; +short ShortMinusTwo = -2; +short ShortUnInit; + +int IntZero = 0; +int IntTwo = 2; +int IntMinusTwo = -2; +int IntUnInit; + +float FloatZero = 0.0; +float FloatMinusZero = -0.0; +float FloatTwo = 2.0; +float FloatUnInit; + +double DoubleZero = 0.0; +double DoubleMinusZero = -0.0; +double DoubleTwo = 2.0; +double DoubleUnInit; + +struct S { + int x, y; + int z; +}; +struct S StructZero = { 0, 0, 0 }; +struct S StructTwo = { 0, 0, 2}; +struct S StructMinusTwo = {0, -2, 0}; +struct S StructUnInit; + +int ArrayZero[] = {0, 0, 0}; +int ArrayTwo[] = {0, 2, 0}; +int ArrayMinusTwo[] = {0, -2, 0}; +int ArrayUnInit; +// CHECK-ZI-DATA: @ShortZero = global i16 0, section ".data.1", align 2 +// CHECK-ZI-BSS: @ShortZero = global i16 0, section ".bss.1", align 2 +// CHECK: @ShortTwo = global i16 2, section ".data.1", align 2 +// CHECK: @ShortMinusTwo = global i16 -2, section ".data.1", align 2 +// CHECK: @ShortUnInit = global i16 0, section ".bss.1", align 2 +// +// CHECK-ZI-DATA: @IntZero = global i32 0, section ".data.1", align 4 +// CHECK-ZI-BSS: @IntZero = global i32 0, section ".bss.1", align 4 +// CHECK: @IntTwo = global i32 2, section ".data.1", align 4 +// CHECK: @IntMinusTwo = global i32 -2, section ".data.1", align 4 +// CHECK: @IntUnInit = global i32 0, section ".bss.1", align 4 +// +// CHECK-ZI-DATA: @FloatZero = global float 0.000000e+00, section ".data.1", align 4 +// CHECK-ZI-BSS: @FloatZero = global float 0.000000e+00, section ".bss.1", align 4 +// CHECK: @FloatMinusZero = global float -0.000000e+00, section ".data.1", align 4 +// CHECK: @FloatTwo = global float 2.000000e+00, section ".data.1", align 4 +// CHECK: @FloatUnInit = global float 0.000000e+00, section ".bss.1", align 4 +// +// CHECK-ZI-DATA: @DoubleZero = global double 0.000000e+00, section ".data.1", align 8 +// CHECK-ZI-BSS: @DoubleZero = global double 0.000000e+00, section ".bss.1", align 8 +// CHECK: @DoubleMinusZero = global double -0.000000e+00, section ".data.1", align 8 +// CHECK: @DoubleTwo = global double 2.000000e+00, section ".data.1", align 8 +// CHECK: @DoubleUnInit = global double 0.000000e+00, section ".bss.1", align 8 +// +// CHECK-ZI-DATA: @StructZero = global %struct.S zeroinitializer, section ".data.1", align 4 +// CHECK-ZI-BSS: @StructZero = global %struct.S zeroinitializer, section ".bss.1", align 4 +// CHECK: @StructTwo = global %struct.S { i32 0, i32 0, i32 2 }, section ".data.1", align 4 +// CHECK: @StructMinusTwo = global %struct.S { i32 0, i32 -2, i32 0 }, section ".data.1", align 4 +// CHECK: @StructUnInit = global %struct.S zeroinitializer, section ".data.1", align 4 +// +// CHECK-ZI-DATA: @ArrayZero = global [3 x i32] zeroinitializer, section ".data.1", align 4 +// CHECK-ZI-BSS: @ArrayZero = global [3 x i32] zeroinitializer, section ".bss.1", align 4 +// CHECK: @ArrayTwo = global [3 x i32] [i32 0, i32 2, i32 0], section ".data.1", align 4 +// CHECK: @ArrayMinusTwo = global [3 x i32] [i32 0, i32 -2, i32 0], section ".data.1", align 4 +// CHECK: @ArrayUnInit = global i32 0, section ".bss.1", align 4 +}