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 = \ ...@@ -92,13 +92,14 @@ SRCS = \
assembler_ia32.cpp \ assembler_ia32.cpp \
IceCfg.cpp \ IceCfg.cpp \
IceCfgNode.cpp \ IceCfgNode.cpp \
IceELFObjectWriter.cpp \
IceELFSection.cpp \
IceGlobalContext.cpp \ IceGlobalContext.cpp \
IceGlobalInits.cpp \ IceGlobalInits.cpp \
IceInst.cpp \ IceInst.cpp \
IceInstX8632.cpp \ IceInstX8632.cpp \
IceIntrinsics.cpp \ IceIntrinsics.cpp \
IceLiveness.cpp \ IceLiveness.cpp \
IceMemoryRegion.cpp \
IceOperand.cpp \ IceOperand.cpp \
IceRegAlloc.cpp \ IceRegAlloc.cpp \
IceRNG.cpp \ IceRNG.cpp \
...@@ -117,6 +118,12 @@ endif ...@@ -117,6 +118,12 @@ endif
OBJS=$(patsubst %.cpp, $(OBJDIR)/%.o, $(SRCS)) 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. # Keep all the first target so it's the default.
all: $(OBJDIR)/llvm2ice make_symlink all: $(OBJDIR)/llvm2ice make_symlink
...@@ -141,20 +148,39 @@ $(OBJDIR)/llvm2ice: $(OBJS) ...@@ -141,20 +148,39 @@ $(OBJDIR)/llvm2ice: $(OBJS)
$(OBJS): $(OBJDIR)/%.o: src/%.cpp src/*.h src/*.def $(OBJS): $(OBJDIR)/%.o: src/%.cpp src/*.h src/*.def
$(CXX) -c $(CXXFLAGS) $< -o $@ $(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) $(OBJS): | $(OBJDIR)
$(UNITTEST_OBJS): | $(OBJDIR)/unittest
$(OBJDIR): $(OBJDIR):
@mkdir -p $@ @mkdir -p $@
$(OBJDIR)/unittest: $(OBJDIR)
@mkdir -p $@
check-lit: llvm2ice make_symlink check-lit: llvm2ice make_symlink
LLVM_BIN_PATH=$(LLVM_BIN_PATH) \ LLVM_BIN_PATH=$(LLVM_BIN_PATH) \
$(LLVM_SRC_PATH)/utils/lit/lit.py -sv tests_lit $(LLVM_SRC_PATH)/utils/lit/lit.py -sv tests_lit
check-unit: $(OBJDIR)/run_unittests
$(OBJDIR)/run_unittests
ifdef MINIMAL ifdef MINIMAL
check: check-lit check: check-lit check-unit
@echo "Crosstests ignored, minimal build" @echo "Crosstests ignored, minimal build"
else else
check: check-lit check: check-lit check-unit
(cd crosstest; ./runtests.sh) (cd crosstest; ./runtests.sh)
endif endif
......
...@@ -405,8 +405,9 @@ void Cfg::emitTextHeader(const IceString &MangledName) { ...@@ -405,8 +405,9 @@ void Cfg::emitTextHeader(const IceString &MangledName) {
Str << "\t.globl\t" << MangledName << "\n"; Str << "\t.globl\t" << MangledName << "\n";
Str << "\t.type\t" << MangledName << ",@function\n"; Str << "\t.type\t" << MangledName << ",@function\n";
} }
Str << "\t.p2align " << getTarget()->getBundleAlignLog2Bytes() << ",0x"; Assembler *Asm = getAssembler<Assembler>();
for (AsmCodeByte I : getTarget()->getNonExecBundlePadding()) Str << "\t.p2align " << Asm->getBundleAlignLog2Bytes() << ",0x";
for (uint8_t I : Asm->getNonExecBundlePadding())
Str.write_hex(I); Str.write_hex(I);
Str << "\n"; Str << "\n";
Str << MangledName << ":\n"; Str << MangledName << ":\n";
...@@ -444,10 +445,20 @@ void Cfg::emitIAS() { ...@@ -444,10 +445,20 @@ void Cfg::emitIAS() {
TimerMarker T(TimerStack::TT_emit, this); TimerMarker T(TimerStack::TT_emit, this);
assert(!Ctx->getFlags().DecorateAsm); assert(!Ctx->getFlags().DecorateAsm);
IceString MangledName = getContext()->mangleName(getFunctionName()); IceString MangledName = getContext()->mangleName(getFunctionName());
emitTextHeader(MangledName); if (!Ctx->getFlags().UseELFWriter)
emitTextHeader(MangledName);
for (CfgNode *Node : Nodes) for (CfgNode *Node : Nodes)
Node->emitIAS(this); 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. // Dumps the IR with an optional introductory message.
......
...@@ -23,17 +23,18 @@ public: ...@@ -23,17 +23,18 @@ public:
ClFlags() ClFlags()
: DisableInternal(false), SubzeroTimingEnabled(false), : DisableInternal(false), SubzeroTimingEnabled(false),
DisableTranslation(false), FunctionSections(false), DataSections(false), DisableTranslation(false), FunctionSections(false), DataSections(false),
UseIntegratedAssembler(false), UseSandboxing(false), UseELFWriter(false), UseIntegratedAssembler(false),
PhiEdgeSplit(false), DecorateAsm(false), DumpStats(false), UseSandboxing(false), PhiEdgeSplit(false), DecorateAsm(false),
AllowUninitializedGlobals(false), TimeEachFunction(false), DumpStats(false), AllowUninitializedGlobals(false),
DisableIRGeneration(false), DefaultGlobalPrefix(""), TimeEachFunction(false), DisableIRGeneration(false),
DefaultFunctionPrefix(""), TimingFocusOn(""), VerboseFocusOn(""), DefaultGlobalPrefix(""), DefaultFunctionPrefix(""), TimingFocusOn(""),
TranslateOnly("") {} VerboseFocusOn(""), TranslateOnly("") {}
bool DisableInternal; bool DisableInternal;
bool SubzeroTimingEnabled; bool SubzeroTimingEnabled;
bool DisableTranslation; bool DisableTranslation;
bool FunctionSections; bool FunctionSections;
bool DataSections; bool DataSections;
bool UseELFWriter;
bool UseIntegratedAssembler; bool UseIntegratedAssembler;
bool UseSandboxing; bool UseSandboxing;
bool PhiEdgeSplit; bool PhiEdgeSplit;
......
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
#include "llvm/ADT/SmallBitVector.h" #include "llvm/ADT/SmallBitVector.h"
#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/STLExtras.h"
#include "llvm/Support/Casting.h" #include "llvm/Support/Casting.h"
#include "llvm/Support/ELF.h"
#include "llvm/Support/raw_ostream.h" #include "llvm/Support/raw_ostream.h"
namespace Ice { namespace Ice {
...@@ -126,6 +127,7 @@ enum VerboseItem { ...@@ -126,6 +127,7 @@ enum VerboseItem {
typedef uint32_t VerboseMask; typedef uint32_t VerboseMask;
typedef llvm::raw_ostream Ostream; typedef llvm::raw_ostream Ostream;
typedef llvm::raw_fd_ostream Fdstream;
} // end of namespace Ice } // 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: ...@@ -103,17 +103,20 @@ public:
UndefPool Undefs; UndefPool Undefs;
}; };
GlobalContext::GlobalContext(llvm::raw_ostream *OsDump, GlobalContext::GlobalContext(Ostream *OsDump, Ostream *OsEmit,
llvm::raw_ostream *OsEmit, VerboseMask Mask, ELFStreamer *ELFStr, VerboseMask Mask,
TargetArch Arch, OptLevel Opt, TargetArch Arch, OptLevel Opt,
IceString TestPrefix, const ClFlags &Flags) IceString TestPrefix, const ClFlags &Flags)
: StrDump(OsDump), StrEmit(OsEmit), VMask(Mask), : StrDump(OsDump), StrEmit(OsEmit), VMask(Mask),
ConstPool(new ConstantPool()), Arch(Arch), Opt(Opt), ConstPool(new ConstantPool()), Arch(Arch), Opt(Opt),
TestPrefix(TestPrefix), Flags(Flags), HasEmittedFirstMethod(false), TestPrefix(TestPrefix), Flags(Flags), HasEmittedFirstMethod(false),
RNG("") { RNG(""), ObjectWriter() {
// Pre-register built-in stack names. // Pre-register built-in stack names.
newTimerStackID("Total across all functions"); newTimerStackID("Total across all functions");
newTimerStackID("Per-function summary"); 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 // Scan a string for S[0-9A-Z]*_ patterns and replace them with
......
...@@ -18,10 +18,10 @@ ...@@ -18,10 +18,10 @@
#include <memory> #include <memory>
#include "llvm/Support/Allocator.h" #include "llvm/Support/Allocator.h"
#include "llvm/Support/raw_ostream.h"
#include "IceDefs.h" #include "IceDefs.h"
#include "IceClFlags.h" #include "IceClFlags.h"
#include "IceELFObjectWriter.h"
#include "IceIntrinsics.h" #include "IceIntrinsics.h"
#include "IceRNG.h" #include "IceRNG.h"
#include "IceTimerTree.h" #include "IceTimerTree.h"
...@@ -74,7 +74,7 @@ class GlobalContext { ...@@ -74,7 +74,7 @@ class GlobalContext {
GlobalContext &operator=(const GlobalContext &) = delete; GlobalContext &operator=(const GlobalContext &) = delete;
public: public:
GlobalContext(llvm::raw_ostream *OsDump, llvm::raw_ostream *OsEmit, GlobalContext(Ostream *OsDump, Ostream *OsEmit, ELFStreamer *ELFStreamer,
VerboseMask Mask, TargetArch Arch, OptLevel Opt, VerboseMask Mask, TargetArch Arch, OptLevel Opt,
IceString TestPrefix, const ClFlags &Flags); IceString TestPrefix, const ClFlags &Flags);
~GlobalContext(); ~GlobalContext();
...@@ -158,6 +158,8 @@ public: ...@@ -158,6 +158,8 @@ public:
// translation. // translation.
RandomNumberGenerator &getRNG() { return RNG; } RandomNumberGenerator &getRNG() { return RNG; }
ELFObjectWriter *getObjectWriter() const { return ObjectWriter.get(); }
// Reset stats at the beginning of a function. // Reset stats at the beginning of a function.
void resetStats() { StatsFunction.reset(); } void resetStats() { StatsFunction.reset(); }
void dumpStats(const IceString &Name, bool Final = false); void dumpStats(const IceString &Name, bool Final = false);
...@@ -212,6 +214,7 @@ private: ...@@ -212,6 +214,7 @@ private:
const ClFlags &Flags; const ClFlags &Flags;
bool HasEmittedFirstMethod; bool HasEmittedFirstMethod;
RandomNumberGenerator RNG; RandomNumberGenerator RNG;
std::unique_ptr<ELFObjectWriter> ObjectWriter;
CodeStats StatsFunction; CodeStats StatsFunction;
CodeStats StatsCumulative; CodeStats StatsCumulative;
std::vector<TimerStack> Timers; 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 @@ ...@@ -24,8 +24,6 @@
namespace Ice { namespace Ice {
typedef uint8_t AsmCodeByte;
class Assembler; class Assembler;
// LoweringContext makes it easy to iterate through non-deleted // LoweringContext makes it easy to iterate through non-deleted
...@@ -165,8 +163,6 @@ public: ...@@ -165,8 +163,6 @@ public:
virtual bool hasFramePointer() const { return false; } virtual bool hasFramePointer() const { return false; }
virtual SizeT getFrameOrStackReg() const = 0; virtual SizeT getFrameOrStackReg() const = 0;
virtual size_t typeWidthInBytesOnStack(Type Ty) 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 hasComputedFrame() const { return HasComputedFrame; }
bool shouldDoNopInsertion() const; bool shouldDoNopInsertion() const;
// Returns true if this function calls a function that has the // Returns true if this function calls a function that has the
......
...@@ -4555,6 +4555,9 @@ TargetGlobalInitX8632::TargetGlobalInitX8632(GlobalContext *Ctx) ...@@ -4555,6 +4555,9 @@ TargetGlobalInitX8632::TargetGlobalInitX8632(GlobalContext *Ctx)
: TargetGlobalInitLowering(Ctx) {} : TargetGlobalInitLowering(Ctx) {}
void TargetGlobalInitX8632::lower(const VariableDeclaration &Var) { void TargetGlobalInitX8632::lower(const VariableDeclaration &Var) {
// TODO(jvoung): handle this without text.
if (Ctx->getFlags().UseELFWriter)
return;
Ostream &Str = Ctx->getStrEmit(); Ostream &Str = Ctx->getStrEmit();
......
...@@ -52,11 +52,6 @@ public: ...@@ -52,11 +52,6 @@ public:
// i8, and i16 are rounded up to 4 bytes. // i8, and i16 are rounded up to 4 bytes.
return (typeWidthInBytes(Ty) + 3) & ~3; 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 emitVariable(const Variable *Var) const override;
void lowerArguments() override; void lowerArguments() override;
void addProlog(CfgNode *Node) override; void addProlog(CfgNode *Node) override;
......
...@@ -97,8 +97,13 @@ void Translator::translateFcn(Cfg *Fcn) { ...@@ -97,8 +97,13 @@ void Translator::translateFcn(Cfg *Fcn) {
} }
void Translator::emitConstants() { void Translator::emitConstants() {
if (!Ctx->getFlags().DisableTranslation && Func) if (!Ctx->getFlags().DisableTranslation && Func) {
Func->getTarget()->emitConstants(); if (Ctx->getFlags().UseELFWriter) {
// TODO(jvoung): create the rodata.cst.{4,8} sections for UseELFWriter.
} else {
Func->getTarget()->emitConstants();
}
}
} }
void Translator::lowerGlobals( void Translator::lowerGlobals(
......
...@@ -19,8 +19,8 @@ namespace Ice { ...@@ -19,8 +19,8 @@ namespace Ice {
namespace { namespace {
const char *TargetArchName[] = { const char *TargetArchName[] = {
#define X(tag, str) str, #define X(tag, str, is_elf64, e_machine, e_flags) str,
TARGETARCH_TABLE TARGETARCH_TABLE
#undef X #undef X
}; };
......
...@@ -15,13 +15,18 @@ ...@@ -15,13 +15,18 @@
#ifndef SUBZERO_SRC_ICETYPES_DEF #ifndef SUBZERO_SRC_ICETYPES_DEF
#define SUBZERO_SRC_ICETYPES_DEF #define SUBZERO_SRC_ICETYPES_DEF
#define TARGETARCH_TABLE \ // Attributes of each target architecture.
/* enum value, printable string */ \ // NOTE on is_elf64 -- At some point NaCl would like to use ELF32 for all
X(Target_X8632, "x86-32") \ // ILP32 sandboxes, but for now the 64-bit architectures use ELF64:
X(Target_X8664, "x86-64") \ // https://code.google.com/p/nativeclient/issues/detail?id=349
X(Target_ARM32, "arm32") \ // TODO: Whoever adds AArch64 will need to set ABI e_flags.
X(Target_ARM64, "arm64") \ #define TARGETARCH_TABLE \
//#define X(tag, str) /* 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 \ #define ICETYPE_TABLE \
/* enum value, size, align, # elts, element type, printable string */ \ /* enum value, size, align, # elts, element type, printable string */ \
......
...@@ -28,7 +28,7 @@ enum Type { ...@@ -28,7 +28,7 @@ enum Type {
}; };
enum TargetArch { enum TargetArch {
#define X(tag, str) tag, #define X(tag, str, is_elf64, e_machine, e_flags) tag,
TARGETARCH_TABLE TARGETARCH_TABLE
#undef X #undef X
TargetArch_NUM TargetArch_NUM
......
...@@ -20,7 +20,6 @@ ...@@ -20,7 +20,6 @@
#include "assembler.h" #include "assembler.h"
#include "IceGlobalContext.h" #include "IceGlobalContext.h"
#include "IceMemoryRegion.h"
#include "IceOperand.h" #include "IceOperand.h"
namespace Ice { namespace Ice {
...@@ -76,25 +75,6 @@ AssemblerBuffer::AssemblerBuffer(Assembler &assembler) : assembler_(assembler) { ...@@ -76,25 +75,6 @@ AssemblerBuffer::AssemblerBuffer(Assembler &assembler) : assembler_(assembler) {
AssemblerBuffer::~AssemblerBuffer() {} 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() { void AssemblerBuffer::ExtendCapacity() {
intptr_t old_size = Size(); intptr_t old_size = Size();
intptr_t old_capacity = Capacity(); intptr_t old_capacity = Capacity();
...@@ -123,6 +103,11 @@ void AssemblerBuffer::ExtendCapacity() { ...@@ -123,6 +103,11 @@ void AssemblerBuffer::ExtendCapacity() {
assert(Size() == old_size); 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 { void Assembler::emitIASBytes(GlobalContext *Ctx) const {
Ostream &Str = Ctx->getStrEmit(); Ostream &Str = Ctx->getStrEmit();
intptr_t EndPosition = buffer_.Size(); intptr_t EndPosition = buffer_.Size();
......
...@@ -35,7 +35,6 @@ class Assembler; ...@@ -35,7 +35,6 @@ class Assembler;
class AssemblerFixup; class AssemblerFixup;
class AssemblerBuffer; class AssemblerBuffer;
class ConstantRelocatable; class ConstantRelocatable;
class MemoryRegion;
// Assembler fixups are positions in generated code that hold relocation // Assembler fixups are positions in generated code that hold relocation
// information that needs to be processed before finalizing the code // information that needs to be processed before finalizing the code
...@@ -45,7 +44,6 @@ class AssemblerFixup { ...@@ -45,7 +44,6 @@ class AssemblerFixup {
AssemblerFixup &operator=(const AssemblerFixup &) = delete; AssemblerFixup &operator=(const AssemblerFixup &) = delete;
public: public:
virtual void Process(const MemoryRegion &region, intptr_t position) = 0;
// It would be ideal if the destructor method could be made private, // It would be ideal if the destructor method could be made private,
// but the g++ compiler complains when this is subclassed. // but the g++ compiler complains when this is subclassed.
...@@ -109,12 +107,6 @@ public: ...@@ -109,12 +107,6 @@ public:
intptr_t Size() const { return cursor_ - contents_; } intptr_t Size() const { return cursor_ - contents_; }
uintptr_t contents() const { return 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 // 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 // must be used to guarantee that the underlying data area is big enough to
// hold the emitted instruction. Usage: // hold the emitted instruction. Usage:
...@@ -189,9 +181,6 @@ private: ...@@ -189,9 +181,6 @@ private:
return (limit_ - contents_) + kMinimumGap; 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 // Compute the limit based on the data area and the capacity. See
// description of kMinimumGap for the reasoning behind the value. // description of kMinimumGap for the reasoning behind the value.
static uintptr_t ComputeLimit(uintptr_t data, intptr_t capacity) { static uintptr_t ComputeLimit(uintptr_t data, intptr_t capacity) {
...@@ -226,8 +215,20 @@ public: ...@@ -226,8 +215,20 @@ public:
// Allocate data of type T using the per-Assembler allocator. // Allocate data of type T using the per-Assembler allocator.
template <typename T> T *Allocate() { return Allocator.Allocate<T>(); } 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; 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; void emitIASBytes(GlobalContext *Ctx) const;
private: private:
......
...@@ -20,7 +20,6 @@ ...@@ -20,7 +20,6 @@
#include "assembler_ia32.h" #include "assembler_ia32.h"
#include "IceCfg.h" #include "IceCfg.h"
#include "IceMemoryRegion.h"
#include "IceOperand.h" #include "IceOperand.h"
namespace Ice { namespace Ice {
...@@ -37,13 +36,6 @@ public: ...@@ -37,13 +36,6 @@ public:
DirectCallRelocation(Kind, Sym); 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: private:
DirectCallRelocation(FixupKind Kind, const ConstantRelocatable *Sym) DirectCallRelocation(FixupKind Kind, const ConstantRelocatable *Sym)
: AssemblerFixup(Kind, Sym) {} : AssemblerFixup(Kind, Sym) {}
...@@ -77,6 +69,22 @@ AssemblerX86::~AssemblerX86() { ...@@ -77,6 +69,22 @@ AssemblerX86::~AssemblerX86() {
#endif #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 *AssemblerX86::GetOrCreateLabel(SizeT Number, LabelVector &Labels) {
Label *L = nullptr; Label *L = nullptr;
if (Number == Labels.size()) { if (Number == Labels.size()) {
......
...@@ -56,12 +56,6 @@ public: ...@@ -56,12 +56,6 @@ public:
DisplacementRelocation(Kind, Sym); 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: private:
DisplacementRelocation(FixupKind Kind, const ConstantRelocatable *Sym) DisplacementRelocation(FixupKind Kind, const ConstantRelocatable *Sym)
: AssemblerFixup(Kind, Sym) {} : AssemblerFixup(Kind, Sym) {}
...@@ -374,6 +368,15 @@ public: ...@@ -374,6 +368,15 @@ public:
static const bool kNearJump = true; static const bool kNearJump = true;
static const bool kFarJump = false; 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); Label *GetOrCreateCfgNodeLabel(SizeT NodeNumber);
void BindCfgNodeLabel(SizeT NodeNumber) override; void BindCfgNodeLabel(SizeT NodeNumber) override;
Label *GetOrCreateLocalLabel(SizeT Number); Label *GetOrCreateLocalLabel(SizeT Number);
...@@ -825,10 +828,6 @@ public: ...@@ -825,10 +828,6 @@ public:
intptr_t CodeSize() const { return buffer_.Size(); } intptr_t CodeSize() const { return buffer_.Size(); }
void FinalizeInstructions(const MemoryRegion &region) {
buffer_.FinalizeInstructions(region);
}
private: private:
inline void EmitUint8(uint8_t value); inline void EmitUint8(uint8_t value);
inline void EmitInt16(int16_t value); inline void EmitInt16(int16_t value);
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include "llvm/IR/LLVMContext.h" #include "llvm/IR/LLVMContext.h"
#include "llvm/IRReader/IRReader.h" #include "llvm/IRReader/IRReader.h"
#include "llvm/Support/CommandLine.h" #include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/raw_os_ostream.h" #include "llvm/Support/raw_os_ostream.h"
#include "llvm/Support/SourceMgr.h" #include "llvm/Support/SourceMgr.h"
...@@ -173,6 +174,11 @@ static cl::opt<bool> ...@@ -173,6 +174,11 @@ static cl::opt<bool>
static cl::alias UseIas("ias", cl::desc("Alias for -integrated-as"), static cl::alias UseIas("ias", cl::desc("Alias for -integrated-as"),
cl::NotHidden, cl::aliasopt(UseIntegratedAssembler)); 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( static cl::opt<bool> AlwaysExitSuccess(
"exit-success", cl::desc("Exit with success status, even if errors found"), "exit-success", cl::desc("Exit with success status, even if errors found"),
cl::init(false)); cl::init(false));
...@@ -200,7 +206,7 @@ static struct { ...@@ -200,7 +206,7 @@ static struct {
// Validates values of build attributes. Prints them to Stream if // Validates values of build attributes. Prints them to Stream if
// Stream is non-null. // Stream is non-null.
static void ValidateAndGenerateBuildAttributes(raw_os_ostream *Stream) { static void ValidateAndGenerateBuildAttributes(Ice::Ostream *Stream) {
if (Stream) if (Stream)
*Stream << TargetArch << "\n"; *Stream << TargetArch << "\n";
...@@ -242,25 +248,20 @@ int main(int argc, char **argv) { ...@@ -242,25 +248,20 @@ int main(int argc, char **argv) {
VMask |= VerboseList[i]; 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::ofstream Lfs;
std::unique_ptr<Ice::Ostream> Ls;
if (LogFilename != "-") { if (LogFilename != "-") {
Lfs.open(LogFilename.c_str(), std::ofstream::out); 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(); Ls->SetUnbuffered();
ValidateAndGenerateBuildAttributes(GenerateBuildAtts ? Ls.get() : nullptr);
if (GenerateBuildAtts)
return GetReturnValue(0);
if (!ALLOW_DISABLE_IR_GEN && DisableIRGeneration) { if (!ALLOW_DISABLE_IR_GEN && DisableIRGeneration) {
*Ls << "Error: Build doesn't allow --no-ir-gen when not " *Ls << "Error: Build doesn't allow --no-ir-gen when not "
<< "ALLOW_DISABLE_IR_GEN!\n"; << "ALLOW_DISABLE_IR_GEN!\n";
...@@ -274,6 +275,7 @@ int main(int argc, char **argv) { ...@@ -274,6 +275,7 @@ int main(int argc, char **argv) {
Flags.FunctionSections = FunctionSections; Flags.FunctionSections = FunctionSections;
Flags.DataSections = DataSections; Flags.DataSections = DataSections;
Flags.UseIntegratedAssembler = UseIntegratedAssembler; Flags.UseIntegratedAssembler = UseIntegratedAssembler;
Flags.UseELFWriter = UseELFWriter;
Flags.UseSandboxing = UseSandboxing; Flags.UseSandboxing = UseSandboxing;
Flags.PhiEdgeSplit = EnablePhiEdgeSplit; Flags.PhiEdgeSplit = EnablePhiEdgeSplit;
Flags.DecorateAsm = DecorateAsm; Flags.DecorateAsm = DecorateAsm;
...@@ -294,11 +296,45 @@ int main(int argc, char **argv) { ...@@ -294,11 +296,45 @@ int main(int argc, char **argv) {
LLSuffix.length(), LLSuffix) == 0) LLSuffix.length(), LLSuffix) == 0)
BuildOnRead = false; BuildOnRead = false;
Ice::GlobalContext Ctx(Ls, Os, VMask, TargetArch, OptLevel, TestPrefix, // With the ELF writer, use a raw_fd_ostream to allow seeking.
Flags); // 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); Ice::TimerMarker T(Ice::TimerStack::TT_szmain, &Ctx);
if (UseELFWriter) {
Ctx.getObjectWriter()->writeInitialELFHeader();
}
int ErrorStatus = 0; int ErrorStatus = 0;
if (BuildOnRead) { if (BuildOnRead) {
Ice::PNaClTranslator Translator(&Ctx, Flags); Ice::PNaClTranslator Translator(&Ctx, Flags);
...@@ -324,6 +360,9 @@ int main(int argc, char **argv) { ...@@ -324,6 +360,9 @@ int main(int argc, char **argv) {
<< "--build-on-read=0 not allowed\n"; << "--build-on-read=0 not allowed\n";
return GetReturnValue(1); return GetReturnValue(1);
} }
if (UseELFWriter) {
Ctx.getObjectWriter()->writeNonUserSections();
}
if (SubzeroTimingEnabled) if (SubzeroTimingEnabled)
Ctx.dumpTimers(); Ctx.dumpTimers();
if (TimeEachFunction) { 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