Commit a5b16abf by Jim Stichnoth

Subzero: Add necessary PNaCl files for standalone build.

The README.txt file is new; all other files under pnacl-llvm/ are copied verbatim from the pnacl-llvm repo. BUG= none R=kschimpf@google.com Review URL: https://codereview.chromium.org/1960393002 .
parent f5fdd236
unset(PNACL_LLVM)
# Define PNACL_LLVM for LLVM_VERSION <= 3.7
if((NOT LLVM_VERSION_MAJOR GREATER 3) AND (NOT LLVM_VERSION_MINOR GREATER 7))
set(PNACL_LLVM 1)
endif()
if(PNACL_LLVM)
add_definitions(
-DPNACL_LLVM
)
set(PNACL_EXTRA_COMPONENTS NaClBitReader NaClBitTestUtils)
else()
include_directories(pnacl-llvm/include)
file(GLOB pnacl_llvm_SRCS "pnacl-llvm/*.cpp")
endif()
set(LLVM_LINK_COMPONENTS set(LLVM_LINK_COMPONENTS
${LLVM_TARGETS_TO_BUILD} ${LLVM_TARGETS_TO_BUILD}
Core Core
IRReader IRReader
NaClBitReader
NaClBitTestUtils
Support Support
${PNACL_EXTRA_COMPONENTS}
) )
file(GLOB pnacl_sz_SRCS "src/*.cpp") file(GLOB pnacl_sz_SRCS "src/*.cpp")
add_llvm_tool(pnacl-sz ${pnacl_sz_SRCS}) add_llvm_tool(pnacl-sz ${pnacl_sz_SRCS} ${pnacl_llvm_SRCS})
if(PNACL_BROWSER_TRANSLATOR) if(PNACL_BROWSER_TRANSLATOR)
add_definitions( add_definitions(
...@@ -35,11 +50,4 @@ else() ...@@ -35,11 +50,4 @@ else()
add_compile_options(-Wno-undefined-var-template) add_compile_options(-Wno-undefined-var-template)
endif() endif()
# Define PNACL_LLVM for LLVM_VERSION <= 3.7
if((NOT LLVM_VERSION_MAJOR GREATER 3) AND (NOT LLVM_VERSION_MINOR GREATER 7))
add_definitions(
-DPNACL_LLVM
)
endif()
target_link_libraries(pnacl-sz pthread) target_link_libraries(pnacl-sz pthread)
...@@ -743,6 +743,7 @@ FORMAT_BLACKLIST += ! -name IceParseTypesTest.cpp ...@@ -743,6 +743,7 @@ FORMAT_BLACKLIST += ! -name IceParseTypesTest.cpp
FORMAT_BLACKLIST += ! -name assembler_arm.h FORMAT_BLACKLIST += ! -name assembler_arm.h
FORMAT_BLACKLIST += ! -name assembler_arm.cc FORMAT_BLACKLIST += ! -name assembler_arm.cc
FORMAT_BLACKLIST += ! -path "./wasm-install/*" FORMAT_BLACKLIST += ! -path "./wasm-install/*"
FORMAT_BLACKLIST += ! -path "./pnacl-llvm/*"
format: format:
$(CLANG_FORMAT_PATH)/clang-format -style=LLVM -i \ $(CLANG_FORMAT_PATH)/clang-format -style=LLVM -i \
`find . -regex '.*\.\(c\|h\|cpp\)' $(FORMAT_BLACKLIST)` `find . -regex '.*\.\(c\|h\|cpp\)' $(FORMAT_BLACKLIST)`
......
...@@ -44,6 +44,27 @@ minimize the size of the translator by compiling out everything unnecessary. ...@@ -44,6 +44,27 @@ minimize the size of the translator by compiling out everything unnecessary.
The result of the ``make`` command is the target ``pnacl-sz`` in the current The result of the ``make`` command is the target ``pnacl-sz`` in the current
directory. directory.
Building within LLVM trunk
--------------------------
Subzero can also be built from within a standard LLVM trunk checkout. Here is
an example of how it can be checked out and built::
mkdir llvm-git
cd llvm-git
git clone http://llvm.org/git/llvm.git
cd llvm/projects/
git clone https://chromium.googlesource.com/native_client/pnacl-subzero
cd ../..
mkdir build
cd build
cmake -G Ninja ../llvm/
ninja
./bin/pnacl-sz -version
This creates a default build of ``pnacl-sz``; currently any options such as
``DEBUG=1`` or ``MINIMAL=1`` have to be added manually.
``pnacl-sz`` ``pnacl-sz``
------------ ------------
......
//===- NaClBitcodeHeader.cpp ----------------------------------------------===//
// PNaCl bitcode header reader.
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Implementation of Bitcode abbrevations.
//
//===----------------------------------------------------------------------===//
#include "llvm/Bitcode/NaCl/NaClBitCodes.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/ErrorHandling.h"
using namespace llvm;
const bool NaClBitCodeAbbrevOp::HasValueArray[] = {
true, // Literal
true, // Fixed
true, // VBR
false, // Array
false // Char6
};
const char *NaClBitCodeAbbrevOp::EncodingNameArray[] = {
"Literal",
"Fixed",
"VBR",
"Array",
"Char6"
};
NaClBitCodeAbbrevOp::NaClBitCodeAbbrevOp(Encoding E, uint64_t Data)
: Enc(E), Val(Data) {
if (isValid(E, Data)) return;
std::string Buffer;
raw_string_ostream StrBuf(Buffer);
StrBuf << "Invalid NaClBitCodeAbbrevOp(" << E << ", " << Data << ")";
report_fatal_error(StrBuf.str());
}
bool NaClBitCodeAbbrevOp::isValid(Encoding E, uint64_t Val) {
switch (NaClBitCodeAbbrevOp::Encoding(E)) {
case Literal:
return true;
case Fixed:
case VBR:
return Val <= naclbitc::MaxAbbrevWidth;
case Char6:
case Array:
return Val == 0;
}
llvm_unreachable("unhandled abbreviation");
}
void NaClBitCodeAbbrevOp::Print(raw_ostream& Stream) const {
if (Enc == Literal) {
Stream << getValue();
return;
}
Stream << getEncodingName(Enc);
if (!hasValue())
return;
Stream << "(" << Val << ")";
}
static void PrintExpression(raw_ostream &Stream,
const NaClBitCodeAbbrev *Abbrev,
unsigned &Index) {
// Bail out early, in case we are incrementally building the
// expression and the argument is not available yet.
if (Index >= Abbrev->getNumOperandInfos()) return;
const NaClBitCodeAbbrevOp &Op = Abbrev->getOperandInfo(Index);
Op.Print(Stream);
if (unsigned NumArgs = Op.NumArguments()) {
Stream << "(";
for (unsigned i = 0; i < NumArgs; ++i) {
++Index;
if (i > 0) Stream << ",";
PrintExpression(Stream, Abbrev, Index);
}
Stream << ")";
}
}
void NaClBitCodeAbbrev::Print(raw_ostream &Stream, bool AddNewLine) const {
Stream << "[";
for (unsigned i = 0; i < getNumOperandInfos(); ++i) {
if (i > 0) Stream << ", ";
PrintExpression(Stream, this, i);
}
Stream << "]";
if (AddNewLine) Stream << "\n";
}
NaClBitCodeAbbrev *NaClBitCodeAbbrev::Simplify() const {
NaClBitCodeAbbrev *Abbrev = new NaClBitCodeAbbrev();
for (unsigned i = 0; i < OperandList.size(); ++i) {
const NaClBitCodeAbbrevOp &Op = OperandList[i];
// Simplify if possible. Currently, the only simplification known
// is to remove unnecessary operands appearing immediately before an
// array operator. That is, apply the simplification:
// Op Array(Op) -> Array(Op)
assert(!Op.isArrayOp() || i == OperandList.size()-2);
while (Op.isArrayOp() && !Abbrev->OperandList.empty() &&
Abbrev->OperandList.back() == OperandList[i+1]) {
Abbrev->OperandList.pop_back();
}
Abbrev->OperandList.push_back(Op);
}
return Abbrev;
}
bool NaClBitCodeAbbrev::isValid() const {
// Verify that an array op appears can only appear if it is the
// second to last element.
unsigned NumOperands = getNumOperandInfos();
if (NumOperands == 0) return false;
for (unsigned i = 0; i < NumOperands; ++i) {
const NaClBitCodeAbbrevOp &Op = getOperandInfo(i);
if (Op.isArrayOp() && i + 2 != NumOperands)
// Note: Unlike LLVM bitcode, we allow literals in arrays!
return false;
}
return true;
}
//===- NaClBitcodeDecoders.cpp --------------------------------------------===//
// Internal implementation of decoder functions for PNaCl Bitcode files.
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "llvm/Bitcode/NaCl/NaClBitcodeDecoders.h"
namespace llvm {
namespace naclbitc {
bool DecodeCastOpcode(uint64_t NaClOpcode,
Instruction::CastOps &LLVMOpcode) {
switch (NaClOpcode) {
default:
LLVMOpcode = Instruction::BitCast;
return false;
case naclbitc::CAST_TRUNC:
LLVMOpcode = Instruction::Trunc;
return true;
case naclbitc::CAST_ZEXT:
LLVMOpcode = Instruction::ZExt;
return true;
case naclbitc::CAST_SEXT:
LLVMOpcode = Instruction::SExt;
return true;
case naclbitc::CAST_FPTOUI:
LLVMOpcode = Instruction::FPToUI;
return true;
case naclbitc::CAST_FPTOSI:
LLVMOpcode = Instruction::FPToSI;
return true;
case naclbitc::CAST_UITOFP:
LLVMOpcode = Instruction::UIToFP;
return true;
case naclbitc::CAST_SITOFP:
LLVMOpcode = Instruction::SIToFP;
return true;
case naclbitc::CAST_FPTRUNC:
LLVMOpcode = Instruction::FPTrunc;
return true;
case naclbitc::CAST_FPEXT:
LLVMOpcode = Instruction::FPExt;
return true;
case naclbitc::CAST_BITCAST:
LLVMOpcode = Instruction::BitCast;
return true;
}
}
bool DecodeLinkage(uint64_t NaClLinkage,
GlobalValue::LinkageTypes &LLVMLinkage) {
switch (NaClLinkage) {
default:
LLVMLinkage = GlobalValue::InternalLinkage;
return false;
case naclbitc::LINKAGE_EXTERNAL:
LLVMLinkage = GlobalValue::ExternalLinkage;
return true;
case naclbitc::LINKAGE_INTERNAL:
LLVMLinkage = GlobalValue::InternalLinkage;
return true;
}
}
bool DecodeBinaryOpcode(uint64_t NaClOpcode, Type *Ty,
Instruction::BinaryOps &LLVMOpcode) {
switch (NaClOpcode) {
default:
LLVMOpcode = Instruction::Add;
return false;
case naclbitc::BINOP_ADD:
LLVMOpcode = Ty->isFPOrFPVectorTy() ? Instruction::FAdd : Instruction::Add;
return true;
case naclbitc::BINOP_SUB:
LLVMOpcode = Ty->isFPOrFPVectorTy() ? Instruction::FSub : Instruction::Sub;
return true;
case naclbitc::BINOP_MUL:
LLVMOpcode = Ty->isFPOrFPVectorTy() ? Instruction::FMul : Instruction::Mul;
return true;
case naclbitc::BINOP_UDIV:
LLVMOpcode = Instruction::UDiv;
return true;
case naclbitc::BINOP_SDIV:
LLVMOpcode = Ty->isFPOrFPVectorTy() ? Instruction::FDiv : Instruction::SDiv;
return true;
case naclbitc::BINOP_UREM:
LLVMOpcode = Instruction::URem;
return true;
case naclbitc::BINOP_SREM:
LLVMOpcode = Ty->isFPOrFPVectorTy() ? Instruction::FRem : Instruction::SRem;
return true;
case naclbitc::BINOP_SHL:
LLVMOpcode = Instruction::Shl;
return true;
case naclbitc::BINOP_LSHR:
LLVMOpcode = Instruction::LShr;
return true;
case naclbitc::BINOP_ASHR:
LLVMOpcode = Instruction::AShr;
return true;
case naclbitc::BINOP_AND:
LLVMOpcode = Instruction::And;
return true;
case naclbitc::BINOP_OR:
LLVMOpcode = Instruction::Or;
return true;
case naclbitc::BINOP_XOR:
LLVMOpcode = Instruction::Xor;
return true;
}
}
bool DecodeCallingConv(uint64_t NaClCallingConv,
CallingConv::ID &LLVMCallingConv) {
switch (NaClCallingConv) {
default:
LLVMCallingConv = CallingConv::C;
return false;
case naclbitc::C_CallingConv:
LLVMCallingConv = CallingConv::C;
return true;
}
}
bool DecodeFcmpPredicate(uint64_t NaClPredicate,
CmpInst::Predicate &LLVMPredicate) {
switch (NaClPredicate) {
default:
LLVMPredicate = CmpInst::FCMP_FALSE;
return false;
case naclbitc::FCMP_FALSE:
LLVMPredicate = CmpInst::FCMP_FALSE;
return true;
case naclbitc::FCMP_OEQ:
LLVMPredicate = CmpInst::FCMP_OEQ;
return true;
case naclbitc::FCMP_OGT:
LLVMPredicate = CmpInst::FCMP_OGT;
return true;
case naclbitc::FCMP_OGE:
LLVMPredicate = CmpInst::FCMP_OGE;
return true;
case naclbitc::FCMP_OLT:
LLVMPredicate = CmpInst::FCMP_OLT;
return true;
case naclbitc::FCMP_OLE:
LLVMPredicate = CmpInst::FCMP_OLE;
return true;
case naclbitc::FCMP_ONE:
LLVMPredicate = CmpInst::FCMP_ONE;
return true;
case naclbitc::FCMP_ORD:
LLVMPredicate = CmpInst::FCMP_ORD;
return true;
case naclbitc::FCMP_UNO:
LLVMPredicate = CmpInst::FCMP_UNO;
return true;
case naclbitc::FCMP_UEQ:
LLVMPredicate = CmpInst::FCMP_UEQ;
return true;
case naclbitc::FCMP_UGT:
LLVMPredicate = CmpInst::FCMP_UGT;
return true;
case naclbitc::FCMP_UGE:
LLVMPredicate = CmpInst::FCMP_UGE;
return true;
case naclbitc::FCMP_ULT:
LLVMPredicate = CmpInst::FCMP_ULT;
return true;
case naclbitc::FCMP_ULE:
LLVMPredicate = CmpInst::FCMP_ULE;
return true;
case naclbitc::FCMP_UNE:
LLVMPredicate = CmpInst::FCMP_UNE;
return true;
case naclbitc::FCMP_TRUE:
LLVMPredicate = CmpInst::FCMP_TRUE;
return true;
}
}
bool DecodeIcmpPredicate(uint64_t NaClPredicate,
CmpInst::Predicate &LLVMPredicate) {
switch (NaClPredicate) {
default:
LLVMPredicate = CmpInst::ICMP_EQ;
return false;
case naclbitc::ICMP_EQ:
LLVMPredicate = CmpInst::ICMP_EQ;
return true;
case naclbitc::ICMP_NE:
LLVMPredicate = CmpInst::ICMP_NE;
return true;
case naclbitc::ICMP_UGT:
LLVMPredicate = CmpInst::ICMP_UGT;
return true;
case naclbitc::ICMP_UGE:
LLVMPredicate = CmpInst::ICMP_UGE;
return true;
case naclbitc::ICMP_ULT:
LLVMPredicate = CmpInst::ICMP_ULT;
return true;
case naclbitc::ICMP_ULE:
LLVMPredicate = CmpInst::ICMP_ULE;
return true;
case naclbitc::ICMP_SGT:
LLVMPredicate = CmpInst::ICMP_SGT;
return true;
case naclbitc::ICMP_SGE:
LLVMPredicate = CmpInst::ICMP_SGE;
return true;
case naclbitc::ICMP_SLT:
LLVMPredicate = CmpInst::ICMP_SLT;
return true;
case naclbitc::ICMP_SLE:
LLVMPredicate = CmpInst::ICMP_SLE;
return true;
}
}
}
}
//===- NaClBitcodeParser.cpp ----------------------------------------------===//
// Low-level bitcode driver to parse PNaCl bitcode files.
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "llvm/Bitcode/NaCl/NaClBitcodeParser.h"
using namespace llvm;
void NaClBitcodeRecordData::Print(raw_ostream &os) const {
os << "[" << Code;
for (NaClRecordVector::const_iterator
Iter = Values.begin(), IterEnd = Values.end();
Iter != IterEnd; ++Iter) {
os << ", " << *Iter;
}
os << "]";
}
void NaClBitcodeRecord::Print(raw_ostream& os) const {
Block.Print(os);
os << ", Code " << Data.Code << ", EntryID " << Entry.ID << ", <";
for (unsigned i = 0, e = Data.Values.size(); i != e; ++i) {
if (i > 0) os << " ";
os << Data.Values[i];
}
os << ">";
}
NaClBitcodeBlock::NaClBitcodeBlock(unsigned BlockID,
const NaClBitcodeRecord &Record)
: NaClBitcodeData(Record),
BlockID(BlockID),
EnclosingBlock(&Record.GetBlock()),
LocalStartBit(Record.GetStartBit())
{}
void NaClBitcodeBlock::Print(raw_ostream &os) const {
os << "Block " << BlockID;
}
void NaClBitcodeParserListener::BeginBlockInfoBlock(unsigned NumWords) {
Parser->EnterBlock(NumWords);
}
void NaClBitcodeParserListener::SetBID() {
Parser->Record.SetStartBit(StartBit);
Parser->Record.Entry.Kind = NaClBitstreamEntry::Record;
Parser->Record.Entry.ID = naclbitc::UNABBREV_RECORD;
Parser->Record.Data.Code = naclbitc::BLOCKINFO_CODE_SETBID;
Parser->Record.Data.Values = Values;
GlobalBlockID = Values[0];
Parser->SetBID();
Values.clear();
}
void NaClBitcodeParserListener::EndBlockInfoBlock() {
Parser->Record.SetStartBit(StartBit);
Parser->Record.Entry.Kind = NaClBitstreamEntry::EndBlock;
Parser->Record.Entry.ID = naclbitc::END_BLOCK;
Parser->Record.Data.Code = naclbitc::END_BLOCK;
Parser->Record.Data.Values.clear();
GlobalBlockID = naclbitc::BLOCKINFO_BLOCK_ID;
Parser->ExitBlock();
}
void NaClBitcodeParserListener::
ProcessAbbreviation(NaClBitCodeAbbrev *Abbrev, bool IsLocal) {
Parser->Record.SetStartBit(StartBit);
Parser->Record.Entry.Kind = NaClBitstreamEntry::Record;
Parser->Record.Entry.ID = naclbitc::DEFINE_ABBREV;
Parser->Record.Data.Code = naclbitc::BLK_CODE_DEFINE_ABBREV;
Parser->Record.Data.Values = Values;
Parser->ProcessAbbreviation(IsLocal ? Parser->GetBlockID() : GlobalBlockID,
Abbrev, IsLocal);
}
NaClBitcodeParser::~NaClBitcodeParser() {
if (EnclosingParser) {
EnclosingParser->Block.LocalStartBit += Block.GetNumBits();
}
}
bool NaClBitcodeParser::ErrorAt(
naclbitc::ErrorLevel Level, uint64_t BitPosition,
const std::string &Message) {
naclbitc::ErrorAt(*ErrStream, Level, BitPosition) << Message << "\n";
if (Level == naclbitc::Fatal)
report_fatal_error("Unable to continue");
return true;
}
bool NaClBitcodeParser::Parse() {
Record.ReadEntry();
if (Record.GetEntryKind() != NaClBitstreamEntry::SubBlock)
return Error("Expected block, but not found");
return ParseBlock(Record.GetEntryID());
}
bool NaClBitcodeParser::ParseBlockInfoInternal() {
// BLOCKINFO is a special part of the stream. Let the bitstream
// reader process this block.
bool Result = Record.GetCursor().ReadBlockInfoBlock(Listener);
if (Result) return Error("Malformed BlockInfoBlock");
return Result;
}
bool NaClBitcodeParser::ParseBlockInternal() {
// Regular block. Enter subblock.
unsigned NumWords;
if (Record.GetCursor().EnterSubBlock(GetBlockID(), &NumWords)) {
return Error("Malformed block record");
}
EnterBlock(NumWords);
// Process records.
while (1) {
if (Record.GetCursor().AtEndOfStream())
return Error("Premature end of bitstream");
// Read entry defining type of entry.
Record.ReadEntry();
switch (Record.GetEntryKind()) {
case NaClBitstreamEntry::Error:
return Error("malformed bitcode file");
case NaClBitstreamEntry::EndBlock: {
return false;
}
case NaClBitstreamEntry::SubBlock: {
if (ParseBlock(Record.GetEntryID())) return true;
break;
}
case NaClBitstreamEntry::Record:
// The interesting case.
if (Record.GetEntryID() == naclbitc::DEFINE_ABBREV) {
// Since this abbreviation is local, the listener doesn't
// have the start bit set (it is only set when processing
// the BlockInfo block). Fix this by setting it here.
if (Listener) Listener->StartBit = Record.GetStartBit();
Record.GetCursor().ReadAbbrevRecord(true, Listener);
} else {
// Read in a record.
Record.ReadValues();
ProcessRecord();
}
break;
}
}
return false;
}
The files in this directory are copied verbatim from the PNaCl repository. They
are ultimately needed by PNaClTranslator.cpp.
Individual files are copied from:
https://chromium.googlesource.com/native_client/pnacl-llvm/+/master/include/llvm/Bitcode/NaCl/
https://chromium.googlesource.com/native_client/pnacl-llvm/+/master/lib/Bitcode/NaCl/Reader/
Only the files needed for compiling and linking pnacl-sz are copied.
//===- NaClBitcodeDecoders.h -------------------------------------*- C++ -*-===//
// Functions used to decode values in PNaCl bitcode files.
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This header file provides a public API for value decoders defined in
// the PNaCl bitcode reader.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_BITCODE_NACL_NACLBITCODEDECODERS_H
#define LLVM_BITCODE_NACL_NACLBITCODEDECODERS_H
#include "llvm/IR/CallingConv.h"
#include "llvm/IR/GlobalValue.h"
#include "llvm/IR/InstrTypes.h"
#include "llvm/IR/Instruction.h"
#include "llvm/Bitcode/NaCl/NaClLLVMBitCodes.h"
namespace llvm {
namespace naclbitc {
/// Converts the NaCl (bitcode file) cast opcode to the corresponding
/// LLVM cast opcode. Returns true if the conversion
/// succeeds. Otherwise sets LLVMOpcode to Instruction::BitCast and
/// returns false.
bool DecodeCastOpcode(uint64_t NaClOpcode,
Instruction::CastOps &LLVMOpcode);
/// Converts the NaCl (bitcode file) linkage type to the corresponding
/// LLVM linkage type. Returns true if the conversion
/// succeeds. Otherwise sets LLVMLinkage to
/// GlobalValue::InternalLinkage and returns false.
bool DecodeLinkage(uint64_t NaClLinkage,
GlobalValue::LinkageTypes &LLVMLinkage);
/// Converts the NaCl (bitcode file) binary opcode to the
/// corresponding LLVM binary opcode, assuming that the operator
/// operates on OpType. Returns true if the conversion
/// succeeds. Otherwise sets LLVMOpcode to Instruction::Add and
/// returns false.
bool DecodeBinaryOpcode(uint64_t NaClOpcode, Type *OpType,
Instruction::BinaryOps &LLVMOpcode);
/// Converts the NaCl (bitcode file) calling convention value to the
/// corresponding LLVM calling conventions. Returns true if the
/// conversion succeeds. Otherwise sets LLVMCallingConv to
/// CallingConv::C and returns false.
bool DecodeCallingConv(uint64_t NaClCallingConv,
CallingConv::ID &LLVMCallingConv);
/// Converts the NaCl (bitcode file) float comparison predicate to the
/// corresponding LLVM float comparison predicate. Returns true if the
/// conversion succeeds. Otherwise sets LLVMPredicate to
/// CmpInst::FCMP_FALSE and returns false.
bool DecodeFcmpPredicate(uint64_t NaClPredicate,
CmpInst::Predicate &LLVMPredicate);
/// Converts the NaCl (bitcode file) integer comparison predicate to
/// the corresponding LLVM integer comparison predicate. Returns true
/// if the conversion succeeds. Otherwise sets LLVMPredicate to
/// CmpInst::ICMP_EQ and returns false.
bool DecodeIcmpPredicate(uint64_t NaClPredicate,
CmpInst::Predicate &LLVMPredicate);
}
}
#endif
//===- NaClBitcodeDefs.h ----------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Defines some common types/constants used by bitcode readers and
// writers. It is intended to make clear assumptions made in
// representing bitcode files.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_BITCODE_NACL_NACLBITCODEDEFS_H
#define LLVM_BITCODE_NACL_NACLBITCODEDEFS_H
namespace llvm {
namespace naclbitc {
// Special record codes used to model codes for predefined records.
// They are very large so that they do not conflict with existing
// record codes for user-defined blocks.
enum SpecialBlockCodes {
BLK_CODE_ENTER = 65535,
BLK_CODE_EXIT = 65534,
BLK_CODE_DEFINE_ABBREV = 65533,
BLK_CODE_HEADER = 65532
};
} // end of namespace naclbitc
/// Defines type for value indicies in bitcode. Like a size_t, but
/// fixed across platforms.
typedef uint32_t NaClBcIndexSize_t;
/// Signed version of NaClBcIndexSize_t. Used to define relative indices.
typedef int32_t NaClRelBcIndexSize_t;
/// Defines maximum allowed bitcode index in bitcode files.
static const size_t NaClBcIndexSize_t_Max =
std::numeric_limits<NaClBcIndexSize_t>::max();
/// Defines the maximum number of initializers allowed, based on ILP32.
static const size_t MaxNaClGlobalVarInits =
std::numeric_limits<uint32_t>::max();
} // end of namespace llvm
#endif // LLVM_BITCODE_NACL_NACLBITCODEDEFS_H
//===-- llvm/Bitcode/NaCl/NaClBitcodeHeader.h - ----------------*- C++ -*-===//
// NaCl Bitcode header reader.
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This header defines interfaces to read and write NaCl bitcode wire format
// file headers.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_BITCODE_NACL_NACLBITCODEHEADER_H
#define LLVM_BITCODE_NACL_NACLBITCODEHEADER_H
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/DataTypes.h"
#include <string>
#include <vector>
namespace llvm {
class MemoryObject;
// Class representing a variable-size metadata field in the bitcode header.
// Also contains the list of known (typed) Tag IDs.
//
// The serialized format has 2 fixed subfields (ID:type and data length) and the
// variable-length data subfield
class NaClBitcodeHeaderField {
NaClBitcodeHeaderField(const NaClBitcodeHeaderField &) = delete;
void operator=(const NaClBitcodeHeaderField &) = delete;
public:
// Defines the ID associated with the value. Valid values are in
// {0x0, ..., 0xFFF}
typedef enum {
kInvalid = 0, // KUnknownType.
kPNaClVersion = 1, // kUint32Type.
kAlignBitcodeRecords = 2, // kFlagType.
kTag_MAX = kAlignBitcodeRecords
} Tag;
// Defines the type of value.
typedef enum {
kBufferType, // Buffer of form uint8_t[len].
kUInt32Type,
kFlagType,
kUnknownType,
kFieldType_MAX = kUnknownType
} FieldType;
// Defines the number of bytes in a (32-bit) word.
static const int WordSize = 4;
// Defines the encoding of the fixed fields {i.e. ID:type and data length).
typedef uint16_t FixedSubfield;
// Create an invalid header field.
NaClBitcodeHeaderField();
// Creates a header field where MyID is a flag.
NaClBitcodeHeaderField(Tag MyID);
// Create a header field with an uint32_t value.
NaClBitcodeHeaderField(Tag MyID, uint32_t value);
// Create a header field for the given data.
NaClBitcodeHeaderField(Tag MyID, size_t MyLen, uint8_t *MyData);
virtual ~NaClBitcodeHeaderField() {
if (Data)
delete[] Data;
}
/// \brief Number of bytes used to represent header field.
size_t GetTotalSize() const {
// Round up to 4 byte alignment
return (kTagLenSize + Len + (WordSize - 1)) & ~(WordSize - 1);
}
/// \brief Write field into Buf[BufLen].
bool Write(uint8_t *Buf, size_t BufLen) const;
/// \brief Read field from Buf[BufLen].
bool Read(const uint8_t *Buf, size_t BufLen);
/// \brief Returns string describing ID of field.
static const char *IDName(Tag ID);
const char *IDName() const {
return IDName(ID);
}
/// \brief Returns string describing type of field.
static const char *TypeName(FieldType FType);
const char *TypeName() const {
return TypeName(FType);
}
/// \brief Returns string describing field.
std::string Contents() const;
/// \brief Get the data size from a serialized field to allow allocation.
static size_t GetDataSizeFromSerialized(const uint8_t *Buf) {
FixedSubfield Length;
ReadFixedSubfield(&Length, Buf + sizeof(FixedSubfield));
return Length;
}
/// \brief Return the ID of the field.
Tag GetID() const { return ID; }
FieldType GetType() const { return FType; }
/// \brief Return the length of the data (in bytes).
size_t GetLen() const { return Len; }
/// \brief Return the data. Data is array getData()[getLen()].
const uint8_t *GetData() const { return Data; }
/// \brief Returns the uint32_t value stored. Requires that
/// getType() == kUint32Type
uint32_t GetUInt32Value() const;
private:
// Convert ID:Type into a fixed subfield
FixedSubfield EncodeTypedID() const { return (ID << 4) | FType; }
// Extract out ID and Type from a fixed subfield.
void DecodeTypedID(FixedSubfield Subfield, Tag &ID, FieldType &FType) {
FixedSubfield PossibleID = Subfield >> 4;
ID = (PossibleID > kTag_MAX ? kInvalid : static_cast<Tag>(PossibleID));
FixedSubfield PossibleFType = Subfield & 0xF;
FType = (PossibleFType > kFieldType_MAX
? kUnknownType : static_cast<FieldType>(PossibleFType));
}
// Combined size of the fixed subfields
const static size_t kTagLenSize = 2 * sizeof(FixedSubfield);
static void WriteFixedSubfield(FixedSubfield Value, uint8_t *Buf) {
Buf[0] = Value & 0xFF;
Buf[1] = (Value >> 8) & 0xFF;
}
static void ReadFixedSubfield(FixedSubfield *Value, const uint8_t *Buf) {
*Value = Buf[0] | Buf[1] << 8;
}
Tag ID;
FieldType FType;
size_t Len;
uint8_t *Data;
};
/// \brief Class holding parsed header fields in PNaCl bitcode file.
class NaClBitcodeHeader {
NaClBitcodeHeader(const NaClBitcodeHeader &) = delete;
void operator=(const NaClBitcodeHeader &) = delete;
// The set of parsed header fields. The header takes ownership of
// all fields in this vector.
std::vector<NaClBitcodeHeaderField *> Fields;
// The number of bytes in the PNaCl header.
size_t HeaderSize;
// String defining why it is unsupported (if unsupported).
std::string UnsupportedMessage;
// Flag defining if header is supported.
bool IsSupportedFlag;
// Flag defining if the corresponding bitcode file is readable.
bool IsReadableFlag;
// Defines the PNaCl version defined by the header file.
uint32_t PNaClVersion;
// Byte align bitcode records when nonzero.
bool AlignBitcodeRecords = false;
public:
static const int WordSize = NaClBitcodeHeaderField::WordSize;
NaClBitcodeHeader();
~NaClBitcodeHeader();
/// \brief Installs the fields of the header, defining if the header
/// is readable and supported. Sets UnsupportedMessage on failure.
void InstallFields();
/// \brief Adds a field to the list of fields in a header. Takes ownership
/// of fields added.
void push_back(NaClBitcodeHeaderField *Field) {
Fields.push_back(Field);
}
/// \brief Read the PNaCl bitcode header, The format of the header is:
///
/// 1) 'PEXE' - The four character sequence defining the magic number.
/// 2) uint_16 num_fields - The number of NaClBitcodeHeaderField's.
/// 3) uint_16 num_bytes - The number of bytes to hold fields in
/// the header.
/// 4) NaClBitcodeHeaderField f1 - The first bitcode header field.
/// ...
/// 2 + num_fields) NaClBitcodeHeaderField fn - The last bitcode header
/// field.
///
/// Returns false if able to read (all of) the bitcode header.
bool Read(const unsigned char *BufPtr, const unsigned char *BufEnd);
// \brief Read the PNaCl bitcode header, recording the fields found
// in the header. Returns false if able to read (all of) the bitcode header.
bool Read(MemoryObject *Bytes);
// \brief Returns the number of bytes read to consume the header.
size_t getHeaderSize() { return HeaderSize; }
/// \brief Returns string describing why the header describes
/// an unsupported PNaCl Bitcode file.
const std::string &Unsupported() const { return UnsupportedMessage; }
/// \brief Returns true if supported. That is, it can be run in the
/// browser.
bool IsSupported() const { return IsSupportedFlag; }
/// \brief Returns true if the bitcode file should be readable. Note
/// that just because it is readable, it doesn't necessarily mean that
/// it is supported.
bool IsReadable() const { return IsReadableFlag; }
/// \brief Returns number of fields defined.
size_t NumberFields() const { return Fields.size(); }
/// \brief Returns a pointer to the field with the given ID
/// (0 if no such field).
NaClBitcodeHeaderField *GetTaggedField(NaClBitcodeHeaderField::Tag ID) const;
/// \brief Returns a pointer to the Nth field in the header
/// (0 if no such field).
NaClBitcodeHeaderField *GetField(size_t index) const;
/// \brief Returns the PNaClVersion, as defined by the header.
uint32_t GetPNaClVersion() const { return PNaClVersion; }
/// \brief Returns if one should byte align bitcode records.
bool getAlignBitcodeRecords() const { return AlignBitcodeRecords; }
private:
// Reads and verifies the first 8 bytes of the header, consisting
// of the magic number 'PEXE', and the value defining the number
// of fields and number of bytes used to hold fields.
// Returns false if successful, sets UnsupportedMessage otherwise.
bool ReadPrefix(const unsigned char *BufPtr, const unsigned char *BufEnd,
unsigned &NumFields, unsigned &NumBytes);
// Reads and verifies the fields in the header.
// Returns false if successful, sets UnsupportedMessage otherwise.
bool ReadFields(const unsigned char *BufPtr, const unsigned char *BufEnd,
unsigned NumFields, unsigned NumBytes);
// Sets the Unsupported error message and returns true.
bool UnsupportedError(StringRef Message) {
UnsupportedMessage = Message.str();
return true;
}
};
} // namespace llvm
#endif
//===-- llvm/Bitcode/NaCl/NaClReaderWriter.h - ------------------*- C++ -*-===//
// NaCl Bitcode reader/writer.
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This header defines interfaces to read and write NaCl bitcode wire format
// files.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_BITCODE_NACL_NACLREADERWRITER_H
#define LLVM_BITCODE_NACL_NACLREADERWRITER_H
#include "llvm/IR/DiagnosticInfo.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/ErrorOr.h"
#include "llvm/Support/MemoryBuffer.h"
#include <string>
namespace llvm {
class LLVMContext;
class Module;
class NaClBitcodeHeader;
class NaClBitstreamWriter;
class StreamingMemoryObject;
class raw_ostream;
/// Defines the data layout used for PNaCl bitcode files. We set the
/// data layout of the module in the bitcode readers rather than in
/// pnacl-llc so that 'opt' will also use the correct data layout if
/// it is run on a pexe.
extern const char *PNaClDataLayout;
/// Allows (function) local symbol tables (unsupported) in PNaCl bitcode
/// files.
extern cl::opt<bool> PNaClAllowLocalSymbolTables;
/// \brief Defines the integer bit size used to model pointers in PNaCl.
static const unsigned PNaClIntPtrTypeBitSize = 32;
/// Diagnostic handler that redirects error diagnostics to the given stream.
DiagnosticHandlerFunction redirectNaClDiagnosticToStream(raw_ostream &Out);
/// Read the header of the specified bitcode buffer and prepare for lazy
/// deserialization of function bodies. If successful, this takes ownership
/// of 'Buffer' (extending its lifetime). On error, this returns an error
/// code and deletes Buffer.
///
/// The AcceptSupportedOnly argument is used to decide which PNaCl versions
/// of the PNaCl bitcode to accept. There are three forms:
/// 1) Readable and supported.
/// 2) Readable and unsupported. Allows testing of code before becoming
/// supported, as well as running experiments on the bitcode format.
/// 3) Unreadable.
/// When AcceptSupportedOnly is true, only form 1 is allowed. When
/// AcceptSupportedOnly is false, forms 1 and 2 are allowed.
ErrorOr<Module *> getNaClLazyBitcodeModule(
std::unique_ptr<MemoryBuffer> &&Buffer, LLVMContext &Context,
DiagnosticHandlerFunction DiagnosticHandler = nullptr,
bool AcceptSupportedOnly = true);
/// Read the header of the specified stream and prepare for lazy
/// deserialization and streaming of function bodies. On error,
/// this returns null, and fills in *ErrMsg with an error description
/// if ErrMsg is non-null.
///
/// See getNaClLazyBitcodeModule for an explanation of argument
/// AcceptSupportedOnly.
/// TODO(kschimpf): Refactor this and getStreamedBitcodeModule to use
/// ErrorOr<Module *> API so that all methods have the same interface.
Module *getNaClStreamedBitcodeModule(
const std::string &name, StreamingMemoryObject *streamer,
LLVMContext &Context,
DiagnosticHandlerFunction DiagnosticHandler = nullptr,
std::string *ErrMsg = nullptr, bool AcceptSupportedOnly = true);
/// Read the bitcode file from a buffer, returning the module.
///
/// See getNaClLazyBitcodeModule for an explanation of argument
/// AcceptSupportedOnly.
ErrorOr<Module *>
NaClParseBitcodeFile(MemoryBufferRef Buffer, LLVMContext &Context,
DiagnosticHandlerFunction DiagnosticHandler = nullptr,
bool AcceptSupportedOnly = true);
/// Read the textual bitcode records in Filename, returning the module.
/// Note: If Filename is "-", stdin will be read.
///
/// TODO(kschimpf) Replace Verbose argument with a DiagnosticHandlerFunction.
ErrorOr<Module *> parseNaClBitcodeText(const std::string &Filename,
LLVMContext &Context,
raw_ostream *Verbose = nullptr);
/// Write the specified module to the specified raw output stream, using
/// PNaCl wire format. For streams where it matters, the given stream
/// should be in "binary" mode.
///
/// The AcceptSupportedOnly argument is used to decide which PNaCl versions
/// of the PNaCl bitcode to generate. There are two forms:
/// 1) Writable and supported.
/// 2) Writable and unsupported. Allows testing of code before becoming
/// supported, as well as running experiments on the bitcode format.
/// When AcceptSupportedOnly is true, only form 1 is allowed. When
/// AcceptSupportedOnly is false, forms 1 and 2 are allowed.
void NaClWriteBitcodeToFile(const Module *M, raw_ostream &Out,
bool AcceptSupportedOnly = true);
/// isNaClBitcode - Return true if the given bytes are the magic bytes for
/// PNaCl bitcode wire format.
///
inline bool isNaClBitcode(const unsigned char *BufPtr,
const unsigned char *BufEnd) {
return BufPtr+4 <= BufEnd &&
BufPtr[0] == 'P' &&
BufPtr[1] == 'E' &&
BufPtr[2] == 'X' &&
BufPtr[3] == 'E';
}
/// NaClWriteHeader - Generate a default header (using the version
/// number defined by kPNaClVersion) and write to the corresponding
/// bitcode stream.
void NaClWriteHeader(NaClBitstreamWriter &Stream, bool AcceptSupportedOnly);
// NaClWriteHeader - Write the contents of the bitcode header to the
// corresponding bitcode stream.
void NaClWriteHeader(const NaClBitcodeHeader &Header,
NaClBitstreamWriter &Stream);
/// NaClObjDump - Read PNaCl bitcode file from input, and print a
/// textual representation of its contents. NoRecords and NoAssembly
/// define what should not be included in the dump.
bool NaClObjDump(MemoryBufferRef Input, raw_ostream &output,
bool NoRecords, bool NoAssembly);
} // end llvm namespace
#endif
...@@ -263,7 +263,7 @@ template <template <typename> class AT> class BitVectorTmpl { ...@@ -263,7 +263,7 @@ template <template <typename> class AT> class BitVectorTmpl {
uint64_t alignTo(uint64_t Value, uint64_t Align) { uint64_t alignTo(uint64_t Value, uint64_t Align) {
#ifdef PNACL_LLVM #ifdef PNACL_LLVM
return llvm::RoundUpToAlignment(Value, Align); return llvm::RoundUpToAlignment(Value, Align);
#else // !PNACL_LLVM #else // !PNACL_LLVM
return llvm::alignTo(Value, Align); return llvm::alignTo(Value, Align);
#endif // !PNACL_LLVM #endif // !PNACL_LLVM
} }
......
...@@ -24,7 +24,9 @@ ...@@ -24,7 +24,9 @@
#pragma clang diagnostic ignored "-Wunused-parameter" #pragma clang diagnostic ignored "-Wunused-parameter"
#endif // __clang__ #endif // __clang__
#ifdef PNACL_LLVM
#include "llvm/Bitcode/NaCl/NaClBitcodeMungeUtils.h" #include "llvm/Bitcode/NaCl/NaClBitcodeMungeUtils.h"
#endif // PNACL_LLVM
#include "llvm/Support/FileSystem.h" #include "llvm/Support/FileSystem.h"
#include "llvm/Support/raw_os_ostream.h" #include "llvm/Support/raw_os_ostream.h"
#include "llvm/Support/Signals.h" #include "llvm/Support/Signals.h"
...@@ -52,11 +54,10 @@ public: ...@@ -52,11 +54,10 @@ public:
~TextDataStreamer() final = default; ~TextDataStreamer() final = default;
#ifdef PNACL_LLVM #ifdef PNACL_LLVM
using CreateType = TextDataStreamer *; using CreateType = TextDataStreamer *;
#else // !PNACL_LLVM #else // !PNACL_LLVM
using CreateType = std::unique_ptr<TextDataStreamer>; using CreateType = std::unique_ptr<TextDataStreamer>;
#endif // !PNACL_LLVM #endif // !PNACL_LLVM
static CreateType create(const std::string &Filename, static CreateType create(const std::string &Filename, std::string *Err);
std::string *Err);
size_t GetBytes(unsigned char *Buf, size_t Len) final; size_t GetBytes(unsigned char *Buf, size_t Len) final;
private: private:
...@@ -64,8 +65,8 @@ private: ...@@ -64,8 +65,8 @@ private:
size_t Cursor = 0; size_t Cursor = 0;
}; };
TextDataStreamer::CreateType TextDataStreamer::create(const std::string &Filename, TextDataStreamer::CreateType
std::string *Err) { TextDataStreamer::create(const std::string &Filename, std::string *Err) {
#ifdef PNACL_LLVM #ifdef PNACL_LLVM
TextDataStreamer *Streamer = new TextDataStreamer(); TextDataStreamer *Streamer = new TextDataStreamer();
llvm::raw_string_ostream ErrStrm(*Err); llvm::raw_string_ostream ErrStrm(*Err);
...@@ -78,7 +79,7 @@ TextDataStreamer::CreateType TextDataStreamer::create(const std::string &Filenam ...@@ -78,7 +79,7 @@ TextDataStreamer::CreateType TextDataStreamer::create(const std::string &Filenam
} }
ErrStrm.flush(); ErrStrm.flush();
return Streamer; return Streamer;
#else // !PNACL_LLVM #else // !PNACL_LLVM
return CreateType(); return CreateType();
#endif // !PNACL_LLVM #endif // !PNACL_LLVM
} }
......
...@@ -87,9 +87,11 @@ void Compiler::run(const Ice::ClFlags &Flags, GlobalContext &Ctx, ...@@ -87,9 +87,11 @@ void Compiler::run(const Ice::ClFlags &Flags, GlobalContext &Ctx,
if (BuildOnRead) { if (BuildOnRead) {
std::unique_ptr<PNaClTranslator> PTranslator(new PNaClTranslator(&Ctx)); std::unique_ptr<PNaClTranslator> PTranslator(new PNaClTranslator(&Ctx));
#ifdef PNACL_LLVM #ifdef PNACL_LLVM
std::unique_ptr<llvm::StreamingMemoryObject> MemObj(new llvm::StreamingMemoryObjectImpl(InputStream.release())); std::unique_ptr<llvm::StreamingMemoryObject> MemObj(
#else // !PNACL_LLVM new llvm::StreamingMemoryObjectImpl(InputStream.release()));
std::unique_ptr<llvm::StreamingMemoryObject> MemObj(new llvm::StreamingMemoryObject(std::move(InputStream))); #else // !PNACL_LLVM
std::unique_ptr<llvm::StreamingMemoryObject> MemObj(
new llvm::StreamingMemoryObject(std::move(InputStream)));
#endif // !PNACL_LLVM #endif // !PNACL_LLVM
PTranslator->translate(IRFilename, std::move(MemObj)); PTranslator->translate(IRFilename, std::move(MemObj));
Translator.reset(PTranslator.release()); Translator.reset(PTranslator.release());
...@@ -131,11 +133,10 @@ void Compiler::run(const Ice::ClFlags &Flags, GlobalContext &Ctx, ...@@ -131,11 +133,10 @@ void Compiler::run(const Ice::ClFlags &Flags, GlobalContext &Ctx,
std::unique_ptr<llvm::Module> Mod = std::unique_ptr<llvm::Module> Mod =
NaClParseIRFile(IRFilename, Flags.getInputFileFormat(), Err, NaClParseIRFile(IRFilename, Flags.getInputFileFormat(), Err,
llvm::getGlobalContext(), DiagnosticHandler); llvm::getGlobalContext(), DiagnosticHandler);
#else // !PNACL_LLVM #else // !PNACL_LLVM
llvm::DiagnosticHandlerFunction DiagnosticHandler = nullptr; llvm::DiagnosticHandlerFunction DiagnosticHandler = nullptr;
llvm::LLVMContext Context; llvm::LLVMContext Context;
std::unique_ptr<llvm::Module> Mod = std::unique_ptr<llvm::Module> Mod = parseIRFile(IRFilename, Err, Context);
parseIRFile(IRFilename, Err, Context);
#endif // !PNACL_LLVM #endif // !PNACL_LLVM
if (!Mod) { if (!Mod) {
Err.print(Flags.getAppName().c_str(), llvm::errs()); Err.print(Flags.getAppName().c_str(), llvm::errs());
......
...@@ -1136,14 +1136,12 @@ namespace Ice { ...@@ -1136,14 +1136,12 @@ namespace Ice {
inline InstList::iterator instToIterator(Inst *Instr) { inline InstList::iterator instToIterator(Inst *Instr) {
#ifdef PNACL_LLVM #ifdef PNACL_LLVM
return Instr; return Instr;
#else // !PNACL_LLVM #else // !PNACL_LLVM
return Instr->getIterator(); return Instr->getIterator();
#endif // !PNACL_LLVM #endif // !PNACL_LLVM
} }
inline Inst *iteratorToInst(InstList::iterator Iter) { inline Inst *iteratorToInst(InstList::iterator Iter) { return &*Iter; }
return &*Iter;
}
inline const Inst *iteratorToInst(InstList::const_iterator Iter) { inline const Inst *iteratorToInst(InstList::const_iterator Iter) {
return &*Iter; return &*Iter;
......
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