Commit 08c3bcd6 by Jan Voung

Subzero: Add basic ELFObjectWriter (text section, symtab, strtab, headers).

Able to write out the ELF file header w/ a text section, a symbol table, and string table. Write text buffer directly to file after translating each CFG. This means that the header is written out early w/ fake data and then we seek back and write the real header at the very end. Does not yet handle relocations, data, rodata, constant pools, bss, or -ffunction-sections, more than 64K sections or more than 2^24 symbols. Numbers w/ current NOASSERT=1 build on 176.gcc: w/out -elf-writer: 0.233771 (21.1%): [ 1287] emit 28MB .s file w/ -elf-writer: 0.051056 ( 5.6%): [ 1287] emit 2.4MB .o file BUG=none R=stichnot@chromium.org Review URL: https://codereview.chromium.org/678533005
parent 2f6f8605
......@@ -92,13 +92,14 @@ SRCS = \
assembler_ia32.cpp \
IceCfg.cpp \
IceCfgNode.cpp \
IceELFObjectWriter.cpp \
IceELFSection.cpp \
IceGlobalContext.cpp \
IceGlobalInits.cpp \
IceInst.cpp \
IceInstX8632.cpp \
IceIntrinsics.cpp \
IceLiveness.cpp \
IceMemoryRegion.cpp \
IceOperand.cpp \
IceRegAlloc.cpp \
IceRNG.cpp \
......@@ -117,6 +118,12 @@ endif
OBJS=$(patsubst %.cpp, $(OBJDIR)/%.o, $(SRCS))
UNITTEST_SRCS = \
IceELFSectionTest.cpp
UNITTEST_OBJS = $(patsubst %.cpp, $(OBJDIR)/unittest/%.o, $(UNITTEST_SRCS))
UNITTEST_LIB_OBJS = $(filter-out $(OBJDIR)/llvm2ice.o,$(OBJS))
# Keep all the first target so it's the default.
all: $(OBJDIR)/llvm2ice make_symlink
......@@ -141,20 +148,39 @@ $(OBJDIR)/llvm2ice: $(OBJS)
$(OBJS): $(OBJDIR)/%.o: src/%.cpp src/*.h src/*.def
$(CXX) -c $(CXXFLAGS) $< -o $@
$(OBJDIR)/run_unittests: $(UNITTEST_OBJS) $(UNITTEST_LIB_OBJS)
$(CXX) $(LDFLAGS) -o $@ $^ $(LLVM_LDFLAGS) -lgtest -lgtest_main -ldl \
-Wl,-rpath=$(abspath $(LIBCXX_INSTALL_PATH)/lib)
$(UNITTEST_OBJS): $(OBJDIR)/unittest/%.o: unittest/%.cpp
$(CXX) -c $(CXXFLAGS) \
-Isrc/ \
-I$(LLVM_SRC_PATH)/utils/unittest/googletest/include \
-DGTEST_HAS_RTTI=0 -DGTEST_USE_OWN_TR1_TUPLE \
$< -o $@
$(OBJS): | $(OBJDIR)
$(UNITTEST_OBJS): | $(OBJDIR)/unittest
$(OBJDIR):
@mkdir -p $@
$(OBJDIR)/unittest: $(OBJDIR)
@mkdir -p $@
check-lit: llvm2ice make_symlink
LLVM_BIN_PATH=$(LLVM_BIN_PATH) \
$(LLVM_SRC_PATH)/utils/lit/lit.py -sv tests_lit
check-unit: $(OBJDIR)/run_unittests
$(OBJDIR)/run_unittests
ifdef MINIMAL
check: check-lit
check: check-lit check-unit
@echo "Crosstests ignored, minimal build"
else
check: check-lit
check: check-lit check-unit
(cd crosstest; ./runtests.sh)
endif
......
......@@ -405,8 +405,9 @@ void Cfg::emitTextHeader(const IceString &MangledName) {
Str << "\t.globl\t" << MangledName << "\n";
Str << "\t.type\t" << MangledName << ",@function\n";
}
Str << "\t.p2align " << getTarget()->getBundleAlignLog2Bytes() << ",0x";
for (AsmCodeByte I : getTarget()->getNonExecBundlePadding())
Assembler *Asm = getAssembler<Assembler>();
Str << "\t.p2align " << Asm->getBundleAlignLog2Bytes() << ",0x";
for (uint8_t I : Asm->getNonExecBundlePadding())
Str.write_hex(I);
Str << "\n";
Str << MangledName << ":\n";
......@@ -444,10 +445,20 @@ void Cfg::emitIAS() {
TimerMarker T(TimerStack::TT_emit, this);
assert(!Ctx->getFlags().DecorateAsm);
IceString MangledName = getContext()->mangleName(getFunctionName());
emitTextHeader(MangledName);
if (!Ctx->getFlags().UseELFWriter)
emitTextHeader(MangledName);
for (CfgNode *Node : Nodes)
Node->emitIAS(this);
getAssembler<Assembler>()->emitIASBytes(Ctx);
// Now write the function to the file and track.
if (Ctx->getFlags().UseELFWriter) {
getAssembler<Assembler>()->alignFunction();
// TODO(jvoung): Transfer remaining fixups too. They may need their
// offsets adjusted.
Ctx->getObjectWriter()->writeFunctionCode(
MangledName, getInternal(), getAssembler<Assembler>()->getBufferView());
} else {
getAssembler<Assembler>()->emitIASBytes(Ctx);
}
}
// Dumps the IR with an optional introductory message.
......
......@@ -23,17 +23,18 @@ public:
ClFlags()
: DisableInternal(false), SubzeroTimingEnabled(false),
DisableTranslation(false), FunctionSections(false), DataSections(false),
UseIntegratedAssembler(false), UseSandboxing(false),
PhiEdgeSplit(false), DecorateAsm(false), DumpStats(false),
AllowUninitializedGlobals(false), TimeEachFunction(false),
DisableIRGeneration(false), DefaultGlobalPrefix(""),
DefaultFunctionPrefix(""), TimingFocusOn(""), VerboseFocusOn(""),
TranslateOnly("") {}
UseELFWriter(false), UseIntegratedAssembler(false),
UseSandboxing(false), PhiEdgeSplit(false), DecorateAsm(false),
DumpStats(false), AllowUninitializedGlobals(false),
TimeEachFunction(false), DisableIRGeneration(false),
DefaultGlobalPrefix(""), DefaultFunctionPrefix(""), TimingFocusOn(""),
VerboseFocusOn(""), TranslateOnly("") {}
bool DisableInternal;
bool SubzeroTimingEnabled;
bool DisableTranslation;
bool FunctionSections;
bool DataSections;
bool UseELFWriter;
bool UseIntegratedAssembler;
bool UseSandboxing;
bool PhiEdgeSplit;
......
......@@ -33,6 +33,7 @@
#include "llvm/ADT/SmallBitVector.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/ELF.h"
#include "llvm/Support/raw_ostream.h"
namespace Ice {
......@@ -126,6 +127,7 @@ enum VerboseItem {
typedef uint32_t VerboseMask;
typedef llvm::raw_ostream Ostream;
typedef llvm::raw_fd_ostream Fdstream;
} // end of namespace Ice
......
//===- subzero/src/IceELFObjectWriter.h - ELF object writer -----*- C++ -*-===//
//
// The Subzero Code Generator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Abstraction for a writer that is responsible for writing an ELF file.
//
//===----------------------------------------------------------------------===//
#ifndef SUBZERO_SRC_ICEELFOBJECTWRITER_H
#define SUBZERO_SRC_ICEELFOBJECTWRITER_H
#include "IceDefs.h"
#include "IceELFSection.h"
#include "IceELFStreamer.h"
using namespace llvm::ELF;
namespace Ice {
// Higher level ELF object writer. Manages section information and writes
// the final ELF object. The object writer will write to file the code
// and data as it is being defined (rather than keep a copy).
// After all definitions are written out, it will finalize the bookkeeping
// sections and write them out. Expected usage:
//
// (1) writeInitialELFHeader
// (2) writeDataInitializer*
// (3) writeFunctionCode*
// (4) writeNonUserSections
class ELFObjectWriter {
ELFObjectWriter(const ELFObjectWriter &) = delete;
ELFObjectWriter &operator=(const ELFObjectWriter &) = delete;
public:
ELFObjectWriter(GlobalContext &Ctx, ELFStreamer &Out);
// Write the initial ELF header. This is just to reserve space in the ELF
// file. Reserving space allows the other functions to write text
// and data directly to the file and get the right file offsets.
void writeInitialELFHeader();
// Copy data of a function's text section to file and note the offset of the
// symbol's definition in the symbol table.
// TODO(jvoung): This also needs the relocations to adjust the
// section-relative offsets and hook them up to the symbol table references.
void writeFunctionCode(const IceString &FuncName, bool IsInternal,
const llvm::StringRef Data);
// Copy initializer data for a global to file and note the offset and
// size of the global's definition in the symbol table.
// TODO(jvoung): This needs to know which section. This also needs the
// relocations to hook them up to the symbol table references. Also
// TODO is handling BSS (which just needs to note the size).
void writeDataInitializer(const IceString &VarName,
const llvm::StringRef Data);
// Do final layout and write out the rest of the object file, then
// patch up the initial ELF header with the final info.
void writeNonUserSections();
private:
GlobalContext &Ctx;
ELFStreamer &Str;
bool SectionNumbersAssigned;
// All created sections, separated into different pools.
typedef std::vector<ELFSection *> SectionList;
typedef std::vector<ELFTextSection *> TextSectionList;
typedef std::vector<ELFDataSection *> DataSectionList;
typedef std::vector<ELFRelocationSectionBase *> RelSectionList;
TextSectionList TextSections;
RelSectionList RelTextSections;
DataSectionList DataSections;
RelSectionList RelDataSections;
DataSectionList RoDataSections;
RelSectionList RelRoDataSections;
// Handles to special sections that need incremental bookkeeping.
ELFSection *NullSection;
ELFStringTableSection *ShStrTab;
ELFSymbolTableSection *SymTab;
ELFStringTableSection *StrTab;
template <typename T>
T *createSection(const IceString &Name, Elf64_Word ShType,
Elf64_Xword ShFlags, Elf64_Xword ShAddralign,
Elf64_Xword ShEntsize);
// Align the file position before writing out a section's data,
// and return the position of the file.
Elf64_Off alignFileOffset(Elf64_Xword Align);
// Assign an ordering / section numbers to each section.
// Fill in other information that is only known near the end
// (such as the size, if it wasn't already incrementally updated).
// This then collects all sections in the decided order, into one vector,
// for conveniently writing out all of the section headers.
void assignSectionNumbersInfo(SectionList &AllSections);
// This function assigns .foo and .rel.foo consecutive section numbers.
// It also sets the relocation section's sh_info field to the related
// section's number.
template <typename UserSectionList>
void assignRelSectionNumInPairs(SizeT &CurSectionNumber,
UserSectionList &UserSections,
RelSectionList &RelSections,
SectionList &AllSections);
// Link the relocation sections to the symbol table.
void assignRelLinkNum(SizeT SymTabNumber, RelSectionList &RelSections);
// Write the ELF file header with the given information about sections.
template <bool IsELF64>
void writeELFHeaderInternal(uint64_t SectionHeaderOffset,
SizeT SectHeaderStrIndex, SizeT NumSections);
};
} // end of namespace Ice
#endif // SUBZERO_SRC_ICEELFOBJECTWRITER_H
//===- subzero/src/IceELFSection.cpp - Representation of ELF sections -----===//
//
// The Subzero Code Generator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines how ELF sections are represented.
//
//===----------------------------------------------------------------------===//
#include "IceDefs.h"
#include "IceELFSection.h"
#include "IceELFStreamer.h"
using namespace llvm::ELF;
namespace Ice {
// Text sections.
void ELFTextSection::appendData(ELFStreamer &Str,
const llvm::StringRef MoreData) {
Str.writeBytes(MoreData);
Header.sh_size += MoreData.size();
}
// Data sections.
void ELFDataSection::appendData(ELFStreamer &Str,
const llvm::StringRef MoreData) {
Str.writeBytes(MoreData);
Header.sh_size += MoreData.size();
}
// Relocation sections.
// Symbol tables.
void ELFSymbolTableSection::createDefinedSym(const IceString &Name,
uint8_t Type, uint8_t Binding,
ELFSection *Section,
RelocOffsetT Offset, SizeT Size) {
ELFSym NewSymbol = ELFSym();
NewSymbol.Sym.setBindingAndType(Binding, Type);
NewSymbol.Sym.st_value = Offset;
NewSymbol.Sym.st_size = Size;
NewSymbol.Number = ELFSym::UnknownNumber;
SymtabKey Key = {Name, Section};
bool Unique;
if (Type == STB_LOCAL)
Unique = LocalSymbols.insert(std::make_pair(Key, NewSymbol)).second;
else
Unique = GlobalSymbols.insert(std::make_pair(Key, NewSymbol)).second;
assert(Unique);
}
void ELFSymbolTableSection::noteUndefinedSym(const IceString &Name,
ELFSection *NullSection) {
ELFSym NewSymbol = ELFSym();
NewSymbol.Sym.setBindingAndType(STB_GLOBAL, STT_NOTYPE);
NewSymbol.Number = ELFSym::UnknownNumber;
SymtabKey Key = {Name, NullSection};
GlobalSymbols.insert(std::make_pair(Key, NewSymbol));
}
void ELFSymbolTableSection::updateIndices(const ELFStringTableSection *StrTab) {
SizeT SymNumber = 0;
for (auto &KeyValue : LocalSymbols) {
const IceString &Name = KeyValue.first.first;
ELFSection *Section = KeyValue.first.second;
Elf64_Sym &SymInfo = KeyValue.second.Sym;
if (!Name.empty())
SymInfo.st_name = StrTab->getIndex(Name);
SymInfo.st_shndx = Section->getNumber();
KeyValue.second.setNumber(SymNumber++);
}
for (auto &KeyValue : GlobalSymbols) {
const IceString &Name = KeyValue.first.first;
ELFSection *Section = KeyValue.first.second;
Elf64_Sym &SymInfo = KeyValue.second.Sym;
if (!Name.empty())
SymInfo.st_name = StrTab->getIndex(Name);
SymInfo.st_shndx = Section->getNumber();
KeyValue.second.setNumber(SymNumber++);
}
}
void ELFSymbolTableSection::writeData(ELFStreamer &Str, bool IsELF64) {
if (IsELF64) {
writeSymbolMap<true>(Str, LocalSymbols);
writeSymbolMap<true>(Str, GlobalSymbols);
} else {
writeSymbolMap<false>(Str, LocalSymbols);
writeSymbolMap<false>(Str, GlobalSymbols);
}
}
// String tables.
void ELFStringTableSection::add(const IceString &Str) {
assert(!isLaidOut());
assert(!Str.empty());
StringToIndexMap.insert(std::make_pair(Str, UnknownIndex));
}
size_t ELFStringTableSection::getIndex(const IceString &Str) const {
assert(isLaidOut());
StringToIndexType::const_iterator It = StringToIndexMap.find(Str);
if (It == StringToIndexMap.end()) {
llvm_unreachable("String index not found");
return UnknownIndex;
}
return It->second;
}
bool ELFStringTableSection::SuffixComparator::
operator()(const IceString &StrA, const IceString &StrB) const {
size_t LenA = StrA.size();
size_t LenB = StrB.size();
size_t CommonLen = std::min(LenA, LenB);
// If there is a difference in the common suffix, use that diff to sort.
for (size_t i = 0; i < CommonLen; ++i) {
char a = StrA[LenA - i - 1];
char b = StrB[LenB - i - 1];
if (a != b)
return a > b;
}
// If the common suffixes are completely equal, let the longer one come
// first, so that it can be laid out first and its characters shared.
return LenA > LenB;
}
void ELFStringTableSection::doLayout() {
assert(!isLaidOut());
llvm::StringRef Prev;
// String table starts with 0 byte.
StringData.push_back(0);
for (auto &StringIndex : StringToIndexMap) {
assert(StringIndex.second == UnknownIndex);
llvm::StringRef Cur = llvm::StringRef(StringIndex.first);
if (Prev.endswith(Cur)) {
// Prev is already in the StringData, and Cur is shorter than Prev
// based on the sort.
StringIndex.second = StringData.size() - Cur.size() - 1;
continue;
}
StringIndex.second = StringData.size();
std::copy(Cur.begin(), Cur.end(), back_inserter(StringData));
StringData.push_back(0);
Prev = Cur;
}
}
} // end of namespace Ice
//===- subzero/src/IceELFStreamer.h - Low level ELF writing -----*- C++ -*-===//
//
// The Subzero Code Generator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Interface for serializing bits for common ELF types (words, extended words,
// etc.), based on the ELF Class.
//
//===----------------------------------------------------------------------===//
#ifndef SUBZERO_SRC_ICEELFSTREAMER_H
#define SUBZERO_SRC_ICEELFSTREAMER_H
#include "IceDefs.h"
namespace Ice {
// Low level writer that can that can handle ELFCLASS32/64.
// Little endian only for now.
class ELFStreamer {
public:
explicit ELFStreamer(Fdstream &Out) : Out(Out) {}
void write8(uint8_t Value) { Out << char(Value); }
void writeLE16(uint16_t Value) {
write8(uint8_t(Value));
write8(uint8_t(Value >> 8));
}
void writeLE32(uint32_t Value) {
writeLE16(uint16_t(Value));
writeLE16(uint16_t(Value >> 16));
}
void writeLE64(uint64_t Value) {
writeLE32(uint32_t(Value));
writeLE32(uint32_t(Value >> 32));
}
template <bool IsELF64, typename T> void writeAddrOrOffset(T Value) {
if (IsELF64)
writeLE64(Value);
else
writeLE32(Value);
}
template <bool IsELF64, typename T> void writeELFWord(T Value) {
writeLE32(Value);
}
template <bool IsELF64, typename T> void writeELFXword(T Value) {
if (IsELF64)
writeLE64(Value);
else
writeLE32(Value);
}
void writeBytes(llvm::StringRef Bytes) { Out << Bytes; }
void writeZeroPadding(SizeT N) {
static const char Zeros[16] = {0};
for (SizeT i = 0, e = N / 16; i != e; ++i)
Out << llvm::StringRef(Zeros, 16);
Out << llvm::StringRef(Zeros, N % 16);
}
uint64_t tell() const { return Out.tell(); }
void seek(uint64_t Off) { Out.seek(Off); }
private:
Fdstream &Out;
};
} // end of namespace Ice
#endif // SUBZERO_SRC_ICEELFSTREAMER_H
......@@ -103,17 +103,20 @@ public:
UndefPool Undefs;
};
GlobalContext::GlobalContext(llvm::raw_ostream *OsDump,
llvm::raw_ostream *OsEmit, VerboseMask Mask,
GlobalContext::GlobalContext(Ostream *OsDump, Ostream *OsEmit,
ELFStreamer *ELFStr, VerboseMask Mask,
TargetArch Arch, OptLevel Opt,
IceString TestPrefix, const ClFlags &Flags)
: StrDump(OsDump), StrEmit(OsEmit), VMask(Mask),
ConstPool(new ConstantPool()), Arch(Arch), Opt(Opt),
TestPrefix(TestPrefix), Flags(Flags), HasEmittedFirstMethod(false),
RNG("") {
RNG(""), ObjectWriter() {
// Pre-register built-in stack names.
newTimerStackID("Total across all functions");
newTimerStackID("Per-function summary");
if (Flags.UseELFWriter) {
ObjectWriter.reset(new ELFObjectWriter(*this, *ELFStr));
}
}
// Scan a string for S[0-9A-Z]*_ patterns and replace them with
......
......@@ -18,10 +18,10 @@
#include <memory>
#include "llvm/Support/Allocator.h"
#include "llvm/Support/raw_ostream.h"
#include "IceDefs.h"
#include "IceClFlags.h"
#include "IceELFObjectWriter.h"
#include "IceIntrinsics.h"
#include "IceRNG.h"
#include "IceTimerTree.h"
......@@ -74,7 +74,7 @@ class GlobalContext {
GlobalContext &operator=(const GlobalContext &) = delete;
public:
GlobalContext(llvm::raw_ostream *OsDump, llvm::raw_ostream *OsEmit,
GlobalContext(Ostream *OsDump, Ostream *OsEmit, ELFStreamer *ELFStreamer,
VerboseMask Mask, TargetArch Arch, OptLevel Opt,
IceString TestPrefix, const ClFlags &Flags);
~GlobalContext();
......@@ -158,6 +158,8 @@ public:
// translation.
RandomNumberGenerator &getRNG() { return RNG; }
ELFObjectWriter *getObjectWriter() const { return ObjectWriter.get(); }
// Reset stats at the beginning of a function.
void resetStats() { StatsFunction.reset(); }
void dumpStats(const IceString &Name, bool Final = false);
......@@ -212,6 +214,7 @@ private:
const ClFlags &Flags;
bool HasEmittedFirstMethod;
RandomNumberGenerator RNG;
std::unique_ptr<ELFObjectWriter> ObjectWriter;
CodeStats StatsFunction;
CodeStats StatsCumulative;
std::vector<TimerStack> Timers;
......
//===- subzero/src/IceMemoryRegion.cpp - Memory region --------------------===//
// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
//
// Modified by the Subzero authors.
//
//===----------------------------------------------------------------------===//
//
// The Subzero Code Generator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines the MemoryRegion class. It tracks a pointer plus its
// bounds for bounds-checking in debug mode.
//===----------------------------------------------------------------------===//
#include "IceMemoryRegion.h"
namespace Ice {
void MemoryRegion::CopyFrom(uintptr_t offset, const MemoryRegion &from) const {
assert(from.pointer() != NULL && from.size() > 0);
assert(this->size() >= from.size());
assert(offset <= this->size() - from.size());
memmove(reinterpret_cast<void *>(start() + offset), from.pointer(),
from.size());
}
} // end of namespace Ice
//===- subzero/src/IceMemoryRegion.h - Memory region ------------*- C++ -*-===//
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
//
// Modified by the Subzero authors.
//
//===----------------------------------------------------------------------===//
//
// 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 MemoryRegion class. It tracks a pointer
// plus its bounds for bounds-checking in debug mode.
//===----------------------------------------------------------------------===//
#ifndef SUBZERO_SRC_ICE_MEMORY_REGION_H_
#define SUBZERO_SRC_ICE_MEMORY_REGION_H_
#include "IceDefs.h"
#include "IceUtils.h"
namespace Ice {
// Memory regions are useful for accessing memory with bounds check in
// debug mode. They can be safely passed by value and do not assume ownership
// of the region.
class MemoryRegion {
public:
MemoryRegion(const MemoryRegion &other) = default;
MemoryRegion &operator=(const MemoryRegion &other) = default;
MemoryRegion() : pointer_(NULL), size_(0) {}
MemoryRegion(void *pointer, size_t size) : pointer_(pointer), size_(size) {}
void *pointer() const { return pointer_; }
size_t size() const { return size_; }
size_t start() const { return reinterpret_cast<size_t>(pointer_); }
size_t end() const { return start() + size_; }
template <typename T> T Load(size_t offset) const {
return *ComputeInternalPointer<T>(offset);
}
template <typename T> void Store(size_t offset, T value) const {
*ComputeInternalPointer<T>(offset) = value;
}
template <typename T> T *PointerTo(size_t offset) const {
return ComputeInternalPointer<T>(offset);
}
bool Contains(size_t address) const {
return (address >= start()) && (address < end());
}
void CopyFrom(size_t offset, const MemoryRegion &from) const;
// Compute a sub memory region based on an existing one.
void Subregion(const MemoryRegion &from, size_t offset, size_t size) {
assert(from.size() >= size);
assert(offset <= (from.size() - size));
pointer_ = reinterpret_cast<void *>(from.start() + offset);
size_ = size;
}
// Compute an extended memory region based on an existing one.
void Extend(const MemoryRegion &region, size_t extra) {
pointer_ = region.pointer();
size_ = (region.size() + extra);
}
private:
template <typename T> T *ComputeInternalPointer(size_t offset) const {
assert(size() >= sizeof(T));
assert(offset <= size() - sizeof(T));
return reinterpret_cast<T *>(start() + offset);
}
void *pointer_;
size_t size_;
};
} // end of namespace Ice
#endif // SUBZERO_SRC_ICE_MEMORY_REGION_H_
......@@ -24,8 +24,6 @@
namespace Ice {
typedef uint8_t AsmCodeByte;
class Assembler;
// LoweringContext makes it easy to iterate through non-deleted
......@@ -165,8 +163,6 @@ public:
virtual bool hasFramePointer() const { return false; }
virtual SizeT getFrameOrStackReg() const = 0;
virtual size_t typeWidthInBytesOnStack(Type Ty) const = 0;
virtual SizeT getBundleAlignLog2Bytes() const = 0;
virtual llvm::ArrayRef<AsmCodeByte> getNonExecBundlePadding() const = 0;
bool hasComputedFrame() const { return HasComputedFrame; }
bool shouldDoNopInsertion() const;
// Returns true if this function calls a function that has the
......
......@@ -4555,6 +4555,9 @@ TargetGlobalInitX8632::TargetGlobalInitX8632(GlobalContext *Ctx)
: TargetGlobalInitLowering(Ctx) {}
void TargetGlobalInitX8632::lower(const VariableDeclaration &Var) {
// TODO(jvoung): handle this without text.
if (Ctx->getFlags().UseELFWriter)
return;
Ostream &Str = Ctx->getStrEmit();
......
......@@ -52,11 +52,6 @@ public:
// i8, and i16 are rounded up to 4 bytes.
return (typeWidthInBytes(Ty) + 3) & ~3;
}
SizeT getBundleAlignLog2Bytes() const override { return 5; }
llvm::ArrayRef<AsmCodeByte> getNonExecBundlePadding() const override {
static const AsmCodeByte Padding[] = { 0xF4 };
return llvm::ArrayRef<AsmCodeByte>(Padding, 1);
}
void emitVariable(const Variable *Var) const override;
void lowerArguments() override;
void addProlog(CfgNode *Node) override;
......
......@@ -97,8 +97,13 @@ void Translator::translateFcn(Cfg *Fcn) {
}
void Translator::emitConstants() {
if (!Ctx->getFlags().DisableTranslation && Func)
Func->getTarget()->emitConstants();
if (!Ctx->getFlags().DisableTranslation && Func) {
if (Ctx->getFlags().UseELFWriter) {
// TODO(jvoung): create the rodata.cst.{4,8} sections for UseELFWriter.
} else {
Func->getTarget()->emitConstants();
}
}
}
void Translator::lowerGlobals(
......
......@@ -19,8 +19,8 @@ namespace Ice {
namespace {
const char *TargetArchName[] = {
#define X(tag, str) str,
TARGETARCH_TABLE
#define X(tag, str, is_elf64, e_machine, e_flags) str,
TARGETARCH_TABLE
#undef X
};
......
......@@ -15,13 +15,18 @@
#ifndef SUBZERO_SRC_ICETYPES_DEF
#define SUBZERO_SRC_ICETYPES_DEF
#define TARGETARCH_TABLE \
/* enum value, printable string */ \
X(Target_X8632, "x86-32") \
X(Target_X8664, "x86-64") \
X(Target_ARM32, "arm32") \
X(Target_ARM64, "arm64") \
//#define X(tag, str)
// Attributes of each target architecture.
// NOTE on is_elf64 -- At some point NaCl would like to use ELF32 for all
// ILP32 sandboxes, but for now the 64-bit architectures use ELF64:
// https://code.google.com/p/nativeclient/issues/detail?id=349
// TODO: Whoever adds AArch64 will need to set ABI e_flags.
#define TARGETARCH_TABLE \
/* enum value, printable string, is_elf64, e_machine, e_flags */ \
X(Target_X8632, "x86-32", false, EM_386, 0) \
X(Target_X8664, "x86-64", true, EM_X86_64, 0) \
X(Target_ARM32, "arm32", false, EM_ARM, EF_ARM_EABI_VER5) \
X(Target_ARM64, "arm64", true, EM_AARCH64, 0) \
//#define X(tag, str, is_elf64, e_machine, e_flags)
#define ICETYPE_TABLE \
/* enum value, size, align, # elts, element type, printable string */ \
......
......@@ -28,7 +28,7 @@ enum Type {
};
enum TargetArch {
#define X(tag, str) tag,
#define X(tag, str, is_elf64, e_machine, e_flags) tag,
TARGETARCH_TABLE
#undef X
TargetArch_NUM
......
......@@ -20,7 +20,6 @@
#include "assembler.h"
#include "IceGlobalContext.h"
#include "IceMemoryRegion.h"
#include "IceOperand.h"
namespace Ice {
......@@ -76,25 +75,6 @@ AssemblerBuffer::AssemblerBuffer(Assembler &assembler) : assembler_(assembler) {
AssemblerBuffer::~AssemblerBuffer() {}
void AssemblerBuffer::ProcessFixups(const MemoryRegion &region) {
for (SizeT I = 0; I < fixups_.size(); ++I) {
AssemblerFixup *fixup = fixups_[I];
fixup->Process(region, fixup->position());
}
}
void AssemblerBuffer::FinalizeInstructions(const MemoryRegion &instructions) {
// Copy the instructions from the buffer.
MemoryRegion from(reinterpret_cast<void *>(contents()), Size());
instructions.CopyFrom(0, from);
// Process fixups in the instructions.
ProcessFixups(instructions);
#ifndef NDEBUG
fixups_processed_ = true;
#endif // !NDEBUG
}
void AssemblerBuffer::ExtendCapacity() {
intptr_t old_size = Size();
intptr_t old_capacity = Capacity();
......@@ -123,6 +103,11 @@ void AssemblerBuffer::ExtendCapacity() {
assert(Size() == old_size);
}
llvm::StringRef Assembler::getBufferView() const {
return llvm::StringRef(reinterpret_cast<const char *>(buffer_.contents()),
buffer_.Size());
}
void Assembler::emitIASBytes(GlobalContext *Ctx) const {
Ostream &Str = Ctx->getStrEmit();
intptr_t EndPosition = buffer_.Size();
......
......@@ -35,7 +35,6 @@ class Assembler;
class AssemblerFixup;
class AssemblerBuffer;
class ConstantRelocatable;
class MemoryRegion;
// Assembler fixups are positions in generated code that hold relocation
// information that needs to be processed before finalizing the code
......@@ -45,7 +44,6 @@ class AssemblerFixup {
AssemblerFixup &operator=(const AssemblerFixup &) = delete;
public:
virtual void Process(const MemoryRegion &region, intptr_t position) = 0;
// It would be ideal if the destructor method could be made private,
// but the g++ compiler complains when this is subclassed.
......@@ -109,12 +107,6 @@ public:
intptr_t Size() const { return cursor_ - contents_; }
uintptr_t contents() const { return contents_; }
// Copy the assembled instructions into the specified memory block
// and apply all fixups.
// TODO(jvoung): This will be different. We'll be writing the text
// and reloc section to a file?
void FinalizeInstructions(const MemoryRegion &region);
// To emit an instruction to the assembler buffer, the EnsureCapacity helper
// must be used to guarantee that the underlying data area is big enough to
// hold the emitted instruction. Usage:
......@@ -189,9 +181,6 @@ private:
return (limit_ - contents_) + kMinimumGap;
}
// Process the fixup chain.
void ProcessFixups(const MemoryRegion &region);
// Compute the limit based on the data area and the capacity. See
// description of kMinimumGap for the reasoning behind the value.
static uintptr_t ComputeLimit(uintptr_t data, intptr_t capacity) {
......@@ -226,8 +215,20 @@ public:
// Allocate data of type T using the per-Assembler allocator.
template <typename T> T *Allocate() { return Allocator.Allocate<T>(); }
// Align the tail end of the function to the required target alignment.
virtual void alignFunction() = 0;
virtual SizeT getBundleAlignLog2Bytes() const = 0;
virtual llvm::ArrayRef<uint8_t> getNonExecBundlePadding() const = 0;
// Mark the current text location as the start of a CFG node
// (represented by NodeNumber).
virtual void BindCfgNodeLabel(SizeT NodeNumber) = 0;
// Return a view of all the bytes of code for the current function.
llvm::StringRef getBufferView() const;
void emitIASBytes(GlobalContext *Ctx) const;
private:
......
......@@ -20,7 +20,6 @@
#include "assembler_ia32.h"
#include "IceCfg.h"
#include "IceMemoryRegion.h"
#include "IceOperand.h"
namespace Ice {
......@@ -37,13 +36,6 @@ public:
DirectCallRelocation(Kind, Sym);
}
void Process(const MemoryRegion &region, intptr_t position) override {
// Direct calls are relative to the following instruction on x86.
int32_t pointer = region.Load<int32_t>(position);
int32_t delta = region.start() + position + sizeof(int32_t);
region.Store<int32_t>(position, pointer - delta);
}
private:
DirectCallRelocation(FixupKind Kind, const ConstantRelocatable *Sym)
: AssemblerFixup(Kind, Sym) {}
......@@ -77,6 +69,22 @@ AssemblerX86::~AssemblerX86() {
#endif
}
void AssemblerX86::alignFunction() {
intptr_t Pos = buffer_.GetPosition();
SizeT Align = 1 << getBundleAlignLog2Bytes();
intptr_t Mod = Pos & (Align - 1);
if (Mod == 0) {
return;
}
SizeT BytesNeeded = Align - Mod;
const SizeT HltSize = 1;
while (BytesNeeded > 0) {
hlt();
BytesNeeded -= HltSize;
}
assert((buffer_.GetPosition() & (Align - 1)) == 0);
}
Label *AssemblerX86::GetOrCreateLabel(SizeT Number, LabelVector &Labels) {
Label *L = nullptr;
if (Number == Labels.size()) {
......
......@@ -56,12 +56,6 @@ public:
DisplacementRelocation(Kind, Sym);
}
void Process(const MemoryRegion &region, intptr_t position) override {
(void)region;
(void)position;
llvm_unreachable("We might not be using this Process() method later.");
}
private:
DisplacementRelocation(FixupKind Kind, const ConstantRelocatable *Sym)
: AssemblerFixup(Kind, Sym) {}
......@@ -374,6 +368,15 @@ public:
static const bool kNearJump = true;
static const bool kFarJump = false;
void alignFunction() override;
SizeT getBundleAlignLog2Bytes() const override { return 5; }
llvm::ArrayRef<uint8_t> getNonExecBundlePadding() const override {
static const uint8_t Padding[] = {0xF4};
return llvm::ArrayRef<uint8_t>(Padding, 1);
}
Label *GetOrCreateCfgNodeLabel(SizeT NodeNumber);
void BindCfgNodeLabel(SizeT NodeNumber) override;
Label *GetOrCreateLocalLabel(SizeT Number);
......@@ -825,10 +828,6 @@ public:
intptr_t CodeSize() const { return buffer_.Size(); }
void FinalizeInstructions(const MemoryRegion &region) {
buffer_.FinalizeInstructions(region);
}
private:
inline void EmitUint8(uint8_t value);
inline void EmitInt16(int16_t value);
......
......@@ -20,6 +20,7 @@
#include "llvm/IR/LLVMContext.h"
#include "llvm/IRReader/IRReader.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/raw_os_ostream.h"
#include "llvm/Support/SourceMgr.h"
......@@ -173,6 +174,11 @@ static cl::opt<bool>
static cl::alias UseIas("ias", cl::desc("Alias for -integrated-as"),
cl::NotHidden, cl::aliasopt(UseIntegratedAssembler));
static cl::opt<bool>
UseELFWriter("elf-writer",
cl::desc("Use ELF writer with the integrated assembler"),
cl::init(false));
static cl::opt<bool> AlwaysExitSuccess(
"exit-success", cl::desc("Exit with success status, even if errors found"),
cl::init(false));
......@@ -200,7 +206,7 @@ static struct {
// Validates values of build attributes. Prints them to Stream if
// Stream is non-null.
static void ValidateAndGenerateBuildAttributes(raw_os_ostream *Stream) {
static void ValidateAndGenerateBuildAttributes(Ice::Ostream *Stream) {
if (Stream)
*Stream << TargetArch << "\n";
......@@ -242,25 +248,20 @@ int main(int argc, char **argv) {
VMask |= VerboseList[i];
}
std::ofstream Ofs;
if (OutputFilename != "-") {
Ofs.open(OutputFilename.c_str(), std::ofstream::out);
}
raw_os_ostream *Os =
new raw_os_ostream(OutputFilename == "-" ? std::cout : Ofs);
Os->SetUnbuffered();
ValidateAndGenerateBuildAttributes(GenerateBuildAtts ? Os : nullptr);
if (GenerateBuildAtts)
return GetReturnValue(0);
std::ofstream Lfs;
std::unique_ptr<Ice::Ostream> Ls;
if (LogFilename != "-") {
Lfs.open(LogFilename.c_str(), std::ofstream::out);
Ls.reset(new raw_os_ostream(Lfs));
} else {
Ls.reset(new raw_os_ostream(std::cout));
}
raw_os_ostream *Ls = new raw_os_ostream(LogFilename == "-" ? std::cout : Lfs);
Ls->SetUnbuffered();
ValidateAndGenerateBuildAttributes(GenerateBuildAtts ? Ls.get() : nullptr);
if (GenerateBuildAtts)
return GetReturnValue(0);
if (!ALLOW_DISABLE_IR_GEN && DisableIRGeneration) {
*Ls << "Error: Build doesn't allow --no-ir-gen when not "
<< "ALLOW_DISABLE_IR_GEN!\n";
......@@ -274,6 +275,7 @@ int main(int argc, char **argv) {
Flags.FunctionSections = FunctionSections;
Flags.DataSections = DataSections;
Flags.UseIntegratedAssembler = UseIntegratedAssembler;
Flags.UseELFWriter = UseELFWriter;
Flags.UseSandboxing = UseSandboxing;
Flags.PhiEdgeSplit = EnablePhiEdgeSplit;
Flags.DecorateAsm = DecorateAsm;
......@@ -294,11 +296,45 @@ int main(int argc, char **argv) {
LLSuffix.length(), LLSuffix) == 0)
BuildOnRead = false;
Ice::GlobalContext Ctx(Ls, Os, VMask, TargetArch, OptLevel, TestPrefix,
Flags);
// With the ELF writer, use a raw_fd_ostream to allow seeking.
// Also don't buffer, otherwise it gets pretty slow.
std::unique_ptr<Ice::Ostream> Os;
std::unique_ptr<Ice::ELFStreamer> ELFStr;
std::ofstream Ofs;
if (UseELFWriter) {
if (OutputFilename == "-") {
*Ls << "Error: writing binary ELF to stdout is unsupported\n";
return GetReturnValue(1);
}
std::string ErrorInfo;
raw_fd_ostream *FdOs =
new raw_fd_ostream(OutputFilename.c_str(), ErrorInfo, sys::fs::F_None);
Os.reset(FdOs);
if (!ErrorInfo.empty()) {
*Ls << "Failed to open output file: " << OutputFilename << ":\n"
<< ErrorInfo << "\n";
return GetReturnValue(1);
}
ELFStr.reset(new Ice::ELFStreamer(*FdOs));
} else {
if (OutputFilename != "-") {
Ofs.open(OutputFilename.c_str(), std::ofstream::out);
Os.reset(new raw_os_ostream(Ofs));
} else {
Os.reset(new raw_os_ostream(std::cout));
}
Os->SetUnbuffered();
}
Ice::GlobalContext Ctx(Ls.get(), Os.get(), ELFStr.get(), VMask, TargetArch,
OptLevel, TestPrefix, Flags);
Ice::TimerMarker T(Ice::TimerStack::TT_szmain, &Ctx);
if (UseELFWriter) {
Ctx.getObjectWriter()->writeInitialELFHeader();
}
int ErrorStatus = 0;
if (BuildOnRead) {
Ice::PNaClTranslator Translator(&Ctx, Flags);
......@@ -324,6 +360,9 @@ int main(int argc, char **argv) {
<< "--build-on-read=0 not allowed\n";
return GetReturnValue(1);
}
if (UseELFWriter) {
Ctx.getObjectWriter()->writeNonUserSections();
}
if (SubzeroTimingEnabled)
Ctx.dumpTimers();
if (TimeEachFunction) {
......
; Tests that we generate an ELF container with fields that make sense,
; cross-validating against llvm-mc.
; For the integrated ELF writer, we can't pipe the output because we need
; to seek backward and patch up the file headers. So, use a temporary file.
; RUN: %p2i -i %s --args -O2 --verbose none -elf-writer -o %t \
; RUN: && llvm-readobj -file-headers -sections -section-data \
; RUN: -relocations -symbols %t | FileCheck %s
; RUN: %p2i -i %s --args -O2 --verbose none \
; RUN: | llvm-mc -triple=i686-none-nacl -filetype=obj -o - \
; RUN: | llvm-readobj -file-headers -sections -section-data \
; RUN: -relocations -symbols - | FileCheck %s
; Use intrinsics to test external calls.
declare void @llvm.memcpy.p0i8.p0i8.i32(i8*, i8*, i32, i32, i1)
declare void @llvm.memset.p0i8.i32(i8*, i8, i32, i32, i1)
; Test some global data relocs (data, rodata, bss).
@bytes = internal global [7 x i8] c"abcdefg", align 1
@const_bytes = internal constant [7 x i8] c"abcdefg", align 1
@ptr = internal global i32 ptrtoint ([7 x i8]* @bytes to i32), align 4
@const_ptr = internal constant i32 ptrtoint ([7 x i8]* @bytes to i32), align 4
@ptr_to_func = internal global i32 ptrtoint (double ()* @returnDoubleConst to i32), align 4
@const_ptr_to_func = internal constant i32 ptrtoint (double ()* @returnDoubleConst to i32), align 4
@short_zero = internal global [2 x i8] zeroinitializer, align 2
@double_zero = internal global [8 x i8] zeroinitializer, align 8
@const_short_zero = internal constant [2 x i8] zeroinitializer, align 2
@const_double_zero = internal constant [8 x i8] zeroinitializer, align 8
@addend_ptr = internal global i32 add (i32 ptrtoint (i32* @ptr to i32), i32 128)
@const_addend_ptr = internal constant i32 add (i32 ptrtoint (i32* @ptr to i32), i32 64)
; Use float/double constants to test constant pools.
define internal float @returnFloatConst() {
entry:
%f = fadd float 0x3FF3AE1480000000, 0x3FF3AE1400000000
ret float %f
}
define internal double @returnDoubleConst() {
entry:
%d = fadd double 1.230000e+00, 3.210000e+00
ret double %d
}
define internal void @test_memcpy(i32 %iptr_dst, i32 %len) {
entry:
%dst = inttoptr i32 %iptr_dst to i8*
%src = bitcast [7 x i8]* @bytes to i8*
call void @llvm.memcpy.p0i8.p0i8.i32(i8* %dst, i8* %src,
i32 %len, i32 1, i1 false)
ret void
}
define internal void @test_memset(i32 %iptr_dst, i32 %wide_val, i32 %len) {
entry:
%val = trunc i32 %wide_val to i8
%dst = inttoptr i32 %iptr_dst to i8*
call void @llvm.memset.p0i8.i32(i8* %dst, i8 %val,
i32 %len, i32 1, i1 false)
ret void
}
; Test non-internal functions too.
define void @_start(i32) {
%f = call float @returnFloatConst()
%d = call double @returnDoubleConst()
call void @test_memcpy(i32 0, i32 99)
call void @test_memset(i32 0, i32 0, i32 99)
ret void
}
; CHECK: ElfHeader {
; CHECK: Ident {
; CHECK: Magic: (7F 45 4C 46)
; CHECK: Class: 32-bit
; CHECK: DataEncoding: LittleEndian
; CHECK: OS/ABI: SystemV (0x0)
; CHECK: ABIVersion: 0
; CHECK: Unused: (00 00 00 00 00 00 00)
; CHECK: }
; CHECK: Type: Relocatable (0x1)
; CHECK: Machine: EM_386 (0x3)
; CHECK: Version: 1
; CHECK: Entry: 0x0
; CHECK: ProgramHeaderOffset: 0x0
; CHECK: SectionHeaderOffset: 0x{{[1-9A-F][0-9A-F]*}}
; CHECK: Flags [ (0x0)
; CHECK: ]
; CHECK: HeaderSize: 52
; CHECK: ProgramHeaderEntrySize: 0
; CHECK: ProgramHeaderCount: 0
; CHECK: SectionHeaderEntrySize: 40
; CHECK: SectionHeaderCount: {{[1-9][0-9]*}}
; CHECK: StringTableSectionIndex: {{[1-9][0-9]*}}
; CHECK: }
; CHECK: Sections [
; CHECK: Section {
; CHECK: Index: 0
; CHECK: Name: (0)
; CHECK: Type: SHT_NULL
; CHECK: Flags [ (0x0)
; CHECK: ]
; CHECK: Address: 0x0
; CHECK: Offset: 0x0
; CHECK: Size: 0
; CHECK: Link: 0
; CHECK: Info: 0
; CHECK: AddressAlignment: 0
; CHECK: EntrySize: 0
; CHECK: SectionData (
; CHECK-NEXT: )
; CHECK: }
; CHECK: Section {
; CHECK: Index: {{[1-9][0-9]*}}
; CHECK: Name: .text
; CHECK: Type: SHT_PROGBITS
; CHECK: Flags [ (0x6)
; CHECK: SHF_ALLOC
; CHECK: SHF_EXECINSTR
; CHECK: ]
; CHECK: Address: 0x0
; CHECK: Offset: 0x{{[1-9A-F][0-9A-F]*}}
; CHECK: Size: {{[1-9][0-9]*}}
; CHECK: Link: 0
; CHECK: Info: 0
; CHECK: AddressAlignment: 32
; CHECK: EntrySize: 0
; CHECK: SectionData (
; There's probably halt padding (0xF4) in there somewhere.
; CHECK: {{.*}}F4
; CHECK: )
; CHECK: }
; CHECK: Section {
; CHECK: Index: {{[1-9][0-9]*}}
; CHECK: Name: .shstrtab
; CHECK: Type: SHT_STRTAB
; CHECK: Flags [ (0x0)
; CHECK: ]
; CHECK: Address: 0x0
; CHECK: Offset: 0x{{[1-9A-F][0-9A-F]*}}
; CHECK: Size: {{[1-9][0-9]*}}
; CHECK: Link: 0
; CHECK: Info: 0
; CHECK: AddressAlignment: 1
; CHECK: EntrySize: 0
; CHECK: SectionData (
; CHECK: {{.*}}.text{{.*}}
; CHECK: )
; CHECK: }
; CHECK: Section {
; CHECK: Index: {{[1-9][0-9]*}}
; CHECK: Name: .symtab
; CHECK: Type: SHT_SYMTAB
; CHECK: Flags [ (0x0)
; CHECK: ]
; CHECK: Address: 0x0
; CHECK: Offset: 0x{{[1-9A-F][0-9A-F]*}}
; CHECK: Size: {{[1-9][0-9]*}}
; CHECK: Link: [[STRTAB_INDEX:[1-9][0-9]*]]
; CHECK: Info: [[GLOBAL_START_INDEX:[1-9][0-9]*]]
; CHECK: AddressAlignment: 4
; CHECK: EntrySize: 16
; CHECK: }
; CHECK: Section {
; CHECK: Index: [[STRTAB_INDEX]]
; CHECK: Name: .strtab
; CHECK: Type: SHT_STRTAB
; CHECK: Flags [ (0x0)
; CHECK: ]
; CHECK: Address: 0x0
; CHECK: Offset: 0x{{[1-9A-F][0-9A-F]*}}
; CHECK: Size: {{[1-9][0-9]*}}
; CHECK: Link: 0
; CHECK: Info: 0
; CHECK: AddressAlignment: 1
; CHECK: EntrySize: 0
; CHECK: }
; CHECK: Relocations [
; TODO: fill it out.
; CHECK: ]
; CHECK: Symbols [
; CHECK-NEXT: Symbol {
; CHECK-NEXT: Name: (0)
; CHECK-NEXT: Value: 0x0
; CHECK-NEXT: Size: 0
; CHECK-NEXT: Binding: Local
; CHECK-NEXT: Type: None
; CHECK-NEXT: Other: 0
; CHECK-NEXT: Section: Undefined (0x0)
; CHECK-NEXT: }
; TODO: fill in the data symbols.
; CHECK: Symbol {
; CHECK: Name: returnDoubleConst
; CHECK-NEXT: Value: 0x{{[1-9A-F][0-9A-F]*}}
; CHECK-NEXT: Size: 0
; CHECK-NEXT: Binding: Local
; CHECK-NEXT: Type: None
; CHECK-NEXT: Other: 0
; CHECK-NEXT: Section: .text
; CHECK-NEXT: }
; CHECK: Symbol {
; CHECK: Name: returnFloatConst
; This happens to be the first function, so its offset is 0 within the text.
; CHECK-NEXT: Value: 0x0
; CHECK-NEXT: Size: 0
; CHECK-NEXT: Binding: Local
; CHECK-NEXT: Type: None
; CHECK-NEXT: Other: 0
; CHECK-NEXT: Section: .text
; CHECK-NEXT: }
; CHECK: Symbol {
; CHECK: Name: test_memcpy
; CHECK-NEXT: Value: 0x{{[1-9A-F][0-9A-F]*}}
; CHECK-NEXT: Size: 0
; CHECK-NEXT: Binding: Local
; CHECK-NEXT: Type: None
; CHECK-NEXT: Other: 0
; CHECK-NEXT: Section: .text
; CHECK-NEXT: }
; CHECK: Symbol {
; CHECK: Name: _start
; CHECK-NEXT: Value: 0x{{[1-9A-F][0-9A-F]*}}
; CHECK-NEXT: Size: 0
; CHECK-NEXT: Binding: Global
; CHECK-NEXT: Type: Function
; CHECK-NEXT: Other: 0
; CHECK-NEXT: Section: .text
; CHECK-NEXT: }
; CHECK: ]
//===- unittest/IceELFSectionTest.cpp - ELF Section unit tests ------------===//
//
// The Subzero Code Generator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include <algorithm>
#include "gtest/gtest.h"
#include "IceDefs.h"
#include "IceELFSection.h"
namespace Ice {
namespace {
// Test string table layout for various permutations. Test that pop,
// lollipop, and lipop are able to share data, while the other strings do not.
void CheckStringTablePermLayout(const ELFStringTableSection &Strtab) {
size_t pop_index = Strtab.getIndex("pop");
size_t pop_size = IceString("pop").size();
size_t lollipop_index = Strtab.getIndex("lollipop");
size_t lollipop_size = IceString("lollipop").size();
size_t lipop_index = Strtab.getIndex("lipop");
size_t lipop_size = IceString("lipop").size();
size_t pops_index = Strtab.getIndex("pops");
size_t pops_size = IceString("pops").size();
size_t unpop_index = Strtab.getIndex("unpop");
size_t unpop_size = IceString("unpop").size();
size_t popular_index = Strtab.getIndex("popular");
size_t popular_size = IceString("popular").size();
size_t strtab_index = Strtab.getIndex(".strtab");
size_t strtab_size = IceString(".strtab").size();
size_t shstrtab_index = Strtab.getIndex(".shstrtab");
size_t shstrtab_size = IceString(".shstrtab").size();
size_t symtab_index = Strtab.getIndex(".symtab");
size_t symtab_size = IceString(".symtab").size();
// Check that some sharing exists.
EXPECT_EQ(pop_index, lollipop_index + (lollipop_size - pop_size));
EXPECT_EQ(lipop_index, lollipop_index + (lollipop_size - lipop_size));
// Check that .strtab does not share with .shstrtab (the dot throws it off).
EXPECT_NE(strtab_index, shstrtab_index + (shstrtab_size - strtab_size));
// Check contents make sense.
EXPECT_EQ(Strtab.getSectionData().slice(pop_index, pop_index + pop_size),
llvm::StringRef("pop"));
EXPECT_EQ(Strtab.getSectionData().slice(lollipop_index,
lollipop_index + lollipop_size),
llvm::StringRef("lollipop"));
EXPECT_EQ(Strtab.getSectionData().slice(pops_index, pops_index + pops_size),
llvm::StringRef("pops"));
EXPECT_EQ(
Strtab.getSectionData().slice(unpop_index, unpop_index + unpop_size),
llvm::StringRef("unpop"));
EXPECT_EQ(Strtab.getSectionData().slice(popular_index,
popular_index + popular_size),
llvm::StringRef("popular"));
EXPECT_EQ(
Strtab.getSectionData().slice(strtab_index, strtab_index + strtab_size),
llvm::StringRef(".strtab"));
EXPECT_EQ(Strtab.getSectionData().slice(shstrtab_index,
shstrtab_index + shstrtab_size),
llvm::StringRef(".shstrtab"));
EXPECT_EQ(
Strtab.getSectionData().slice(symtab_index, symtab_index + symtab_size),
llvm::StringRef(".symtab"));
}
// Test that the order in which strings are added doesn't matter.
TEST(IceELFSectionTest, StringTableBuilderPermSeveral) {
std::vector<IceString> Strings;
Strings.push_back("pop");
Strings.push_back("lollipop");
Strings.push_back("lipop");
Strings.push_back("pops");
Strings.push_back("unpop");
Strings.push_back("popular");
Strings.push_back("a");
Strings.push_back("z");
Strings.push_back("foo");
Strings.push_back("bar");
Strings.push_back(".text");
Strings.push_back(".symtab");
Strings.push_back(".strtab");
Strings.push_back(".shstrtab");
Strings.push_back("_start");
const SizeT NumTests = 128;
for (SizeT i = 0; i < NumTests; ++i) {
std::random_shuffle(Strings.begin(), Strings.end());
ELFStringTableSection Strtab(".strtab", SHT_STRTAB, 0, 1, 0);
for (auto &S : Strings) {
Strtab.add(S);
}
Strtab.doLayout();
CheckStringTablePermLayout(Strtab);
}
}
// Test that adding duplicate strings is fine.
TEST(IceELFSectionTest, StringTableBuilderDuplicates) {
ELFStringTableSection Strtab(".strtab", SHT_STRTAB, 0, 1, 0);
Strtab.add("unpop");
Strtab.add("pop");
Strtab.add("lollipop");
Strtab.add("a");
Strtab.add("popular");
Strtab.add("pops");
Strtab.add("lipop");
Strtab.add(".strtab");
Strtab.add(".shstrtab");
Strtab.add(".symtab");
Strtab.add(".symtab");
Strtab.add(".shstrtab");
Strtab.add(".strtab");
Strtab.add("lipop");
Strtab.add("pops");
Strtab.add("popular");
Strtab.add("a");
Strtab.add("lollipop");
Strtab.add("pop");
Strtab.add("unpop");
Strtab.doLayout();
CheckStringTablePermLayout(Strtab);
}
} // end of anonymous namespace
} // end of namespace Ice
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