Implemented global redzones.
BUG=https://bugs.chromium.org/p/nativeclient/issues/detail?id=4374
R=kschimpf@google.com
Review URL: https://codereview.chromium.org/2054943002 .
diff --git a/Makefile.standalone b/Makefile.standalone
index 6c40903..4a9470d 100644
--- a/Makefile.standalone
+++ b/Makefile.standalone
@@ -282,6 +282,7 @@
# List the target-specific source files first, which generally take longer to
# compile, in the hope of improving parallel build time.
SRCS = \
+ IceASanInstrumentation.cpp \
IceAssemblerARM32.cpp \
IceInstrumentation.cpp \
IceInstARM32.cpp \
diff --git a/src/IceASanInstrumentation.cpp b/src/IceASanInstrumentation.cpp
new file mode 100644
index 0000000..c0b87dc
--- /dev/null
+++ b/src/IceASanInstrumentation.cpp
@@ -0,0 +1,114 @@
+//===- subzero/src/IceASanInstrumentation.cpp - ASan ------------*- C++ -*-===//
+//
+// The Subzero Code Generator
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Implements the AddressSanitizer instrumentation class.
+///
+//===----------------------------------------------------------------------===//
+
+#include "IceASanInstrumentation.h"
+
+#include "IceBuildDefs.h"
+#include "IceGlobalInits.h"
+
+#include <sstream>
+
+namespace Ice {
+
+namespace {
+constexpr SizeT RzSize = 32;
+const std::string RzPrefix = "__$rz";
+const llvm::NaClBitcodeRecord::RecordVector RzContents =
+ llvm::NaClBitcodeRecord::RecordVector(RzSize, 'R');
+} // end of anonymous namespace
+
+// Create redzones between all global variables, ensuring that the initializer
+// types of the redzones and their associated globals match so that they are
+// laid out together in memory.
+void ASanInstrumentation::instrumentGlobals(VariableDeclarationList &Globals) {
+ if (BuildDefs::minimal() || DidInsertRedZones)
+ return;
+
+ VariableDeclarationList NewGlobals;
+ // Global holding pointers to all redzones
+ auto *RzArray = VariableDeclaration::create(&NewGlobals);
+ // Global holding the size of RzArray
+ auto *RzArraySizeVar = VariableDeclaration::create(&NewGlobals);
+ SizeT RzArraySize = 0;
+
+ RzArray->setName(Ctx, nextRzName());
+ RzArraySizeVar->setName(Ctx, nextRzName());
+ RzArray->setIsConstant(true);
+ RzArraySizeVar->setIsConstant(true);
+ NewGlobals.push_back(RzArray);
+ NewGlobals.push_back(RzArraySizeVar);
+
+ for (VariableDeclaration *Global : Globals) {
+ VariableDeclaration *RzLeft = createRz(&NewGlobals, RzArray, RzArraySize, Global);
+ VariableDeclaration *RzRight = createRz(&NewGlobals, RzArray, RzArraySize, Global);
+ NewGlobals.push_back(RzLeft);
+ NewGlobals.push_back(Global);
+ NewGlobals.push_back(RzRight);
+ }
+
+ // update the contents of the RzArraySize global
+ llvm::NaClBitcodeRecord::RecordVector SizeContents;
+ for (unsigned i = 0; i < sizeof(RzArraySize); i++) {
+ SizeContents.emplace_back(RzArraySize % (1 << CHAR_BIT));
+ RzArraySize >>= CHAR_BIT;
+ }
+ RzArraySizeVar->addInitializer(
+ VariableDeclaration::DataInitializer::create(&NewGlobals, SizeContents));
+
+ // Replace old list of globals, without messing up arena allocators
+ Globals.clear();
+ Globals.merge(&NewGlobals);
+ DidInsertRedZones = true;
+
+ // Log the new set of globals
+ if (BuildDefs::dump() && (getFlags().getVerbose() & IceV_GlobalInit)) {
+ OstreamLocker _(Ctx);
+ Ctx->getStrDump() << "========= Instrumented Globals =========\n";
+ for (VariableDeclaration *Global : Globals) {
+ Global->dump(Ctx->getStrDump());
+ }
+ }
+}
+
+std::string ASanInstrumentation::nextRzName() {
+ if (BuildDefs::minimal())
+ return "";
+ std::stringstream Name;
+ Name << RzPrefix << RzNum++;
+ return Name.str();
+}
+
+VariableDeclaration *
+ASanInstrumentation::createRz(VariableDeclarationList *List,
+ VariableDeclaration *RzArray, SizeT &RzArraySize,
+ VariableDeclaration *Global) {
+ if (BuildDefs::minimal())
+ return nullptr;
+ auto *Rz = VariableDeclaration::create(List);
+ Rz->setName(Ctx, nextRzName());
+ if (Global->hasNonzeroInitializer()) {
+ Rz->addInitializer(
+ VariableDeclaration::DataInitializer::create(List, RzContents));
+ } else {
+ Rz->addInitializer(
+ VariableDeclaration::ZeroInitializer::create(List, RzSize));
+ }
+ Rz->setIsConstant(Global->getIsConstant());
+ RzArray->addInitializer(VariableDeclaration::RelocInitializer::create(
+ List, Rz, RelocOffsetArray(0)));
+ ++RzArraySize;
+ return Rz;
+}
+
+} // end of namespace Ice
diff --git a/src/IceASanInstrumentation.h b/src/IceASanInstrumentation.h
new file mode 100644
index 0000000..ee20e7d
--- /dev/null
+++ b/src/IceASanInstrumentation.h
@@ -0,0 +1,48 @@
+//===- subzero/src/IceASanInstrumentation.h - AddressSanitizer --*- C++ -*-===//
+//
+// The Subzero Code Generator
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// \brief Declares the AddressSanitizer instrumentation class.
+///
+/// This class is responsible for inserting redzones around global and stack
+/// variables, inserting code responsible for poisoning those redzones, and
+/// performing any other instrumentation necessary to implement
+/// AddressSanitizer.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef SUBZERO_SRC_ICEASANINSTRUMENTATION_H
+#define SUBZERO_SRC_ICEASANINSTRUMENTATION_H
+
+#include "IceGlobalInits.h"
+#include "IceInstrumentation.h"
+
+namespace Ice {
+
+class ASanInstrumentation : public Instrumentation {
+ ASanInstrumentation() = delete;
+ ASanInstrumentation(const ASanInstrumentation &) = delete;
+ ASanInstrumentation &operator=(const ASanInstrumentation &) = delete;
+
+public:
+ ASanInstrumentation(GlobalContext *Ctx) : Instrumentation(Ctx) {}
+ void instrumentGlobals(VariableDeclarationList &Globals) override;
+
+private:
+ std::string nextRzName();
+ VariableDeclaration *createRz(VariableDeclarationList *List,
+ VariableDeclaration *RzArray,
+ SizeT &RzArraySize,
+ VariableDeclaration *Global);
+ bool DidInsertRedZones = false;
+ uint32_t RzNum = 0;
+};
+} // end of namespace Ice
+
+#endif // SUBZERO_SRC_ICEASANINSTRUMENTATION_H
diff --git a/src/IceCompileServer.cpp b/src/IceCompileServer.cpp
index 19b19f8..b1f168c 100644
--- a/src/IceCompileServer.cpp
+++ b/src/IceCompileServer.cpp
@@ -14,6 +14,7 @@
#include "IceCompileServer.h"
+#include "IceASanInstrumentation.h"
#include "IceClFlags.h"
#include "IceELFStreamer.h"
#include "IceGlobalContext.h"
@@ -244,7 +245,7 @@
// TODO(tlively): Make this instantiate an instrumentation subclass
if (!BuildDefs::minimal() && getFlags().getSanitizeAddresses()) {
- std::unique_ptr<Instrumentation> Instr(new Instrumentation(Ctx.get()));
+ std::unique_ptr<Instrumentation> Instr(new ASanInstrumentation(Ctx.get()));
Ctx->setInstrumentation(std::move(Instr));
}
diff --git a/src/IceDefs.h b/src/IceDefs.h
index d7ef288..2c09ba9 100644
--- a/src/IceDefs.h
+++ b/src/IceDefs.h
@@ -177,7 +177,7 @@
}
// This do nothing method is invoked when a global variable is created, but it
- // will not be emitted. If we ever need to track the created variabled, having
+ // will not be emitted. If we ever need to track the created variable, having
// this hook is handy.
void willNotBeEmitted(VariableDeclaration *) {}
diff --git a/src/IceGlobalContext.cpp b/src/IceGlobalContext.cpp
index 0d53127..a1e817b 100644
--- a/src/IceGlobalContext.cpp
+++ b/src/IceGlobalContext.cpp
@@ -488,7 +488,7 @@
}
if (!BuildDefs::minimal() && Instrumentor)
- Instrumentor->instrumentGlobals();
+ Instrumentor->instrumentGlobals(Globals);
DataLowering->lowerGlobals(Globals, SectionSuffix);
if (ProfileBlockInfos.empty() && DisposeGlobalVariablesAfterLowering) {
diff --git a/src/IceGlobalInits.h b/src/IceGlobalInits.h
index 378d5d7..e951734 100644
--- a/src/IceGlobalInits.h
+++ b/src/IceGlobalInits.h
@@ -279,7 +279,7 @@
: Initializer(DataInitializerKind), ContentsSize(Values.size()),
// ugh, we should actually do new char[], but this may involve
// implementation-specific details. Given that Contents is arena
- // allocated, and never detele[]d, just use char --
+ // allocated, and never delete[]d, just use char --
// AllocOwner->allocate_array will allocate a buffer with the right
// size.
Contents(new (VDL->allocate_initializer<char>(ContentsSize)) char) {
diff --git a/src/IceInstrumentation.cpp b/src/IceInstrumentation.cpp
index 383e151..0d56100 100644
--- a/src/IceInstrumentation.cpp
+++ b/src/IceInstrumentation.cpp
@@ -47,61 +47,61 @@
assert(!Context.atEnd());
Inst *Instr = iteratorToInst(Context.getCur());
switch (Instr->getKind()) {
- case Inst::Alloca:
- instrumentAlloca(Context, llvm::cast<InstAlloca>(Instr));
- break;
- case Inst::Arithmetic:
- instrumentArithmetic(Context, llvm::cast<InstArithmetic>(Instr));
- break;
- case Inst::Br:
- instrumentBr(Context, llvm::cast<InstBr>(Instr));
- break;
- case Inst::Call:
- instrumentCall(Context, llvm::cast<InstCall>(Instr));
- break;
- case Inst::Cast:
- instrumentCast(Context, llvm::cast<InstCast>(Instr));
- break;
- case Inst::ExtractElement:
- instrumentExtractElement(Context, llvm::cast<InstExtractElement>(Instr));
- break;
- case Inst::Fcmp:
- instrumentFcmp(Context, llvm::cast<InstFcmp>(Instr));
- break;
- case Inst::Icmp:
- instrumentIcmp(Context, llvm::cast<InstIcmp>(Instr));
- break;
- case Inst::InsertElement:
- instrumentInsertElement(Context, llvm::cast<InstInsertElement>(Instr));
- break;
- case Inst::IntrinsicCall:
- instrumentIntrinsicCall(Context, llvm::cast<InstIntrinsicCall>(Instr));
- break;
- case Inst::Load:
- instrumentLoad(Context, llvm::cast<InstLoad>(Instr));
- break;
- case Inst::Phi:
- instrumentPhi(Context, llvm::cast<InstPhi>(Instr));
- break;
- case Inst::Ret:
- instrumentRet(Context, llvm::cast<InstRet>(Instr));
- break;
- case Inst::Select:
- instrumentSelect(Context, llvm::cast<InstSelect>(Instr));
- break;
- case Inst::Store:
- instrumentStore(Context, llvm::cast<InstStore>(Instr));
- break;
- case Inst::Switch:
- instrumentSwitch(Context, llvm::cast<InstSwitch>(Instr));
- break;
- case Inst::Unreachable:
- instrumentUnreachable(Context, llvm::cast<InstUnreachable>(Instr));
- break;
- default:
- // Only instrument high-level ICE instructions
- assert(false && "Instrumentation encountered an unexpected instruction");
- break;
+ case Inst::Alloca:
+ instrumentAlloca(Context, llvm::cast<InstAlloca>(Instr));
+ break;
+ case Inst::Arithmetic:
+ instrumentArithmetic(Context, llvm::cast<InstArithmetic>(Instr));
+ break;
+ case Inst::Br:
+ instrumentBr(Context, llvm::cast<InstBr>(Instr));
+ break;
+ case Inst::Call:
+ instrumentCall(Context, llvm::cast<InstCall>(Instr));
+ break;
+ case Inst::Cast:
+ instrumentCast(Context, llvm::cast<InstCast>(Instr));
+ break;
+ case Inst::ExtractElement:
+ instrumentExtractElement(Context, llvm::cast<InstExtractElement>(Instr));
+ break;
+ case Inst::Fcmp:
+ instrumentFcmp(Context, llvm::cast<InstFcmp>(Instr));
+ break;
+ case Inst::Icmp:
+ instrumentIcmp(Context, llvm::cast<InstIcmp>(Instr));
+ break;
+ case Inst::InsertElement:
+ instrumentInsertElement(Context, llvm::cast<InstInsertElement>(Instr));
+ break;
+ case Inst::IntrinsicCall:
+ instrumentIntrinsicCall(Context, llvm::cast<InstIntrinsicCall>(Instr));
+ break;
+ case Inst::Load:
+ instrumentLoad(Context, llvm::cast<InstLoad>(Instr));
+ break;
+ case Inst::Phi:
+ instrumentPhi(Context, llvm::cast<InstPhi>(Instr));
+ break;
+ case Inst::Ret:
+ instrumentRet(Context, llvm::cast<InstRet>(Instr));
+ break;
+ case Inst::Select:
+ instrumentSelect(Context, llvm::cast<InstSelect>(Instr));
+ break;
+ case Inst::Store:
+ instrumentStore(Context, llvm::cast<InstStore>(Instr));
+ break;
+ case Inst::Switch:
+ instrumentSwitch(Context, llvm::cast<InstSwitch>(Instr));
+ break;
+ case Inst::Unreachable:
+ instrumentUnreachable(Context, llvm::cast<InstUnreachable>(Instr));
+ break;
+ default:
+ // Only instrument high-level ICE instructions
+ assert(false && "Instrumentation encountered an unexpected instruction");
+ break;
}
}
diff --git a/src/IceInstrumentation.h b/src/IceInstrumentation.h
index abd5d08..a7e099b 100644
--- a/src/IceInstrumentation.h
+++ b/src/IceInstrumentation.h
@@ -41,102 +41,35 @@
public:
Instrumentation(GlobalContext *Ctx) : Ctx(Ctx) {}
- virtual void instrumentGlobals() {};
+ virtual void instrumentGlobals(VariableDeclarationList &) {}
void instrumentFunc(Cfg *Func);
private:
void instrumentInst(LoweringContext &Context);
- virtual void instrumentFuncStart(LoweringContext &Context) {
- (void) Context;
- }
- virtual void instrumentAlloca(LoweringContext &Context,
- const class InstAlloca *Instr) {
- (void) Context;
- (void) Instr;
- }
- virtual void instrumentArithmetic(LoweringContext &Context,
- const class InstArithmetic *Instr) {
- (void) Context;
- (void) Instr;
- }
- virtual void instrumentBr(LoweringContext &Context,
- const class InstBr *Instr) {
- (void) Context;
- (void) Instr;
- }
- virtual void instrumentCall(LoweringContext &Context,
- const class InstCall *Instr) {
- (void) Context;
- (void) Instr;
- }
- virtual void instrumentCast(LoweringContext &Context,
- const class InstCast *Instr) {
- (void) Context;
- (void) Instr;
- }
- virtual void instrumentExtractElement(LoweringContext &Context,
- const class InstExtractElement *Instr) {
- (void) Context;
- (void) Instr;
- }
- virtual void instrumentFcmp(LoweringContext &Context,
- const class InstFcmp *Instr) {
- (void) Context;
- (void) Instr;
- }
- virtual void instrumentIcmp(LoweringContext &Context,
- const class InstIcmp *Instr) {
- (void) Context;
- (void) Instr;
- }
- virtual void instrumentInsertElement(LoweringContext &Context,
- const class InstInsertElement *Instr) {
- (void) Context;
- (void) Instr;
- }
- virtual void instrumentIntrinsicCall(LoweringContext &Context,
- const class InstIntrinsicCall *Instr) {
- (void) Context;
- (void) Instr;
- }
- virtual void instrumentLoad(LoweringContext &Context,
- const class InstLoad *Instr) {
- (void) Context;
- (void) Instr;
- }
- virtual void instrumentPhi(LoweringContext &Context,
- const class InstPhi *Instr) {
- (void) Context;
- (void) Instr;
- }
- virtual void instrumentRet(LoweringContext &Context,
- const class InstRet *Instr) {
- (void) Context;
- (void) Instr;
- }
- virtual void instrumentSelect(LoweringContext &Context,
- const class InstSelect *Instr) {
- (void) Context;
- (void) Instr;
- }
- virtual void instrumentStore(LoweringContext &Context,
- const class InstStore *Instr) {
- (void) Context;
- (void) Instr;
- }
- virtual void instrumentSwitch(LoweringContext &Context,
- const class InstSwitch *Instr) {
- (void) Context;
- (void) Instr;
- }
- virtual void instrumentUnreachable(LoweringContext &Context,
- const class InstUnreachable *Instr) {
- (void) Context;
- (void) Instr;
- }
- virtual void instrumentLocalVars(Cfg *Func) {
- (void) Func;
- }
+ virtual void instrumentFuncStart(LoweringContext &) {}
+ virtual void instrumentAlloca(LoweringContext &, const class InstAlloca *) {}
+ virtual void instrumentArithmetic(LoweringContext &,
+ const class InstArithmetic *) {}
+ virtual void instrumentBr(LoweringContext &, const class InstBr *) {}
+ virtual void instrumentCall(LoweringContext &, const class InstCall *) {}
+ virtual void instrumentCast(LoweringContext &, const class InstCast *) {}
+ virtual void instrumentExtractElement(LoweringContext &,
+ const class InstExtractElement *) {}
+ virtual void instrumentFcmp(LoweringContext &, const class InstFcmp *) {}
+ virtual void instrumentIcmp(LoweringContext &, const class InstIcmp *) {}
+ virtual void instrumentInsertElement(LoweringContext &,
+ const class InstInsertElement *) {}
+ virtual void instrumentIntrinsicCall(LoweringContext &,
+ const class InstIntrinsicCall *) {}
+ virtual void instrumentLoad(LoweringContext &, const class InstLoad *) {}
+ virtual void instrumentPhi(LoweringContext &, const class InstPhi *) {}
+ virtual void instrumentRet(LoweringContext &, const class InstRet *) {}
+ virtual void instrumentSelect(LoweringContext &, const class InstSelect *) {}
+ virtual void instrumentStore(LoweringContext &, const class InstStore *) {}
+ virtual void instrumentSwitch(LoweringContext &, const class InstSwitch *) {}
+ virtual void instrumentUnreachable(LoweringContext &,
+ const class InstUnreachable *) {}
+ virtual void instrumentLocalVars(Cfg *) {}
protected:
GlobalContext *Ctx;
diff --git a/tests_lit/asan_tests/globalredzones.ll b/tests_lit/asan_tests/globalredzones.ll
new file mode 100644
index 0000000..a4052e7
--- /dev/null
+++ b/tests_lit/asan_tests/globalredzones.ll
@@ -0,0 +1,92 @@
+; Test of global redzone layout
+
+; REQUIRES: allow_dump
+
+; RUN: %p2i -i %s --args -threads=0 -fsanitize-address \
+; RUN: | %iflc FileCheck %s
+; RUN: %p2i -i %s --args -verbose=global_init,inst -threads=0 \
+; RUN: -fsanitize-address | %iflc FileCheck --check-prefix=DUMP %s
+
+; The array of redzones
+
+; DUMP-LABEL: ========= Instrumented Globals =========
+; DUMP: @__$rz0 = internal constant <{ i32, i32, i32, i32, i32, i32 }>
+; DUMP: <{ i32 ptrtoint ([32 x i8]* @__$rz2 to i32), i32 ptrtoint ([32 x i8]* @__$rz3 to i32),
+; DUMP: i32 ptrtoint ([32 x i8]* @__$rz4 to i32), i32 ptrtoint ([32 x i8]* @__$rz5 to i32),
+; DUMP: i32 ptrtoint ([32 x i8]* @__$rz6 to i32), i32 ptrtoint ([32 x i8]* @__$rz7 to i32) }>
+; DUMP-NEXT: @__$rz1 = internal constant [4 x i8] c"\06\00\00\00"
+
+; CHECK-LABEL: .type __$rz0,%object
+; CHECK-NEXT: .section .rodata
+; CHECK-NEXT: __$rz0:
+; CHECK-NEXT: .long __$rz2
+; CHECK-NEXT: .long __$rz3
+; CHECK-NEXT: .long __$rz4
+; CHECK-NEXT: .long __$rz5
+; CHECK-NEXT: .long __$rz6
+; CHECK-NEXT: .long __$rz7
+; CHECK-LABEL: .type __$rz1,%object
+; CHECK-NEXT: .section .rodata
+; CHECK-NEXT: __$rz1:
+; CHECK-NEXT: .byte 6
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .byte 0
+; CHECK-NEXT: .byte 0
+
+; A zero-initialized global
+@zeroInitGlobal = internal global [32 x i8] zeroinitializer
+
+; DUMP-NEXT: @__$rz2 = internal global [32 x i8] zeroinitializer
+; DUMP-NEXT: @zeroInitGlobal = internal global [32 x i8] zeroinitializer
+; DUMP-NEXT: @__$rz3 = internal global [32 x i8] zeroinitializer
+
+; CHECK-LABEL: .type __$rz2,%object
+; CHECK-NEXT: .section .bss
+; CHECK-NEXT: __$rz2:
+; CHECK-LABEL: .type zeroInitGlobal,%object
+; CHECK-NEXT: .section .bss
+; CHECK-NEXT: zeroInitGlobal:
+; CHECK-LABEL: .type __$rz3,%object
+; CHECK-NEXT: .section .bss
+; CHECK-NEXT: __$rz3:
+
+; A constant-initialized global
+@constInitGlobal = internal constant [32 x i8] c"ABCDEFGHIJKLMNOPQRSTUVWXYZ012345"
+
+; CHECK-LABEL: .type __$rz4,%object
+; CHECK-NEXT: .section .rodata
+; CHECK-NEXT: __$rz4:
+; CHECK-LABEL: .type constInitGlobal,%object
+; CHECK-NEXT: .section .rodata
+; CHECK-NEXT: constInitGlobal:
+; CHECK-LABEL: .type __$rz5,%object
+; CHECK-NEXT: .section .rodata
+; CHECK-NEXT: __$rz5:
+
+; DUMP-NEXT: @__$rz4 = internal constant [32 x i8] c"RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR"
+; DUMP-NEXT: @constInitGlobal = internal constant [32 x i8] c"ABCDEFGHIJKLMNOPQRSTUVWXYZ012345"
+; DUMP-NEXT: @__$rz5 = internal constant [32 x i8] c"RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR"
+
+; A regular global
+@regInitGlobal = internal global [32 x i8] c"ABCDEFGHIJKLMNOPQRSTUVWXYZ012345"
+
+; DUMP-NEXT: @__$rz6 = internal global [32 x i8] c"RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR"
+; DUMP-NEXT: @regInitGlobal = internal global [32 x i8] c"ABCDEFGHIJKLMNOPQRSTUVWXYZ012345"
+; DUMP-NEXT: @__$rz7 = internal global [32 x i8] c"RRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR"
+
+; CHECK-LABEL: .type __$rz6,%object
+; CHECK-NEXT: .section .data
+; CHECK-NEXT: __$rz6:
+; CHECK-LABEL: .type regInitGlobal,%object
+; CHECK-NEXT: .section .data
+; CHECK-NEXT: regInitGlobal:
+; CHECK-LABEL: .type __$rz7,%object
+; CHECK-NEXT: .section .data
+; CHECK-NEXT: __$rz7:
+
+define internal void @func() {
+ ret void
+}
+
+; DUMP-LABEL: define internal void @func() {
+; CHECK-LABEL: func:
\ No newline at end of file