Index: llvm/docs/LangRef.rst =================================================================== --- llvm/docs/LangRef.rst +++ llvm/docs/LangRef.rst @@ -7094,6 +7094,16 @@ %a.addr = alloca float*, align 8, !annotation !0 !0 = !{!"auto-init"} +'``explicit_size``' Metadata +^^^^^^^^^^^^^^^^^^^^^^^^^ + +The ``explicit_size`` metadata may be attached to a global variable definition +with a size different from the object's total size. This can be useful when an +instrumentation enlarges the object while the symbol size should reflect the +accessible or meaningful part of the object. This is currently only meaningful +on ELF where an objects ``st_size`` can be controlled with a ``.size`` +directive. This metadata is ignored in contexts where it cannot be honored. + Module Flags Metadata ===================== Index: llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp =================================================================== --- llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp +++ llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp @@ -690,10 +690,19 @@ emitGlobalConstant(GV->getParent()->getDataLayout(), GV->getInitializer()); - if (MAI->hasDotTypeDotSizeDirective()) + if (MAI->hasDotTypeDotSizeDirective()) { + if (const MDNode *ExplicitValue = GV->getMetadata("explicit_size")) { + assert(ExplicitValue->getNumOperands() && + "Should have been checked by verifier allready"); + const auto *MetadataValue = + cast(ExplicitValue->getOperand(0)); + const auto *CI = cast(MetadataValue->getValue()); + Size = CI->getZExtValue(); + } // .size foo, 42 OutStreamer->emitELFSize(EmittedInitSym, MCConstantExpr::create(Size, OutContext)); + } OutStreamer->AddBlankLine(); } Index: llvm/lib/IR/Verifier.cpp =================================================================== --- llvm/lib/IR/Verifier.cpp +++ llvm/lib/IR/Verifier.cpp @@ -776,6 +776,20 @@ "DIGlobalVariableExpression"); } + MDs.clear(); + GV.getMetadata("explicit_size", MDs); + if (MDs.size()) { + Check(MDs.size() == 1, + "only one !explicit_size can be attached to a global variable"); + Check(MDs[0]->getNumOperands() == 1, + "!explicit_size must have exactly one operand"); + const auto *MetadataValue = + dyn_cast(MDs[0]->getOperand(0)); + Check(MetadataValue, "!explicit_size operand must be a value"); + const auto *CI = dyn_cast(MetadataValue->getValue()); + Check(CI, "!explicit_size value must be an integer"); + } + // Scalable vectors cannot be global variables, since we don't know // the runtime size. If the global is an array containing scalable vectors, // that will be caught by the isValidElementType methods in StructType or Index: llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp =================================================================== --- llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp +++ llvm/lib/Transforms/Instrumentation/AddressSanitizer.cpp @@ -2510,6 +2510,12 @@ // zero so we can copy the metadata over as is. NewGlobal->copyMetadata(G, 0); + LLVMContext &C = NewGlobal->getContext(); + MDBuilder MDB{C}; + MDTuple *MDN = MDNode::get(C, MDB.createConstant(ConstantInt::get( + Type::getInt64Ty(C), SizeInBytes))); + NewGlobal->setMetadata("explicit_size", MDN); + Value *Indices2[2]; Indices2[0] = IRB.getInt32(0); Indices2[1] = IRB.getInt32(0); Index: llvm/test/CodeGen/X86/explicit-size-metadata.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/X86/explicit-size-metadata.ll @@ -0,0 +1,15 @@ +; RUN: llc < %s -mtriple=x86_64 | FileCheck %s + +@a = global i64 0, align 8, !explicit_size !0 +; CHECK: .size a, 4 + +@b = global i64 0, align 8 +; CHECK: .size b, 8 + +@larger = global i16 0, align 4, !explicit_size !0 +; CHECK: .size larger, 4 + +@array = global { [8 x i8] } zeroinitializer, !explicit_size !0 +; CHECK: .size array, 4 + +!0 = !{i64 4} Index: llvm/test/Instrumentation/AddressSanitizer/adaptive_global_redzones.ll =================================================================== --- llvm/test/Instrumentation/AddressSanitizer/adaptive_global_redzones.ll +++ llvm/test/Instrumentation/AddressSanitizer/adaptive_global_redzones.ll @@ -6,14 +6,14 @@ ; Here we check that the global redzone sizes grow with the object size. @G10 = global [10 x i8] zeroinitializer, align 1 -; CHECK: @G10 = global { [10 x i8], [22 x i8] } +; CHECK: @G10 = global { [10 x i8], [22 x i8] } zeroinitializer, align 32, !explicit_size ![[#TAG10:]] @G31 = global [31 x i8] zeroinitializer, align 1 @G32 = global [32 x i8] zeroinitializer, align 1 @G33 = global [33 x i8] zeroinitializer, align 1 -; CHECK: @G31 = global { [31 x i8], [33 x i8] } -; CHECK: @G32 = global { [32 x i8], [32 x i8] } -; CHECK: @G33 = global { [33 x i8], [63 x i8] } +; CHECK: @G31 = global { [31 x i8], [33 x i8] } zeroinitializer, align 32, !explicit_size ![[#TAG31:]] +; CHECK: @G32 = global { [32 x i8], [32 x i8] } zeroinitializer, align 32, !explicit_size ![[#TAG32:]] +; CHECK: @G33 = global { [33 x i8], [63 x i8] } zeroinitializer, align 32, !explicit_size ![[#TAG33:]] @G63 = global [63 x i8] zeroinitializer, align 1 @G64 = global [64 x i8] zeroinitializer, align 1 @@ -53,6 +53,12 @@ @G1000000 = global [1000000 x i8] zeroinitializer, align 1 @G10000000 = global [10000000 x i8] zeroinitializer, align 1 @G100000000 = global [100000000 x i8] zeroinitializer, align 1 -; CHECK: @G1000000 = global { [1000000 x i8], [249984 x i8] } -; CHECK: @G10000000 = global { [10000000 x i8], [262144 x i8] } -; CHECK: @G100000000 = global { [100000000 x i8], [262144 x i8] } +; CHECK: @G1000000 = global { [1000000 x i8], [249984 x i8] } zeroinitializer, align 32, !explicit_size ![[#]] +; CHECK: @G10000000 = global { [10000000 x i8], [262144 x i8] } zeroinitializer, align 32, !explicit_size ![[#]] +; CHECK: @G100000000 = global { [100000000 x i8], [262144 x i8] } zeroinitializer, align 32, !explicit_size ![[#TAGBIG:]] + +; CHECK: ![[#TAG10]] = !{i64 10} +; CHECK: ![[#TAG31]] = !{i64 31} +; CHECK: ![[#TAG32]] = !{i64 32} +; CHECK: ![[#TAG33]] = !{i64 33} +; CHECK: ![[#TAGBIG]] = !{i64 100000000} Index: llvm/test/Instrumentation/AddressSanitizer/debug-info-global-var.ll =================================================================== --- llvm/test/Instrumentation/AddressSanitizer/debug-info-global-var.ll +++ llvm/test/Instrumentation/AddressSanitizer/debug-info-global-var.ll @@ -2,7 +2,7 @@ source_filename = "version.c" target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-apple-macosx10.12.0" -; CHECK: @version = constant { [5 x i8], [27 x i8] } {{.*}}, !dbg ![[GV:.*]] +; CHECK: @version = constant { [5 x i8], [27 x i8] } {{.*}}, !dbg ![[GV:.*]], @version = constant [5 x i8] c"4.00\00", align 1, !dbg !0 Index: llvm/test/Verifier/explicit_size.ll =================================================================== --- /dev/null +++ llvm/test/Verifier/explicit_size.ll @@ -0,0 +1,31 @@ +; RUN: split-file %s %t +; RUN: not opt -verify -disable-output < %t/multiple.ll 2>&1 | FileCheck %s --check-prefix=MULTIPLE +; RUN: not opt -verify -disable-output < %t/operands.ll 2>&1 | FileCheck %s --check-prefix=OPERANDS +; RUN: not opt -verify -disable-output < %t/invalid_md.ll 2>&1 | FileCheck %s --check-prefix=INVALID_MD +; RUN: not opt -verify -disable-output < %t/not_int.ll 2>&1 | FileCheck %s --check-prefix=NOT_INT + +; MULTIPLE: only one !explicit_size can be attached to a global variable +;--- multiple.ll +@a = global { i32, [28 x i8] } zeroinitializer, align 32, !explicit_size !0, !explicit_size !0 + +!0 = !{i64 4} + +; OPERANDS: !explicit_size must have exactly one operand +;--- operands.ll +@a = global { i32, [28 x i8] } zeroinitializer, align 32, !explicit_size !0 + +!0 = !{i64 4, i64 4} + +; INVALID_MD: !explicit_size operand must be a value +;--- invalid_md.ll +@a = global { i32, [28 x i8] } zeroinitializer, align 32, !explicit_size !0 + +!0 = !{!1} +!1 = !{i64 4} + +; NOT_INT: !explicit_size value must be an integer +;--- not_int.ll +@a = global { i32, [28 x i8] } zeroinitializer, align 32, !explicit_size !0 +declare i32 @b() + +!0 = !{i32 ()* @b}