Commit e3f64d09 by Karl Schimpf

Introduce model of global initializers in Subzero.

Modifies both LLVM to ICE converter, and Subzero's bitcode reader, to build Subzero's global initializers. Modifies target lowering routines for global initializers to use this new model. Also modifies both to now handle relocations in global variable initializers. BUG=None R=jvoung@chromium.org, stichnot@chromium.org Review URL: https://codereview.chromium.org/624663002
parent 8363a066
......@@ -80,6 +80,7 @@ SRCS= \
IceCfgNode.cpp \
IceConverter.cpp \
IceGlobalContext.cpp \
IceGlobalInits.cpp \
IceInst.cpp \
IceInstX8632.cpp \
IceIntrinsics.cpp \
......
......@@ -16,63 +16,75 @@
#include "test_global.h"
// Note: The following take advantage of the fact that external global
// names are not mangled with the --prefix CL argument. Hence, they
// should have the same relocation value for both llc and Subzero.
extern uint8_t *ExternName1;
extern uint8_t *ExternName2;
extern uint8_t *ExternName3;
extern uint8_t *ExternName4;
extern uint8_t *ExternName5;
// Partially initialized array
int ArrayInitPartial[10] = { 60, 70, 80, 90, 100 };
int ArrayInitFull[] = { 10, 20, 30, 40, 50 };
const int ArrayConst[] = { -10, -20, -30 };
int ArrayInitPartial[10] = {60, 70, 80, 90, 100};
int ArrayInitFull[] = {10, 20, 30, 40, 50};
const int ArrayConst[] = {-10, -20, -30};
static double ArrayDouble[10] = { 0.5, 1.5, 2.5, 3.5 };
#if 0
// TODO(kschimpf) Add this example once we know how to not mangle
// uninitialized, external globals (so that we can compare that
// the same, unmangled relocations are used). See comment in
// TargetGlobalInitX8632::lower in IceTargetLoweringX8632.cpp for
// details.
static struct {
int Array1[5];
uint8_t *Pointer1;
double Array2[3];
uint8_t *Pointer2;
struct {
uint8_t *Pointer3;
int Array1[3];
uint8_t *Pointer4;
} NestedStuff;
uint8_t *Pointer5;
} StructEx = {
{ 10, 20, 30, 40, 50 },
ExternName1,
{ 0.5, 1.5, 2.5 },
ExternName4,
{ ExternName3, {1000, 1010, 1020}, ExternName2 },
ExternName5,
};
#endif
#define ARRAY(a) \
{ (uint8_t *)(a), sizeof(a) }
// Note: By embedding the array addresses in this table, we are indirectly
// testing relocations (i.e. getArray would return the wrong address if
// relocations are broken).
struct {
uint8_t *ArrayAddress;
size_t ArraySizeInBytes;
} Arrays[] = {
ARRAY(ArrayInitPartial),
ARRAY(ArrayInitFull),
ARRAY(ArrayConst),
ARRAY(ArrayDouble),
ARRAY(ArrayInitPartial),
ARRAY(ArrayInitFull),
ARRAY(ArrayConst),
ARRAY(ArrayDouble),
{(uint8_t *)(ArrayInitPartial + 2),
sizeof(ArrayInitPartial) - 2 * sizeof(int)},
// { (uint8_t*)(&StructEx), sizeof(StructEx) },
};
size_t NumArraysElements = sizeof(Arrays) / sizeof(*Arrays);
#endif // 0
size_t getNumArrays() {
return 4;
// return NumArraysElements;
}
size_t getNumArrays() { return NumArraysElements; }
const uint8_t *getArray(size_t WhichArray, size_t &Len) {
// Using a switch statement instead of a table lookup because such a
// table is represented as a kind of initializer that Subzero
// doesn't yet support. Specifically, the table becomes constant
// aggregate data, and it contains relocations. TODO(stichnot):
// switch over to the cleaner table-based method when global
// initializers are fully implemented.
switch (WhichArray) {
default:
Len = -1;
return NULL;
case 0:
Len = sizeof(ArrayInitPartial);
return (uint8_t *)&ArrayInitPartial;
case 1:
Len = sizeof(ArrayInitFull);
return (uint8_t *)&ArrayInitFull;
case 2:
Len = sizeof(ArrayConst);
return (uint8_t *)&ArrayConst;
case 3:
Len = sizeof(ArrayDouble);
return (uint8_t *)&ArrayDouble;
}
#if 0
if (WhichArray >= NumArraysElements) {
Len = -1;
return NULL;
}
Len = Arrays[WhichArray].ArraySizeInBytes;
return Arrays[WhichArray].ArrayAddress;
#endif // 0
}
......@@ -104,6 +104,7 @@ if __name__ == '__main__':
'-mattr=' + args.attr,
'--target=' + args.target,
'--prefix=' + args.prefix,
'-allow-uninitialized-globals',
'-o=' + asm_sz,
bitcode])
shellcmd(['llvm-mc',
......
......@@ -24,18 +24,21 @@ public:
ClFlags()
: DisableInternal(false), SubzeroTimingEnabled(false),
DisableTranslation(false), DisableGlobals(false),
FunctionSections(false), UseIntegratedAssembler(false),
UseSandboxing(false), DumpStats(false), TimeEachFunction(false),
DefaultGlobalPrefix(""), DefaultFunctionPrefix(""), TimingFocusOn(""),
FunctionSections(false), DataSections(false),
UseIntegratedAssembler(false), UseSandboxing(false), DumpStats(false),
AllowUninitializedGlobals(false), TimeEachFunction(false),
DefaultGlobalPrefix(""), DefaultFunctionPrefix(""),TimingFocusOn(""),
VerboseFocusOn("") {}
bool DisableInternal;
bool SubzeroTimingEnabled;
bool DisableTranslation;
bool DisableGlobals;
bool FunctionSections;
bool DataSections;
bool UseIntegratedAssembler;
bool UseSandboxing;
bool DumpStats;
bool AllowUninitializedGlobals;
bool TimeEachFunction;
IceString DefaultGlobalPrefix;
IceString DefaultFunctionPrefix;
......
......@@ -26,6 +26,7 @@ class Converter : public Translator {
public:
Converter(llvm::Module *Mod, GlobalContext *Ctx, const Ice::ClFlags &Flags)
: Translator(Ctx, Flags), Mod(Mod) {}
/// Converts the LLVM Module to ICE. Sets exit status to false if successful,
/// true otherwise.
void convertToIce();
......@@ -34,9 +35,14 @@ private:
llvm::Module *Mod;
// Converts functions to ICE, and then machine code.
void convertFunctions();
// Converts globals to ICE, and then machine code.
void convertGlobals(llvm::Module *Mod);
Converter(const Converter &) = delete;
Converter &operator=(const Converter &) = delete;
};
}
} // end of namespace ICE.
#endif // SUBZERO_SRC_ICECONVERTER_H
//===- subzero/src/IceGlobalInits.cpp - Global initializers ---------------===//
//
// The Subzero Code Generator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements the notion of global addresses and
// initializers in Subzero.
//
//===----------------------------------------------------------------------===//
#include "llvm/ADT/STLExtras.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/Value.h"
#include "IceDefs.h"
#include "IceGlobalInits.h"
#include "IceTypes.h"
namespace {
char hexdigit(unsigned X) { return X < 10 ? '0' + X : 'A' + X - 10; }
}
namespace Ice {
GlobalAddress::~GlobalAddress() { llvm::DeleteContainerPointers(Initializers); }
void GlobalAddress::dumpType(Ostream &Stream) const {
if (Initializers.size() == 1) {
Initializers.front()->dumpType(Stream);
} else {
Stream << "<{ ";
bool IsFirst = true;
for (Initializer *Init : Initializers) {
if (IsFirst) {
IsFirst = false;
} else {
Stream << ", ";
}
Init->dumpType(Stream);
}
Stream << " }>";
}
}
void GlobalAddress::dump(Ostream &Stream) const {
Stream << "@" << getName() << " = internal "
<< (IsConstant ? "constant" : "global") << " ";
// Add initializer.
if (Initializers.size() == 1) {
Initializers.front()->dump(Stream);
} else {
dumpType(Stream);
Stream << " <{ ";
bool IsFirst = true;
for (Initializer *Init : Initializers) {
if (IsFirst) {
IsFirst = false;
} else {
Stream << ", ";
}
Init->dump(Stream);
}
Stream << " }>";
}
// Add alignment.
if (Alignment > 0)
Stream << ", align " << Alignment;
Stream << "\n";
}
void GlobalAddress::Initializer::dumpType(Ostream &Stream) const {
Stream << "[" << getNumBytes() << " x " << Ice::IceType_i8 << "]";
}
void GlobalAddress::DataInitializer::dump(Ostream &Stream) const {
dumpType(Stream);
Stream << " c\"";
// Code taken from PrintEscapedString() in AsmWriter.cpp. Keep
// the strings in the same format as the .ll file for practical
// diffing.
for (uint8_t C : Contents) {
if (isprint(C) && C != '\\' && C != '"')
Stream << C;
else
Stream << '\\' << hexdigit(C >> 4) << hexdigit(C & 0x0F);
}
Stream << "\"";
}
void GlobalAddress::ZeroInitializer::dump(Ostream &Stream) const {
dumpType(Stream);
Stream << " zeroinitializer";
}
IceString GlobalAddress::RelocInitializer::getName() const {
switch (Address.getKind()) {
case FunctionRelocation:
return Address.getFunction()->getName();
case GlobalAddressRelocation:
return Address.getGlobalAddr()->getName();
default:
llvm::report_fatal_error("Malformed relocation address!");
}
}
void GlobalAddress::RelocInitializer::dumpType(Ostream &Stream) const {
Stream << Ice::IceType_i32;
}
void GlobalAddress::RelocInitializer::dump(Ostream &Stream) const {
if (Offset != 0) {
dumpType(Stream);
Stream << " add (";
}
dumpType(Stream);
Stream << " ptrtoint (";
if (Address.getKind() == FunctionRelocation) {
Stream << *Address.getFunction()->getType() << " @"
<< Address.getFunction()->getName();
} else {
Address.getGlobalAddr()->dumpType(Stream);
Stream << "* @" << Address.getGlobalAddr()->getName();
}
Stream << " to ";
dumpType(Stream);
Stream << ")";
if (Offset != 0) {
Stream << ", ";
dumpType(Stream);
Stream << " " << Offset << ")";
}
}
}
//===- subzero/src/IceGlobalInits.h - Global initializers -------*- C++ -*-===//
//
// The Subzero Code Generator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file declares the representation of global addresses and
// initializers in Subzero. Global initializers are represented as a
// sequence of simple initializers.
//
//===----------------------------------------------------------------------===//
#ifndef SUBZERO_SRC_ICEGLOBALINITS_H
#define SUBZERO_SRC_ICEGLOBALINITS_H
#include "IceDefs.h"
namespace llvm {
// TODO(kschimpf): Remove this dependency on LLVM IR.
class Value;
}
namespace Ice {
/// Models a global address, and its initializers.
class GlobalAddress {
GlobalAddress(const GlobalAddress &) = delete;
GlobalAddress &operator=(const GlobalAddress &) = delete;
public:
/// Base class for a global variable initializer.
class Initializer {
Initializer(const Initializer &) = delete;
Initializer &operator=(const Initializer &) = delete;
public:
/// Discriminator for LLVM-style RTTI.
enum InitializerKind {
DataInitializerKind,
ZeroInitializerKind,
RelocInitializerKind
};
InitializerKind getKind() const { return Kind; }
virtual ~Initializer() {}
virtual SizeT getNumBytes() const = 0;
virtual void dump(Ostream &Stream) const = 0;
virtual void dumpType(Ostream &Stream) const;
protected:
explicit Initializer(InitializerKind Kind) : Kind(Kind) {}
private:
const InitializerKind Kind;
};
// Models the data in a data initializer.
typedef std::vector<uint8_t> DataVecType;
/// Defines a sequence of byte values as a data initializer.
class DataInitializer : public Initializer {
DataInitializer(const DataInitializer &) = delete;
DataInitializer &operator=(const DataInitializer &) = delete;
public:
template <class IntContainer>
DataInitializer(const IntContainer &Values)
: Initializer(DataInitializerKind), Contents(Values.size()) {
size_t i = 0;
for (auto &V : Values) {
Contents[i] = static_cast<uint8_t>(V);
++i;
}
}
DataInitializer(const char *Str, size_t StrLen)
: Initializer(DataInitializerKind), Contents(StrLen) {
for (size_t i = 0; i < StrLen; ++i)
Contents[i] = static_cast<uint8_t>(Str[i]);
}
~DataInitializer() override {}
const DataVecType &getContents() const { return Contents; }
SizeT getNumBytes() const override { return Contents.size(); }
void dump(Ostream &Stream) const override;
static bool classof(const Initializer *D) {
return D->getKind() == DataInitializerKind;
}
private:
// The byte contents of the data initializer.
DataVecType Contents;
};
/// Defines a sequence of bytes initialized to zero.
class ZeroInitializer : public Initializer {
ZeroInitializer(const ZeroInitializer &) = delete;
ZeroInitializer &operator=(const ZeroInitializer &) = delete;
public:
explicit ZeroInitializer(SizeT Size)
: Initializer(ZeroInitializerKind), Size(Size) {}
~ZeroInitializer() override {}
SizeT getNumBytes() const override { return Size; }
void dump(Ostream &Stream) const override;
static bool classof(const Initializer *Z) {
return Z->getKind() == ZeroInitializerKind;
}
private:
// The number of bytes to be zero initialized.
SizeT Size;
};
/// Defines the kind of relocation addresses allowed.
enum RelocationKind { FunctionRelocation, GlobalAddressRelocation };
/// Defines a relocation address (i.e. reference to a function
/// or global variable address).
class RelocationAddress {
RelocationAddress &operator=(const RelocationAddress &) = delete;
public:
explicit RelocationAddress(const RelocationAddress &Addr)
: Kind(Addr.Kind) {
switch (Kind) {
case FunctionRelocation:
Address.Function = Addr.Address.Function;
break;
case GlobalAddressRelocation:
Address.GlobalAddr = Addr.Address.GlobalAddr;
}
}
explicit RelocationAddress(llvm::Value *Function)
: Kind(FunctionRelocation) {
Address.Function = Function;
}
explicit RelocationAddress(GlobalAddress *GlobalAddr)
: Kind(GlobalAddressRelocation) {
Address.GlobalAddr = GlobalAddr;
}
RelocationKind getKind() const { return Kind; }
llvm::Value *getFunction() const {
assert(Kind == FunctionRelocation);
return Address.Function;
}
GlobalAddress *getGlobalAddr() const {
assert(Kind == GlobalAddressRelocation);
return Address.GlobalAddr;
}
private:
const RelocationKind Kind;
union {
// TODO(kschimpf) Integrate Functions into ICE model.
llvm::Value *Function;
GlobalAddress *GlobalAddr;
} Address;
};
// Relocation address offsets must be 32 bit values.
typedef int32_t RelocOffsetType;
static const SizeT RelocAddrSize = 4;
/// Defines the relocation value of another address.
class RelocInitializer : public Initializer {
RelocInitializer(const RelocInitializer &) = delete;
RelocInitializer &operator=(const RelocInitializer &) = delete;
public:
RelocInitializer(const RelocationAddress &Address, RelocOffsetType Offset)
: Initializer(RelocInitializerKind), Address(Address), Offset(Offset) {}
~RelocInitializer() override {}
RelocOffsetType getOffset() const { return Offset; }
IceString getName() const;
SizeT getNumBytes() const override { return RelocAddrSize; }
void dump(Ostream &Stream) const override;
virtual void dumpType(Ostream &Stream) const;
static bool classof(const Initializer *R) {
return R->getKind() == RelocInitializerKind;
}
private:
// The global address used in the relocation.
const RelocationAddress Address;
// The offset to add to the relocation.
const RelocOffsetType Offset;
};
/// Models the list of initializers.
typedef std::vector<Initializer *> InitializerListType;
GlobalAddress() : Alignment(0), IsConstant(false), IsInternal(true) {}
~GlobalAddress();
const InitializerListType &getInitializers() const { return Initializers; }
bool hasName() const { return !Name.empty(); }
const IceString &getName() const { return Name; }
void setName(const IceString &NewName) { Name = NewName; }
bool getIsConstant() const { return IsConstant; }
void setIsConstant(bool NewValue) { IsConstant = NewValue; }
uint32_t getAlignment() const { return Alignment; }
void setAlignment(uint32_t NewAlignment) { Alignment = NewAlignment; }
bool getIsInternal() const { return IsInternal; }
void setIsInternal(bool NewValue) { IsInternal = NewValue; }
/// Returns the number of bytes for the initializer of the global
/// address.
SizeT getNumBytes() const {
SizeT Count = 0;
for (Initializer *Init : Initializers) {
Count += Init->getNumBytes();
}
return Count;
}
/// Adds Initializer to the list of initializers. Takes ownership of
/// the initializer.
void addInitializer(Initializer *Initializer) {
Initializers.push_back(Initializer);
}
/// Prints out type for initializer associated with the global address
/// to Stream.
void dumpType(Ostream &Stream) const;
/// Prints out the definition of the global address (including
/// initialization).
void dump(Ostream &Stream) const;
private:
// list of initializers associated with the global address.
InitializerListType Initializers;
// The name for the global.
IceString Name;
// The alignment of the initializer.
uint32_t Alignment;
// True if a constant initializer.
bool IsConstant;
// True if the address is internal.
bool IsInternal;
};
template <class StreamType>
inline StreamType &operator<<(StreamType &Stream,
const GlobalAddress::Initializer &Init) {
Init.dump(Stream);
return Stream;
}
template <class StreamType>
inline StreamType &operator<<(StreamType &Stream, const GlobalAddress &Addr) {
Addr.dump(Stream);
return Stream;
}
} // end of namespace Ice
#endif // SUBZERO_SRC_ICEGLOBALINITS_H
......@@ -27,6 +27,7 @@ namespace Ice {
typedef uint8_t AsmCodeByte;
class Assembler;
class GlobalAddress;
// LoweringContext makes it easy to iterate through non-deleted
// instructions in a node, and insert new (lowered) instructions at
......@@ -248,10 +249,7 @@ public:
GlobalContext *Ctx);
virtual ~TargetGlobalInitLowering();
// TODO: Allow relocations to be represented as part of the Data.
virtual void lower(const IceString &Name, SizeT Align, bool IsInternal,
bool IsConst, bool IsZeroInitializer, SizeT Size,
const char *Data, bool DisableTranslation) = 0;
virtual void lower(const GlobalAddress &Addr, bool DisableTranslation) = 0;
protected:
TargetGlobalInitLowering(GlobalContext *Ctx) : Ctx(Ctx) {}
......
......@@ -23,6 +23,7 @@
#include "IceCfgNode.h"
#include "IceClFlags.h"
#include "IceDefs.h"
#include "IceGlobalInits.h"
#include "IceInstX8632.h"
#include "IceOperand.h"
#include "IceRegistersX8632.h"
......@@ -4428,38 +4429,10 @@ void ConstantUndef::emit(GlobalContext *) const {
TargetGlobalInitX8632::TargetGlobalInitX8632(GlobalContext *Ctx)
: TargetGlobalInitLowering(Ctx) {}
namespace {
char hexdigit(unsigned X) { return X < 10 ? '0' + X : 'A' + X - 10; }
}
void TargetGlobalInitX8632::lower(const IceString &Name, SizeT Align,
bool IsInternal, bool IsConst,
bool IsZeroInitializer, SizeT Size,
const char *Data, bool DisableTranslation) {
void TargetGlobalInitX8632::lower(const GlobalAddress &Global,
bool DisableTranslation) {
if (Ctx->isVerbose()) {
// TODO: Consider moving the dump output into the driver to be
// reused for all targets.
Ostream &Str = Ctx->getStrDump();
Str << "@" << Name << " = " << (IsInternal ? "internal" : "external");
Str << (IsConst ? " constant" : " global");
Str << " [" << Size << " x i8] ";
if (IsZeroInitializer) {
Str << "zeroinitializer";
} else {
Str << "c\"";
// Code taken from PrintEscapedString() in AsmWriter.cpp. Keep
// the strings in the same format as the .ll file for practical
// diffing.
for (uint64_t i = 0; i < Size; ++i) {
unsigned char C = Data[i];
if (isprint(C) && C != '\\' && C != '"')
Str << C;
else
Str << '\\' << hexdigit(C >> 4) << hexdigit(C & 0x0F);
}
Str << "\"";
}
Str << ", align " << Align << "\n";
Global.dump(Ctx->getStrDump());
}
if (DisableTranslation)
......@@ -4489,35 +4462,86 @@ void TargetGlobalInitX8632::lower(const IceString &Name, SizeT Align,
// .local NAME
// .comm NAME, SIZE, ALIGN
IceString MangledName = Ctx->mangleName(Name);
// TODO(kschimpf): Don't mangle name if external and uninitialized. This
// will allow us to cross test relocations for references to external
// global variables.
IceString MangledName = Ctx->mangleName(Global.getName());
// Start a new section.
if (IsConst) {
if (Ctx->getFlags().DataSections) {
Str << "\t.section\t.rodata." << MangledName << ",\"a\",@progbits\n";
} else if (Global.getIsConstant()) {
Str << "\t.section\t.rodata,\"a\",@progbits\n";
} else {
Str << "\t.type\t" << MangledName << ",@object\n";
Str << "\t.data\n";
}
Str << "\t" << (IsInternal ? ".local" : ".global") << "\t" << MangledName
<< "\n";
if (IsZeroInitializer) {
if (IsConst) {
Str << "\t.align\t" << Align << "\n";
Str << MangledName << ":\n";
Str << "\t.zero\t" << Size << "\n";
Str << "\t.size\t" << MangledName << ", " << Size << "\n";
} else {
// TODO(stichnot): Put the appropriate non-constant
// zeroinitializers in a .bss section to reduce object size.
Str << "\t.comm\t" << MangledName << ", " << Size << ", " << Align
<< "\n";
Str << "\t" << (Global.getIsInternal() ? ".local" : ".global") << "\t"
<< MangledName << "\n";
const GlobalAddress::InitializerListType &Initializers =
Global.getInitializers();
// Note: Handle zero initializations specially when lowering, since
// we may be able to reduce object size.
GlobalAddress::ZeroInitializer *SimpleZeroInit = nullptr;
if (Initializers.size() == 1) {
GlobalAddress::Initializer *Init = Initializers[0];
if (const auto ZeroInit =
llvm::dyn_cast<GlobalAddress::ZeroInitializer>(Init)) {
SimpleZeroInit = ZeroInit;
}
}
if (SimpleZeroInit && !Global.getIsConstant()) {
// TODO(stichnot): Put the appropriate non-constant
// zeroinitializers in a .bss section to reduce object size.
Str << "\t.comm\t" << MangledName << ", " << Global.getNumBytes() << ", "
<< Global.getAlignment() << "\n";
// }
} else {
Str << "\t.align\t" << Align << "\n";
Str << "\t.align\t" << Global.getAlignment() << "\n";
Str << MangledName << ":\n";
for (SizeT i = 0; i < Size; ++i) {
Str << "\t.byte\t" << (((unsigned)Data[i]) & 0xff) << "\n";
for (GlobalAddress::Initializer *Init : Initializers) {
switch (Init->getKind()) {
case GlobalAddress::Initializer::DataInitializerKind: {
const auto Data =
llvm::cast<GlobalAddress::DataInitializer>(Init)->getContents();
for (SizeT i = 0; i < Init->getNumBytes(); ++i) {
Str << "\t.byte\t" << (((unsigned)Data[i]) & 0xff) << "\n";
}
break;
}
case GlobalAddress::Initializer::ZeroInitializerKind:
Str << "\t.zero\t" << Init->getNumBytes() << "\n";
break;
case GlobalAddress::Initializer::RelocInitializerKind: {
const auto Reloc = llvm::cast<GlobalAddress::RelocInitializer>(Init);
Str << "\t.long\t";
// TODO(kschimpf): Once the representation of a relocation has
// been modified to reference the corresponding global
// address, modify to not mangle the name if the global is
// external and uninitialized. This will allow us to better
// test cross test relocations.
Str << Ctx->mangleName(Reloc->getName());
if (GlobalAddress::RelocOffsetType Offset = Reloc->getOffset()) {
Str << " + " << Offset;
}
Str << "\n";
break;
}
default: {
std::string Buffer;
llvm::raw_string_ostream StrBuf(Buffer);
StrBuf << "Unable to lower initializer: ";
Init->dump(StrBuf);
llvm::report_fatal_error(StrBuf.str());
break;
}
}
}
Str << "\t.size\t" << MangledName << ", " << Size << "\n";
Str << "\t.size\t" << MangledName << ", " << Global.getNumBytes() << "\n";
}
}
......
......@@ -486,9 +486,9 @@ public:
static TargetGlobalInitLowering *create(GlobalContext *Ctx) {
return new TargetGlobalInitX8632(Ctx);
}
void lower(const IceString &Name, SizeT Align, bool IsInternal, bool IsConst,
bool IsZeroInitializer, SizeT Size, const char *Data,
bool DisableTranslation) override;
virtual void lower(const GlobalAddress &Addr,
bool DisableTranslation) override;
protected:
TargetGlobalInitX8632(GlobalContext *Ctx);
......
......@@ -29,44 +29,61 @@ using namespace Ice;
Translator::~Translator() {}
namespace {
void setValueName(llvm::Value *V, const char *Kind, const IceString &Prefix,
uint32_t &NameIndex, Ostream &errs) {
if (V->hasName()) {
const std::string &Name(V->getName());
if (Name.find(Prefix) == 0) {
errs << "Warning: Default " << Kind << " prefix '" << Prefix
<< "' conflicts with name '" << Name << "'.\n";
}
return;
}
if (NameIndex == 0) {
V->setName(Prefix);
++NameIndex;
return;
}
IceString Translator::createUnnamedName(const IceString &Prefix, SizeT Index) {
if (Index == 0)
return Prefix;
std::string Buffer;
llvm::raw_string_ostream StrBuf(Buffer);
StrBuf << Prefix << NameIndex;
V->setName(StrBuf.str());
++NameIndex;
StrBuf << Prefix << Index;
return StrBuf.str();
}
bool Translator::checkIfUnnamedNameSafe(const IceString &Name, const char *Kind,
const IceString &Prefix,
Ostream &Stream) {
if (Name.find(Prefix) == 0) {
for (size_t i = Prefix.size(); i < Name.size(); ++i) {
if (!isdigit(Name[i])) {
return false;
}
}
Stream << "Warning : Default " << Kind << " prefix '" << Prefix
<< "' potentially conflicts with name '" << Name << "'.\n";
return true;
}
return false;
}
} // end of anonymous namespace
void Translator::nameUnnamedGlobalAddresses(llvm::Module *Mod) {
const IceString &GlobalPrefix = Flags.DefaultGlobalPrefix;
if (GlobalPrefix.empty())
return;
uint32_t NameIndex = 0;
Ostream &errs = Ctx->getStrDump();
if (!GlobalPrefix.empty()) {
uint32_t NameIndex = 0;
for (auto I = Mod->global_begin(), E = Mod->global_end(); I != E; ++I)
setValueName(I, "global", GlobalPrefix, NameIndex, errs);
for (auto V = Mod->global_begin(), E = Mod->global_end(); V != E; ++V) {
if (!V->hasName()) {
V->setName(createUnnamedName(GlobalPrefix, NameIndex));
++NameIndex;
} else {
checkIfUnnamedNameSafe(V->getName(), "global", GlobalPrefix, errs);
}
}
}
void Translator::nameUnnamedFunctions(llvm::Module *Mod) {
const IceString &FunctionPrefix = Flags.DefaultFunctionPrefix;
if (FunctionPrefix.empty())
return;
uint32_t NameIndex = 0;
for (llvm::Function &I : *Mod)
setValueName(&I, "function", FunctionPrefix, NameIndex, errs);
Ostream &errs = Ctx->getStrDump();
for (llvm::Function &F : *Mod) {
if (!F.hasName()) {
F.setName(createUnnamedName(FunctionPrefix, NameIndex));
++NameIndex;
} else {
checkIfUnnamedNameSafe(F.getName(), "function", FunctionPrefix, errs);
}
}
}
void Translator::translateFcn(Cfg *Fcn) {
......@@ -93,46 +110,12 @@ void Translator::emitConstants() {
Func->getTarget()->emitConstants();
}
void Translator::convertGlobals(llvm::Module *Mod) {
std::unique_ptr<TargetGlobalInitLowering> GlobalLowering(
TargetGlobalInitLowering::createLowering(Ctx->getTargetArch(), Ctx));
for (auto I = Mod->global_begin(), E = Mod->global_end(); I != E; ++I) {
if (!I->hasInitializer())
continue;
const llvm::Constant *Initializer = I->getInitializer();
IceString Name = I->getName();
unsigned Align = I->getAlignment();
uint64_t NumElements = 0;
const char *Data = NULL;
bool IsInternal = I->hasInternalLinkage();
bool IsConst = I->isConstant();
bool IsZeroInitializer = false;
if (const llvm::ConstantDataArray *CDA =
llvm::dyn_cast<llvm::ConstantDataArray>(Initializer)) {
NumElements = CDA->getNumElements();
assert(llvm::isa<llvm::IntegerType>(CDA->getElementType()) &&
(llvm::cast<llvm::IntegerType>(CDA->getElementType())
->getBitWidth() == 8));
Data = CDA->getRawDataValues().data();
} else if (llvm::isa<llvm::ConstantAggregateZero>(Initializer)) {
if (const llvm::ArrayType *AT =
llvm::dyn_cast<llvm::ArrayType>(Initializer->getType())) {
assert(llvm::isa<llvm::IntegerType>(AT->getElementType()) &&
(llvm::cast<llvm::IntegerType>(AT->getElementType())
->getBitWidth() == 8));
NumElements = AT->getNumElements();
IsZeroInitializer = true;
} else {
llvm_unreachable("Unhandled constant aggregate zero type");
}
} else {
llvm_unreachable("Unhandled global initializer");
}
GlobalLowering->lower(Name, Align, IsInternal, IsConst, IsZeroInitializer,
NumElements, Data,
Ctx->getFlags().DisableTranslation);
void Translator::lowerGlobals(const GlobalAddressList &GlobalAddresses) {
llvm::OwningPtr<Ice::TargetGlobalInitLowering> GlobalLowering(
Ice::TargetGlobalInitLowering::createLowering(Ctx->getTargetArch(), Ctx));
bool DisableTranslation = Ctx->getFlags().DisableTranslation;
for (const Ice::GlobalAddress *Addr : GlobalAddresses) {
GlobalLowering->lower(*Addr, DisableTranslation);
}
GlobalLowering.reset();
}
......@@ -25,6 +25,7 @@ namespace Ice {
class ClFlags;
class Cfg;
class GlobalAddress;
class GlobalContext;
// Base class for translating ICE to machine code.
......@@ -33,6 +34,8 @@ class GlobalContext;
// machine instructions.
class Translator {
public:
typedef std::vector<Ice::GlobalAddress *> GlobalAddressList;
Translator(GlobalContext *Ctx, const ClFlags &Flags)
: Ctx(Ctx), Flags(Flags), ErrorStatus(0) {}
......@@ -51,15 +54,26 @@ public:
/// Emits the constant pool.
void emitConstants();
// Walks module and generates names for unnamed globals and
// functions using prefix getFlags().DefaultGlobalPrefix, if the
// prefix is non-empty.
/// Lowers the given list of global addresses to target.
void lowerGlobals(const GlobalAddressList &GlobalAddresses);
/// Creates a name using the given prefix and corresponding index.
std::string createUnnamedName(const IceString &Prefix, SizeT Index);
/// Reports if there is a (potential) conflict between Name, and using
/// Prefix to name unnamed names. Errors are put on Ostream.
/// Returns true if there isn't a potential conflict.
bool checkIfUnnamedNameSafe(const IceString &Name, const char *Kind,
const IceString &Prefix, Ostream &Stream);
// Walks module and generates names for unnamed globals using prefix
// getFlags().DefaultGlobalPrefix, if the prefix is non-empty.
void nameUnnamedGlobalAddresses(llvm::Module *Mod);
// Converts globals to ICE, and then machine code.
// TODO(kschimpf) Remove this once we have ported to PNaClTranslator,
// and PNaClTranslator generates initializers while parsing.
void convertGlobals(llvm::Module *Mod);
// Walks module and generates names for unnamed functions using
// prefix getFlags().DefaultFunctionPrefix, if the prefix is
// non-empty.
void nameUnnamedFunctions(llvm::Module *Mod);
protected:
GlobalContext *Ctx;
......
......@@ -19,25 +19,25 @@
namespace Ice {
TypeConverter::TypeConverter(llvm::LLVMContext &Context) {
AddLLVMType(IceType_void, llvm::Type::getVoidTy(Context));
AddLLVMType(IceType_i1, llvm::IntegerType::get(Context, 1));
AddLLVMType(IceType_i8, llvm::IntegerType::get(Context, 8));
AddLLVMType(IceType_i16, llvm::IntegerType::get(Context, 16));
AddLLVMType(IceType_i32, llvm::IntegerType::get(Context, 32));
AddLLVMType(IceType_i64, llvm::IntegerType::get(Context, 64));
AddLLVMType(IceType_f32, llvm::Type::getFloatTy(Context));
AddLLVMType(IceType_f64, llvm::Type::getDoubleTy(Context));
AddLLVMType(IceType_v4i1, llvm::VectorType::get(LLVMTypes[IceType_i1], 4));
AddLLVMType(IceType_v8i1, llvm::VectorType::get(LLVMTypes[IceType_i1], 8));
AddLLVMType(IceType_v16i1, llvm::VectorType::get(LLVMTypes[IceType_i1], 16));
AddLLVMType(IceType_v16i8, llvm::VectorType::get(LLVMTypes[IceType_i8], 16));
AddLLVMType(IceType_v8i16, llvm::VectorType::get(LLVMTypes[IceType_i16], 8));
AddLLVMType(IceType_v4i32, llvm::VectorType::get(LLVMTypes[IceType_i32], 4));
AddLLVMType(IceType_v4f32, llvm::VectorType::get(LLVMTypes[IceType_f32], 4));
addLLVMType(IceType_void, llvm::Type::getVoidTy(Context));
addLLVMType(IceType_i1, llvm::IntegerType::get(Context, 1));
addLLVMType(IceType_i8, llvm::IntegerType::get(Context, 8));
addLLVMType(IceType_i16, llvm::IntegerType::get(Context, 16));
addLLVMType(IceType_i32, llvm::IntegerType::get(Context, 32));
addLLVMType(IceType_i64, llvm::IntegerType::get(Context, 64));
addLLVMType(IceType_f32, llvm::Type::getFloatTy(Context));
addLLVMType(IceType_f64, llvm::Type::getDoubleTy(Context));
addLLVMType(IceType_v4i1, llvm::VectorType::get(LLVMTypes[IceType_i1], 4));
addLLVMType(IceType_v8i1, llvm::VectorType::get(LLVMTypes[IceType_i1], 8));
addLLVMType(IceType_v16i1, llvm::VectorType::get(LLVMTypes[IceType_i1], 16));
addLLVMType(IceType_v16i8, llvm::VectorType::get(LLVMTypes[IceType_i8], 16));
addLLVMType(IceType_v8i16, llvm::VectorType::get(LLVMTypes[IceType_i16], 8));
addLLVMType(IceType_v4i32, llvm::VectorType::get(LLVMTypes[IceType_i32], 4));
addLLVMType(IceType_v4f32, llvm::VectorType::get(LLVMTypes[IceType_f32], 4));
assert(LLVMTypes.size() == static_cast<size_t>(IceType_NUM));
}
void TypeConverter::AddLLVMType(Type Ty, llvm::Type *LLVMTy) {
void TypeConverter::addLLVMType(Type Ty, llvm::Type *LLVMTy) {
assert(static_cast<size_t>(Ty) == LLVMTypes.size());
LLVMTypes.push_back(LLVMTy);
LLVM2IceMap[LLVMTy] = Ty;
......
......@@ -68,7 +68,7 @@ private:
std::map<llvm::Type *, Type> LLVM2IceMap;
// Add LLVM/ICE pair to internal tables.
void AddLLVMType(Type Ty, llvm::Type *LLVMTy);
void addLLVMType(Type Ty, llvm::Type *LLVMTy);
// Converts types not in LLVM2IceMap.
Type convertToIceTypeOther(llvm::Type *LLVMTy) const;
......
......@@ -65,6 +65,9 @@ static cl::opt<bool> UseSandboxing("sandbox", cl::desc("Use sandboxing"));
static cl::opt<bool>
FunctionSections("ffunction-sections",
cl::desc("Emit functions into separate sections"));
static cl::opt<bool>
DataSections("fdata-sections",
cl::desc("Emit (global) data into separate sections"));
static cl::opt<Ice::OptLevel>
OptLevel(cl::desc("Optimization level"), cl::init(Ice::Opt_m1),
cl::value_desc("level"),
......@@ -121,6 +124,11 @@ static cl::opt<bool>
DumpStats("stats",
cl::desc("Print statistics after translating each function"));
// This is currently needed by crosstest.py.
static cl::opt<bool> AllowUninitializedGlobals(
"allow-uninitialized-globals",
cl::desc("Allow global variables to be uninitialized"));
static cl::opt<NaClFileFormat> InputFileFormat(
"bitcode-format", cl::desc("Define format of input file:"),
cl::values(clEnumValN(LLVMFormat, "llvm", "LLVM file (default)"),
......@@ -149,9 +157,20 @@ static cl::opt<bool>
UseIntegratedAssembler("integrated-as",
cl::desc("Use integrated assembler (default yes)"),
cl::init(true));
static cl::alias UseIas("ias", cl::desc("Alias for -integrated-as"),
cl::NotHidden, cl::aliasopt(UseIntegratedAssembler));
static cl::opt<bool> AlwaysExitSuccess(
"exit-success", cl::desc("Exit with success status, even if errors found"),
cl::init(false));
static int GetReturnValue(int Val) {
if (AlwaysExitSuccess)
return 0;
return Val;
}
int main(int argc, char **argv) {
cl::ParseCommandLineOptions(argc, argv);
......@@ -180,9 +199,11 @@ int main(int argc, char **argv) {
Flags.DisableTranslation = DisableTranslation;
Flags.DisableGlobals = DisableGlobals;
Flags.FunctionSections = FunctionSections;
Flags.DataSections = DataSections;
Flags.UseIntegratedAssembler = UseIntegratedAssembler;
Flags.UseSandboxing = UseSandboxing;
Flags.DumpStats = DumpStats;
Flags.AllowUninitializedGlobals = AllowUninitializedGlobals;
Flags.TimeEachFunction = TimeEachFunction;
Flags.DefaultGlobalPrefix = DefaultGlobalPrefix;
Flags.DefaultFunctionPrefix = DefaultFunctionPrefix;
......@@ -207,7 +228,7 @@ int main(int argc, char **argv) {
if (!Mod) {
Err.print(argv[0], errs());
return 1;
return GetReturnValue(1);
}
Ice::Converter Converter(Mod, &Ctx, Flags);
......@@ -222,5 +243,5 @@ int main(int argc, char **argv) {
Ctx.dumpTimers();
const bool FinalStats = true;
Ctx.dumpStats("_FINAL_", FinalStats);
return ErrorStatus;
return GetReturnValue(ErrorStatus);
}
......@@ -5,8 +5,8 @@
; RUN: %lc2i -i %s --args --verbose inst | FileCheck %s
; RUN: %lc2i -i %s --args --verbose none | FileCheck --check-prefix=ERRORS %s
; Note: PNaCl ABI Doesn't allow external globals. Hence, not tested.
@intern_global = internal global [4 x i8] c"\00\00\00\0C", align 4
@extern_global = external global [4 x i8]
define i32 @test_intern_global() {
; CHECK: define i32 @test_intern_global
......@@ -16,12 +16,4 @@ entry:
ret i32 %v0
}
define i32 @test_extern_global() {
; CHECK: define i32 @test_extern_global
entry:
%__1 = bitcast [4 x i8]* @extern_global to i32*
%v0 = load i32* %__1, align 1
ret i32 %v0
}
; ERRORS-NOT: ICE translation error
; Tests that we name unnamed global addresses.
; RUN: %p2i -i %s --insts | FileCheck %s
; RUN: %p2i -i %s --insts --args -default-function-prefix=h \
; RUN: -default-global-prefix=g | FileCheck --check-prefix=BAD %s
@0 = internal global [4 x i8] zeroinitializer, align 4
; CHECK: @Global = internal global [4 x i8] zeroinitializer, align 4
@1 = internal constant [10 x i8] c"Some stuff", align 1
; CHECK-NEXT: @Global1 = internal constant [10 x i8] c"Some stuff", align 1
@g = internal global [4 x i8] zeroinitializer, align 4
; BAD: Warning: Default global prefix 'g' conflicts with name 'g'.
; CHECK-NEXT: @g = internal global [4 x i8] zeroinitializer, align 4
define i32 @2(i32 %v) {
ret i32 %v
}
; CHECK-NEXT: define i32 @Function(i32 %v) {
; CHECK-NEXT: __0:
; CHECK-NEXT: ret i32 %v
; CHECK-NEXT: }
define void @hg() {
ret void
}
; BAD: Warning: Default function prefix 'h' conflicts with name 'hg'.
; CHECK-NEXT: define void @hg() {
; CHECK-NEXT: __0:
; CHECK-NEXT: ret void
; CHECK-NEXT: }
define void @3() {
ret void
}
; CHECK-NEXT: define void @Function1() {
; CHECK-NEXT: __0:
; CHECK-NEXT: ret void
; CHECK-NEXT: }
; Tests if we handle global variables with relocation initializers.
; Test that we handle it in the ICE converter.
; RUN: %lc2i -i %s --insts | FileCheck %s
; Test that we handle it using Subzero's bitcode reader.
; RUN: %p2i -i %s --insts | FileCheck %s
@bytes = internal global [7 x i8] c"abcdefg"
; CHECK: @bytes = internal global [7 x i8] c"abcdefg"
@const_bytes = internal constant [7 x i8] c"abcdefg"
; CHECK-NEXT: @const_bytes = internal constant [7 x i8] c"abcdefg"
@ptr_to_ptr = internal global i32 ptrtoint (i32* @ptr to i32)
; CHECK-NEXT: @ptr_to_ptr = internal global i32 ptrtoint (i32* @ptr to i32)
@const_ptr_to_ptr = internal constant i32 ptrtoint (i32* @ptr to i32)
; CHECK-NEXT: @const_ptr_to_ptr = internal constant i32 ptrtoint (i32* @ptr to i32)
@ptr_to_func = internal global i32 ptrtoint (void ()* @func to i32)
; CHECK-NEXT: @ptr_to_func = internal global i32 ptrtoint (void ()* @func to i32)
@const_ptr_to_func = internal constant i32 ptrtoint (void ()* @func to i32)
; CHECK-NEXT: @const_ptr_to_func = internal constant i32 ptrtoint (void ()* @func to i32)
@compound = internal global <{ [3 x i8], i32 }> <{ [3 x i8] c"foo", i32 ptrtoint (void ()* @func to i32) }>
; CHECK-NEXT: @compound = internal global <{ [3 x i8], i32 }> <{ [3 x i8] c"foo", i32 ptrtoint (void ()* @func to i32) }>
@const_compound = internal constant <{ [3 x i8], i32 }> <{ [3 x i8] c"foo", i32 ptrtoint (void ()* @func to i32) }>
; CHECK-NEXT: @const_compound = internal constant <{ [3 x i8], i32 }> <{ [3 x i8] c"foo", i32 ptrtoint (void ()* @func to i32) }>
@ptr = internal global i32 ptrtoint ([7 x i8]* @bytes to i32)
; CHECK-NEXT: @ptr = internal global i32 ptrtoint ([7 x i8]* @bytes to i32)
@const_ptr = internal constant i32 ptrtoint ([7 x i8]* @bytes to i32)
; CHECK-NEXT: @const_ptr = internal constant i32 ptrtoint ([7 x i8]* @bytes to i32)
@addend_ptr = internal global i32 add (i32 ptrtoint (i32* @ptr to i32), i32 1)
; CHECK-NEXT: @addend_ptr = internal global i32 add (i32 ptrtoint (i32* @ptr to i32), i32 1)
@const_addend_ptr = internal constant i32 add (i32 ptrtoint (i32* @ptr to i32), i32 1)
; CHECK-NEXT: @const_addend_ptr = internal constant i32 add (i32 ptrtoint (i32* @ptr to i32), i32 1)
@addend_negative = internal global i32 add (i32 ptrtoint (i32* @ptr to i32), i32 -1)
; CHECK-NEXT: @addend_negative = internal global i32 add (i32 ptrtoint (i32* @ptr to i32), i32 -1)
@const_addend_negative = internal constant i32 add (i32 ptrtoint (i32* @ptr to i32), i32 -1)
; CHECK-NEXT: @const_addend_negative = internal constant i32 add (i32 ptrtoint (i32* @ptr to i32), i32 -1)
@addend_array1 = internal global i32 add (i32 ptrtoint ([7 x i8]* @bytes to i32), i32 1)
; CHECK-NEXT: @addend_array1 = internal global i32 add (i32 ptrtoint ([7 x i8]* @bytes to i32), i32 1)
@const_addend_array1 = internal constant i32 add (i32 ptrtoint ([7 x i8]* @bytes to i32), i32 1)
; CHECK-NEXT: @const_addend_array1 = internal constant i32 add (i32 ptrtoint ([7 x i8]* @bytes to i32), i32 1)
@addend_array2 = internal global i32 add (i32 ptrtoint ([7 x i8]* @bytes to i32), i32 7)
; CHECK-NEXT: @addend_array2 = internal global i32 add (i32 ptrtoint ([7 x i8]* @bytes to i32), i32 7)
@const_addend_array2 = internal constant i32 add (i32 ptrtoint ([7 x i8]* @bytes to i32), i32 7)
; CHECK-NEXT: @const_addend_array2 = internal constant i32 add (i32 ptrtoint ([7 x i8]* @bytes to i32), i32 7)
@addend_array3 = internal global i32 add (i32 ptrtoint ([7 x i8]* @bytes to i32), i32 9)
; CHECK-NEXT: @addend_array3 = internal global i32 add (i32 ptrtoint ([7 x i8]* @bytes to i32), i32 9)
@const_addend_array3 = internal constant i32 add (i32 ptrtoint ([7 x i8]* @bytes to i32), i32 9)
; CHECK-NEXT: @const_addend_array3 = internal constant i32 add (i32 ptrtoint ([7 x i8]* @bytes to i32), i32 9)
@addend_struct1 = internal global i32 add (i32 ptrtoint (<{ [3 x i8], i32 }>* @compound to i32), i32 1)
; CHECK-NEXT: @addend_struct1 = internal global i32 add (i32 ptrtoint (<{ [3 x i8], i32 }>* @compound to i32), i32 1)
@const_addend_struct1 = internal constant i32 add (i32 ptrtoint (<{ [3 x i8], i32 }>* @compound to i32), i32 1)
; CHECK-NEXT: @const_addend_struct1 = internal constant i32 add (i32 ptrtoint (<{ [3 x i8], i32 }>* @compound to i32), i32 1)
@addend_struct2 = internal global i32 add (i32 ptrtoint (<{ [3 x i8], i32 }>* @compound to i32), i32 4)
; CHECK-NEXT: @addend_struct2 = internal global i32 add (i32 ptrtoint (<{ [3 x i8], i32 }>* @compound to i32), i32 4)
@const_addend_struct2 = internal constant i32 add (i32 ptrtoint (<{ [3 x i8], i32 }>* @compound to i32), i32 4)
; CHECK-NEXT: @const_addend_struct2 = internal constant i32 add (i32 ptrtoint (<{ [3 x i8], i32 }>* @compound to i32), i32 4)
@ptr_to_func_align = internal global i32 ptrtoint (void ()* @func to i32), align 8
; CHECK-NEXT: @ptr_to_func_align = internal global i32 ptrtoint (void ()* @func to i32), align 8
@const_ptr_to_func_align = internal constant i32 ptrtoint (void ()* @func to i32), align 8
; CHECK-NEXT: @const_ptr_to_func_align = internal constant i32 ptrtoint (void ()* @func to i32), align 8
@char = internal constant [1 x i8] c"0"
; CHECK-NEXT: @char = internal constant [1 x i8] c"0"
@short = internal constant [2 x i8] zeroinitializer
; CHECK-NEXT: @short = internal constant [2 x i8] zeroinitializer
define void @func() {
ret void
}
; CHECK-NEXT: define void @func() {
; Tests that we name unnamed global addresses.
; Check that the ICE converter handles renaming correctly.
; RUN: %l2i --no-local-syms -i %s --insts | FileCheck %s
; RUN: %l2i --no-local-syms -i %s --insts --args --exit-success \
; RUN: -default-function-prefix=h -default-global-prefix=g \
; RUN: | FileCheck --check-prefix=BAD %s
; Check that Subzero's bitcode reader handles renaming correctly.
; RUN: %p2i --no-local-syms -i %s --insts | FileCheck %s
; RUN: %p2i --no-local-syms -i %s --insts --args \
; RUN: %p2i --no-local-syms -i %s --insts --args --exit-success \
; RUN: -default-function-prefix=h -default-global-prefix=g \
; RUN: | FileCheck --check-prefix=BAD %s
......@@ -12,8 +20,6 @@
@1 = internal constant [10 x i8] c"Some stuff", align 1
@g = internal global [4 x i8] zeroinitializer, align 4
; BAD: Warning: Default global prefix 'g' conflicts with name 'g'.
define i32 @2(i32 %v) {
ret i32 %v
}
......@@ -27,7 +33,6 @@ define void @hg() {
ret void
}
; BAD: Warning: Default function prefix 'h' conflicts with name 'hg'.
; CHECK-NEXT: define void @hg() {
; CHECK-NEXT: __0:
......@@ -42,3 +47,15 @@ define void @3() {
; CHECK-NEXT: __0:
; CHECK-NEXT: ret void
; CHECK-NEXT: }
define void @h5() {
ret void
}
; CHECK-NEXT: define void @h5() {
; CHECK-NEXT: __0:
; CHECK-NEXT: ret void
; CHECK-NEXT: }
; BAD: Warning : Default global prefix 'g' potentially conflicts with name 'g'.
; BAD: Warning : Default function prefix 'h' potentially conflicts with name 'h5'.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment