Commit f7c9a141 by Jim Stichnoth

Initial skeleton of Subzero.

This includes just enough code to build the high-level ICE IR and dump it back out again. There is a script szdiff.py that does a fuzzy diff of the input and output for verification. See the comment in szdiff.py for a description of the fuzziness. Building llvm2ice requires LLVM headers, libs, and tools (e.g. FileCheck) to be present. These default to something like llvm_i686_linux_work/Release+Asserts/ based on the checked-out and built pnacl-llvm code; I'll try to figure out how to more automatically detect the build configuration. "make check" runs the lit tests. This CL has under 2000 lines of "interesting" Ice*.{h,cpp} code, plus 600 lines of llvm2ice.cpp driver code, and the rest is tests. Here is the high-level mapping of source files to functionality: IceDefs.h, IceTypes.h, IceTypes.cpp: Commonly used types and utilities. IceCfg.h, IceCfg.cpp: Operations at the function level. IceCfgNode.h, IceCfgNode.cpp: Operations on basic blocks (nodes). IceInst.h, IceInst.cpp: Operations on instructions. IceOperand.h, IceOperand.cpp: Operations on operands, such as stack locations, physical registers, and constants. BUG= none R=jfb@chromium.org Review URL: https://codereview.chromium.org/205613002
parent db7bd0b5
# Ignore filename patterns wherever they appear
*~
*.o
*.orig
*.pyc
*.swp
.#*
\#*
# Ignore specific patterns at the top-level directory
/llvm2ice
/build/
==============================================================================
Subzero Release License
==============================================================================
University of Illinois/NCSA
Open Source License
Copyright (c) 2014 Google Inc.
All rights reserved.
Developed by:
Native Client Team
Google Inc.
http://www.chromium.org/nativeclient
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal with
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimers.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimers in the
documentation and/or other materials provided with the distribution.
* Neither the names of the Native Client Team, Google Inc., nor the names of
its contributors may be used to endorse or promote products derived from
this Software without specific prior written permission.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
SOFTWARE.
# The following variables will likely need to be modified, depending on where
# and how you built LLVM & Clang. They can be overridden in a command-line
# invocation of make, like:
#
# make LLVM_SRC_PATH=<path> LLVM_BIN_PATH=<path> ...
#
# LLVM_SRC_PATH is the path to the root of the checked out source code. This
# directory should contain the configure script, the include/ and lib/
# directories of LLVM, Clang in tools/clang/, etc.
# Alternatively, if you're building vs. a binary download of LLVM, then
# LLVM_SRC_PATH can point to the main untarred directory.
LLVM_SRC_PATH ?= ../llvm
# LLVM_BIN_PATH is the directory where binaries are placed by the LLVM build
# process. It should contain the tools like opt, llc and clang. The default
# reflects a debug build with autotools (configure & make).
LLVM_BIN_PATH ?= $(shell readlink -e \
../../out/llvm_i686_linux_work/Release+Asserts/bin)
$(info -----------------------------------------------)
$(info Using LLVM_SRC_PATH = $(LLVM_SRC_PATH))
$(info Using LLVM_BIN_PATH = $(LLVM_BIN_PATH))
$(info -----------------------------------------------)
LLVM_CXXFLAGS := `$(LLVM_BIN_PATH)/llvm-config --cxxflags`
LLVM_LDFLAGS := `$(LLVM_BIN_PATH)/llvm-config --ldflags --libs`
# It's recommended that CXX matches the compiler you used to build LLVM itself.
OPTLEVEL := -O0
CXX := g++
CXXFLAGS := -Wall -Werror -fno-rtti -fno-exceptions \
$(OPTLEVEL) -g $(LLVM_CXXFLAGS) -m32
LDFLAGS := -m32
SRCS= \
IceCfg.cpp \
IceCfgNode.cpp \
IceGlobalContext.cpp \
IceInst.cpp \
IceOperand.cpp \
IceTypes.cpp \
llvm2ice.cpp
OBJS=$(patsubst %.cpp, build/%.o, $(SRCS))
# Keep all the first target so it's the default.
all: llvm2ice
.PHONY: all
llvm2ice: $(OBJS)
$(CXX) $(LDFLAGS) -o $@ $^ $(LLVM_LDFLAGS) -ldl
# TODO: Be more precise than "*.h" here and elsewhere.
$(OBJS): build/%.o: src/%.cpp src/*.h src/*.def
$(CXX) -c $(CXXFLAGS) $< -o $@
$(OBJS): | build
build:
@mkdir -p $@
check: llvm2ice
LLVM_BIN_PATH=$(LLVM_BIN_PATH) \
$(LLVM_SRC_PATH)/utils/lit/lit.py -sv tests_lit
# TODO: Fix the use of wildcards.
format:
$(LLVM_BIN_PATH)/clang-format -style=LLVM -i \
src/Ice*.h src/Ice*.cpp src/llvm2ice.cpp
clean:
rm -rf llvm2ice *.o build/
Subzero - Fast code generator for PNaCl bitcode
===============================================
Building
--------
You must have LLVM trunk source code available and built. See
http://llvm.org/docs/GettingStarted.html#getting-started-quickly-a-summary for
guidance.
Set variables ``LLVM_SRC_PATH`` and ``LLVM_BIN_PATH`` to point to the
appropriate directories in the LLVM source and build directories. These can be
set as environment variables, or you can modify the top-level Makefile.
Run ``make`` at the top level to build the main target ``llvm2ice``.
``llvm2ice``
------------
The ``llvm2ice`` program uses the LLVM infrastructure to parse an LLVM bitcode
file and translate it into ICE. It then invokes ICE's translate method to lower
it to target-specific machine code, dumping the IR at various stages of the
translation.
The program can be run as follows::
../llvm2ice ./ir_samples/<file>.ll
../llvm2ice ./tests_lit/llvm2ice_tests/<file>.ll
At this time, ``llvm2ice`` accepts a few arguments:
``-help`` -- Show available arguments and possible values.
``-notranslate`` -- Suppress the ICE translation phase, which is useful if
ICE is missing some support.
``-target=<TARGET>`` -- Set the target architecture. The default is x8632,
and x8632fast (generate x8632 code as fast as possible at the cost of code
quality) is also available. Future targets include x8664, arm32, and arm64.
``-verbose=<list>`` -- Set verbosity flags. This argument allows a
comma-separated list of values. The default is ``none``, and the value
``inst,pred`` will roughly match the .ll bitcode file. Of particular use
are ``all`` and ``none``.
See ir_samples/README.rst for more details.
Running the test suite
----------------------
Subzero uses the LLVM ``lit`` testing tool for its test suite, which lives in
``tests_lit``. To execute the test suite, first build Subzero, and then run::
python <path_to_lit.py> -sv tests_lit
``path_to_lit`` is the direct path to the lit script in the LLVM source
(``$LLVM_SRC_PATH/utils/lit/lit.py``).
The above ``lit`` execution also needs the LLVM binary path in the
``LLVM_BIN_PATH`` env var.
Assuming the LLVM paths are set up, ``make check`` is a convenient way to run
the test suite.
Assembling ``llvm2ice`` output
------------------------------
Currently ``llvm2ice`` produces textual assembly code in a structure suitable
for input to ``llvm-mc`` and currently using "intel" assembly syntax. The first
line of output is a convenient comment indicating how to pipe the output to
``llvm-mc`` to produce object code.
//===- subzero/src/IceCfg.cpp - Control flow graph implementation ---------===//
//
// The Subzero Code Generator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements the Cfg class, including constant pool
// management.
//
//===----------------------------------------------------------------------===//
#include "IceCfg.h"
#include "IceCfgNode.h"
#include "IceDefs.h"
#include "IceInst.h"
#include "IceOperand.h"
namespace Ice {
Cfg::Cfg(GlobalContext *Ctx)
: Ctx(Ctx), FunctionName(""), ReturnType(IceType_void),
IsInternalLinkage(false), HasError(false), ErrorMessage(""), Entry(NULL),
NextInstNumber(1), CurrentNode(NULL) {}
Cfg::~Cfg() {}
void Cfg::setError(const IceString &Message) {
HasError = true;
ErrorMessage = Message;
Ctx->getStrDump() << "ICE translation error: " << ErrorMessage << "\n";
}
CfgNode *Cfg::makeNode(const IceString &Name) {
SizeT LabelIndex = Nodes.size();
CfgNode *Node = CfgNode::create(this, LabelIndex, Name);
Nodes.push_back(Node);
return Node;
}
// Create a new Variable with a particular type and an optional
// name. The Node argument is the node where the variable is defined.
Variable *Cfg::makeVariable(Type Ty, const CfgNode *Node,
const IceString &Name) {
SizeT Index = Variables.size();
Variables.push_back(Variable::create(this, Ty, Node, Index, Name));
return Variables[Index];
}
void Cfg::addArg(Variable *Arg) {
Arg->setIsArg(this);
Args.push_back(Arg);
}
void Cfg::computePredecessors() {
for (NodeList::iterator I = Nodes.begin(), E = Nodes.end(); I != E; ++I) {
(*I)->computePredecessors();
}
}
// ======================== Dump routines ======================== //
void Cfg::dump() {
Ostream &Str = Ctx->getStrDump();
setCurrentNode(getEntryNode());
// Print function name+args
if (getContext()->isVerbose(IceV_Instructions)) {
Str << "define ";
if (getInternal())
Str << "internal ";
Str << ReturnType << " @" << getFunctionName() << "(";
for (SizeT i = 0; i < Args.size(); ++i) {
if (i > 0)
Str << ", ";
Str << Args[i]->getType() << " ";
Args[i]->dump(this);
}
Str << ") {\n";
}
setCurrentNode(NULL);
// Print each basic block
for (NodeList::const_iterator I = Nodes.begin(), E = Nodes.end(); I != E;
++I) {
(*I)->dump(this);
}
if (getContext()->isVerbose(IceV_Instructions)) {
Str << "}\n";
}
}
} // end of namespace Ice
//===- subzero/src/IceCfg.h - Control flow graph ----------------*- C++ -*-===//
//
// The Subzero Code Generator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file declares the Cfg class, which represents the control flow
// graph and the overall per-function compilation context.
//
//===----------------------------------------------------------------------===//
#ifndef SUBZERO_SRC_ICECFG_H
#define SUBZERO_SRC_ICECFG_H
#include "IceDefs.h"
#include "IceTypes.h"
#include "IceGlobalContext.h"
#include "llvm/ADT/OwningPtr.h"
#include "llvm/Support/Allocator.h"
namespace Ice {
class Cfg {
public:
Cfg(GlobalContext *Ctx);
~Cfg();
GlobalContext *getContext() const { return Ctx; }
// Manage the name and return type of the function being translated.
void setFunctionName(const IceString &Name) { FunctionName = Name; }
IceString getFunctionName() const { return FunctionName; }
void setReturnType(Type Ty) { ReturnType = Ty; }
// Manage the "internal" attribute of the function.
void setInternal(bool Internal) { IsInternalLinkage = Internal; }
bool getInternal() const { return IsInternalLinkage; }
// Translation error flagging. If support for some construct is
// known to be missing, instead of an assertion failure, setError()
// should be called and the error should be propagated back up.
// This way, we can gracefully fail to translate and let a fallback
// translator handle the function.
void setError(const IceString &Message);
bool hasError() const { return HasError; }
IceString getError() const { return ErrorMessage; }
// Manage nodes (a.k.a. basic blocks, CfgNodes).
void setEntryNode(CfgNode *EntryNode) { Entry = EntryNode; }
CfgNode *getEntryNode() const { return Entry; }
// Create a node and append it to the end of the linearized list.
CfgNode *makeNode(const IceString &Name = "");
SizeT getNumNodes() const { return Nodes.size(); }
const NodeList &getNodes() const { return Nodes; }
// Manage instruction numbering.
int newInstNumber() { return NextInstNumber++; }
// Manage Variables.
Variable *makeVariable(Type Ty, const CfgNode *Node,
const IceString &Name = "");
SizeT getNumVariables() const { return Variables.size(); }
const VarList &getVariables() const { return Variables; }
// Manage arguments to the function.
void addArg(Variable *Arg);
const VarList &getArgs() const { return Args; }
// After the CFG is fully constructed, iterate over the nodes and
// compute the predecessor edges, in the form of
// CfgNode::InEdges[].
void computePredecessors();
// Manage the CurrentNode field, which is used for validating the
// Variable::DefNode field during dumping/emitting.
void setCurrentNode(const CfgNode *Node) { CurrentNode = Node; }
const CfgNode *getCurrentNode() const { return CurrentNode; }
void dump();
// Allocate data of type T using the per-Cfg allocator.
template <typename T> T *allocate() { return Allocator.Allocate<T>(); }
// Allocate an instruction of type T using the per-Cfg instruction allocator.
template <typename T> T *allocateInst() { return Allocator.Allocate<T>(); }
// Allocate an array of data of type T using the per-Cfg allocator.
template <typename T> T *allocateArrayOf(size_t NumElems) {
return Allocator.Allocate<T>(NumElems);
}
// Deallocate data that was allocated via allocate<T>().
template <typename T> void deallocate(T *Object) {
Allocator.Deallocate(Object);
}
// Deallocate data that was allocated via allocateInst<T>().
template <typename T> void deallocateInst(T *Instr) {
Allocator.Deallocate(Instr);
}
// Deallocate data that was allocated via allocateArrayOf<T>().
template <typename T> void deallocateArrayOf(T *Array) {
Allocator.Deallocate(Array);
}
private:
// TODO: for now, everything is allocated from the same allocator. In the
// future we may want to split this to several allocators, for example in
// order to use a "Recycler" to preserve memory. If we keep all allocation
// requests from the Cfg exposed via methods, we can always switch the
// implementation over at a later point.
llvm::BumpPtrAllocator Allocator;
GlobalContext *Ctx;
IceString FunctionName;
Type ReturnType;
bool IsInternalLinkage;
bool HasError;
IceString ErrorMessage;
CfgNode *Entry; // entry basic block
NodeList Nodes; // linearized node list; Entry should be first
int NextInstNumber;
VarList Variables;
VarList Args; // subset of Variables, in argument order
// CurrentNode is maintained during dumping/emitting just for
// validating Variable::DefNode. Normally, a traversal over
// CfgNodes maintains this, but before global operations like
// register allocation, setCurrentNode(NULL) should be called to
// avoid spurious validation failures.
const CfgNode *CurrentNode;
Cfg(const Cfg &) LLVM_DELETED_FUNCTION;
Cfg &operator=(const Cfg &) LLVM_DELETED_FUNCTION;
};
} // end of namespace Ice
#endif // SUBZERO_SRC_ICECFG_H
//===- subzero/src/IceCfgNode.cpp - Basic block (node) implementation -----===//
//
// The Subzero Code Generator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements the CfgNode class, including the
// complexities of instruction insertion and in-edge calculation.
//
//===----------------------------------------------------------------------===//
#include "IceCfg.h"
#include "IceCfgNode.h"
#include "IceInst.h"
#include "IceOperand.h"
namespace Ice {
CfgNode::CfgNode(Cfg *Func, SizeT LabelNumber, IceString Name)
: Func(Func), Number(LabelNumber), Name(Name) {}
// Returns the name the node was created with. If no name was given,
// it synthesizes a (hopefully) unique name.
IceString CfgNode::getName() const {
if (!Name.empty())
return Name;
char buf[30];
snprintf(buf, llvm::array_lengthof(buf), "__%u", getIndex());
return buf;
}
// Adds an instruction to either the Phi list or the regular
// instruction list. Validates that all Phis are added before all
// regular instructions.
void CfgNode::appendInst(Inst *Inst) {
if (InstPhi *Phi = llvm::dyn_cast<InstPhi>(Inst)) {
if (!Insts.empty()) {
Func->setError("Phi instruction added to the middle of a block");
return;
}
Phis.push_back(Phi);
} else {
Insts.push_back(Inst);
}
Inst->updateVars(this);
}
// When a node is created, the OutEdges are immediately knows, but the
// InEdges have to be built up incrementally. After the CFG has been
// constructed, the computePredecessors() pass finalizes it by
// creating the InEdges list.
void CfgNode::computePredecessors() {
OutEdges = (*Insts.rbegin())->getTerminatorEdges();
for (NodeList::const_iterator I = OutEdges.begin(), E = OutEdges.end();
I != E; ++I) {
CfgNode *Node = *I;
Node->InEdges.push_back(this);
}
}
// ======================== Dump routines ======================== //
void CfgNode::dump(Cfg *Func) const {
Func->setCurrentNode(this);
Ostream &Str = Func->getContext()->getStrDump();
if (Func->getContext()->isVerbose(IceV_Instructions)) {
Str << getName() << ":\n";
}
// Dump list of predecessor nodes.
if (Func->getContext()->isVerbose(IceV_Preds) && !InEdges.empty()) {
Str << " // preds = ";
for (NodeList::const_iterator I = InEdges.begin(), E = InEdges.end();
I != E; ++I) {
if (I != InEdges.begin())
Str << ", ";
Str << "%" << (*I)->getName();
}
Str << "\n";
}
// Dump each instruction.
if (Func->getContext()->isVerbose(IceV_Instructions)) {
for (PhiList::const_iterator I = Phis.begin(), E = Phis.end(); I != E;
++I) {
const Inst *Inst = *I;
Inst->dumpDecorated(Func);
}
InstList::const_iterator I = Insts.begin(), E = Insts.end();
while (I != E) {
Inst *Inst = *I++;
Inst->dumpDecorated(Func);
}
}
// Dump list of successor nodes.
if (Func->getContext()->isVerbose(IceV_Succs)) {
Str << " // succs = ";
for (NodeList::const_iterator I = OutEdges.begin(), E = OutEdges.end();
I != E; ++I) {
if (I != OutEdges.begin())
Str << ", ";
Str << "%" << (*I)->getName();
}
Str << "\n";
}
}
} // end of namespace Ice
//===- subzero/src/IceCfgNode.h - Control flow graph node -------*- C++ -*-===//
//
// The Subzero Code Generator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file declares the CfgNode class, which represents a single
// basic block as its instruction list, in-edge list, and out-edge
// list.
//
//===----------------------------------------------------------------------===//
#ifndef SUBZERO_SRC_ICECFGNODE_H
#define SUBZERO_SRC_ICECFGNODE_H
#include "IceDefs.h"
namespace Ice {
class CfgNode {
public:
static CfgNode *create(Cfg *Func, SizeT LabelIndex, IceString Name = "") {
return new (Func->allocate<CfgNode>()) CfgNode(Func, LabelIndex, Name);
}
// Access the label number and name for this node.
SizeT getIndex() const { return Number; }
IceString getName() const;
// Access predecessor and successor edge lists.
const NodeList &getInEdges() const { return InEdges; }
const NodeList &getOutEdges() const { return OutEdges; }
// Manage the instruction list.
InstList &getInsts() { return Insts; }
void appendInst(Inst *Inst);
// Add a predecessor edge to the InEdges list for each of this
// node's successors.
void computePredecessors();
void dump(Cfg *Func) const;
private:
CfgNode(Cfg *Func, SizeT LabelIndex, IceString Name);
CfgNode(const CfgNode &) LLVM_DELETED_FUNCTION;
CfgNode &operator=(const CfgNode &) LLVM_DELETED_FUNCTION;
Cfg *const Func;
const SizeT Number; // label index
IceString Name; // for dumping only
NodeList InEdges; // in no particular order
NodeList OutEdges; // in no particular order
PhiList Phis; // unordered set of phi instructions
InstList Insts; // ordered list of non-phi instructions
};
} // end of namespace Ice
#endif // SUBZERO_SRC_ICECFGNODE_H
//===- subzero/src/IceDefs.h - Common Subzero declaraions -------*- C++ -*-===//
//
// The Subzero Code Generator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file declares various useful types and classes that have
// widespread use across Subzero. Every Subzero source file is
// expected to include IceDefs.h.
//
//===----------------------------------------------------------------------===//
#ifndef SUBZERO_SRC_ICEDEFS_H
#define SUBZERO_SRC_ICEDEFS_H
#include <stdint.h> // TODO: <cstdint> with C++11
#include <cassert>
#include <cstdio> // snprintf
#include <functional> // std::less
#include <list>
#include <map>
#include <set>
#include <string>
#include <vector>
#include "llvm/ADT/BitVector.h"
#include "llvm/ADT/SmallBitVector.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/Compiler.h" // LLVM_STATIC_ASSERT
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/Timer.h"
namespace Ice {
class CfgNode;
class Constant;
class GlobalContext;
class Cfg;
class Inst;
class InstPhi;
class InstTarget;
class Operand;
class Variable;
// TODO: Switch over to LLVM's ADT container classes.
// http://llvm.org/docs/ProgrammersManual.html#picking-the-right-data-structure-for-a-task
typedef std::string IceString;
typedef std::list<Inst *> InstList;
typedef std::list<InstPhi *> PhiList;
typedef std::vector<Variable *> VarList;
typedef std::vector<CfgNode *> NodeList;
// SizeT is for holding small-ish limits like number of source
// operands in an instruction. It is used instead of size_t (which
// may be 64-bits wide) when we want to save space.
typedef uint32_t SizeT;
enum VerboseItem {
IceV_None = 0,
IceV_Instructions = 1 << 0,
IceV_Deleted = 1 << 1,
IceV_InstNumbers = 1 << 2,
IceV_Preds = 1 << 3,
IceV_Succs = 1 << 4,
IceV_Liveness = 1 << 5,
IceV_RegManager = 1 << 6,
IceV_RegOrigins = 1 << 7,
IceV_LinearScan = 1 << 8,
IceV_Frame = 1 << 9,
IceV_Timing = 1 << 10,
IceV_All = ~IceV_None
};
typedef uint32_t VerboseMask;
// The Ostream class wraps an output stream and a Cfg pointer, so
// that dump routines have access to the Cfg object and can print
// labels and variable names.
class Ostream {
public:
Ostream(llvm::raw_ostream *Stream) : Stream(Stream) {}
llvm::raw_ostream *Stream;
private:
Ostream(const Ostream &) LLVM_DELETED_FUNCTION;
Ostream &operator=(const Ostream &) LLVM_DELETED_FUNCTION;
};
template <typename T> inline Ostream &operator<<(Ostream &Str, const T &Val) {
if (Str.Stream)
(*Str.Stream) << Val;
return Str;
}
// TODO: Implement in terms of std::chrono after switching to C++11.
class Timer {
public:
Timer() : Start(llvm::TimeRecord::getCurrentTime(false)) {}
uint64_t getElapsedNs() const { return getElapsedSec() * 1000 * 1000 * 1000; }
uint64_t getElapsedUs() const { return getElapsedSec() * 1000 * 1000; }
uint64_t getElapsedMs() const { return getElapsedSec() * 1000; }
double getElapsedSec() const {
llvm::TimeRecord End = llvm::TimeRecord::getCurrentTime(false);
return End.getWallTime() - Start.getWallTime();
}
void printElapsedUs(GlobalContext *Ctx, const IceString &Tag) const;
private:
const llvm::TimeRecord Start;
Timer(const Timer &) LLVM_DELETED_FUNCTION;
Timer &operator=(const Timer &) LLVM_DELETED_FUNCTION;
};
} // end of namespace Ice
#endif // SUBZERO_SRC_ICEDEFS_H
//===- subzero/src/IceGlobalContext.cpp - Global context defs ---*- C++ -*-===//
//
// The Subzero Code Generator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines aspects of the compilation that persist across
// multiple functions.
//
//===----------------------------------------------------------------------===//
#include "IceDefs.h"
#include "IceTypes.h"
#include "IceCfg.h"
#include "IceGlobalContext.h"
#include "IceOperand.h"
namespace Ice {
// TypePool maps constants of type KeyType (e.g. float) to pointers to
// type ValueType (e.g. ConstantFloat). KeyType values are compared
// using memcmp() because of potential NaN values in KeyType values.
// KeyTypeHasFP indicates whether KeyType is a floating-point type
// whose values need to be compared using memcmp() for NaN
// correctness. TODO: use std::is_floating_point<KeyType> instead of
// KeyTypeHasFP with C++11.
template <typename KeyType, typename ValueType, bool KeyTypeHasFP = false>
class TypePool {
TypePool(const TypePool &) LLVM_DELETED_FUNCTION;
TypePool &operator=(const TypePool &) LLVM_DELETED_FUNCTION;
public:
TypePool() {}
ValueType *getOrAdd(GlobalContext *Ctx, Type Ty, KeyType Key) {
TupleType TupleKey = std::make_pair(Ty, Key);
typename ContainerType::const_iterator Iter = Pool.find(TupleKey);
if (Iter != Pool.end())
return Iter->second;
ValueType *Result = ValueType::create(Ctx, Ty, Key);
Pool[TupleKey] = Result;
return Result;
}
private:
typedef std::pair<Type, KeyType> TupleType;
struct TupleCompare {
bool operator()(const TupleType &A, const TupleType &B) {
if (A.first != B.first)
return A.first < B.first;
if (KeyTypeHasFP)
return memcmp(&A.second, &B.second, sizeof(KeyType)) < 0;
return A.second < B.second;
}
};
typedef std::map<const TupleType, ValueType *, TupleCompare> ContainerType;
ContainerType Pool;
};
// The global constant pool bundles individual pools of each type of
// interest.
class ConstantPool {
ConstantPool(const ConstantPool &) LLVM_DELETED_FUNCTION;
ConstantPool &operator=(const ConstantPool &) LLVM_DELETED_FUNCTION;
public:
ConstantPool() {}
TypePool<float, ConstantFloat, true> Floats;
TypePool<double, ConstantDouble, true> Doubles;
TypePool<uint64_t, ConstantInteger> Integers;
TypePool<RelocatableTuple, ConstantRelocatable> Relocatables;
};
GlobalContext::GlobalContext(llvm::raw_ostream *OsDump,
llvm::raw_ostream *OsEmit, VerboseMask Mask,
IceString TestPrefix)
: StrDump(OsDump), StrEmit(OsEmit), VMask(Mask),
ConstPool(new ConstantPool()), TestPrefix(TestPrefix) {}
// In this context, name mangling means to rewrite a symbol using a
// given prefix. For a C++ symbol, nest the original symbol inside
// the "prefix" namespace. For other symbols, just prepend the
// prefix.
IceString GlobalContext::mangleName(const IceString &Name) const {
// TODO: Add explicit tests (beyond the implicit tests in the linker
// that come from the cross tests).
//
// An already-nested name like foo::bar() gets pushed down one
// level, making it equivalent to Prefix::foo::bar().
// _ZN3foo3barExyz ==> _ZN6Prefix3foo3barExyz
// A non-nested but mangled name like bar() gets nested, making it
// equivalent to Prefix::bar().
// _Z3barxyz ==> ZN6Prefix3barExyz
// An unmangled, extern "C" style name, gets a simple prefix:
// bar ==> Prefixbar
if (getTestPrefix().empty())
return Name;
unsigned PrefixLength = getTestPrefix().length();
char NameBase[1 + Name.length()];
const size_t BufLen = 30 + Name.length() + getTestPrefix().length();
char NewName[BufLen];
uint32_t BaseLength = 0;
int ItemsParsed = sscanf(Name.c_str(), "_ZN%s", NameBase);
if (ItemsParsed == 1) {
// Transform _ZN3foo3barExyz ==> _ZN6Prefix3foo3barExyz
// (splice in "6Prefix") ^^^^^^^
snprintf(NewName, BufLen, "_ZN%u%s%s", PrefixLength,
getTestPrefix().c_str(), NameBase);
// We ignore the snprintf return value (here and below). If we
// somehow miscalculated the output buffer length, the output will
// be truncated, but it will be truncated consistently for all
// mangleName() calls on the same input string.
return NewName;
}
ItemsParsed = sscanf(Name.c_str(), "_Z%u%s", &BaseLength, NameBase);
if (ItemsParsed == 2) {
// Transform _Z3barxyz ==> ZN6Prefix3barExyz
// ^^^^^^^^ ^
// (splice in "N6Prefix", and insert "E" after "3bar")
char OrigName[Name.length()];
char OrigSuffix[Name.length()];
strncpy(OrigName, NameBase, BaseLength);
OrigName[BaseLength] = '\0';
strcpy(OrigSuffix, NameBase + BaseLength);
snprintf(NewName, BufLen, "_ZN%u%s%u%sE%s", PrefixLength,
getTestPrefix().c_str(), BaseLength, OrigName, OrigSuffix);
return NewName;
}
// Transform bar ==> Prefixbar
// ^^^^^^
return getTestPrefix() + Name;
}
GlobalContext::~GlobalContext() {}
Constant *GlobalContext::getConstantInt(Type Ty, uint64_t ConstantInt64) {
return ConstPool->Integers.getOrAdd(this, Ty, ConstantInt64);
}
Constant *GlobalContext::getConstantFloat(float ConstantFloat) {
return ConstPool->Floats.getOrAdd(this, IceType_f32, ConstantFloat);
}
Constant *GlobalContext::getConstantDouble(double ConstantDouble) {
return ConstPool->Doubles.getOrAdd(this, IceType_f64, ConstantDouble);
}
Constant *GlobalContext::getConstantSym(Type Ty, int64_t Offset,
const IceString &Name,
bool SuppressMangling) {
return ConstPool->Relocatables.getOrAdd(
this, Ty, RelocatableTuple(Offset, Name, SuppressMangling));
}
void Timer::printElapsedUs(GlobalContext *Ctx, const IceString &Tag) const {
if (Ctx->isVerbose(IceV_Timing)) {
// Prefixing with '#' allows timing strings to be included
// without error in textual assembly output.
Ctx->getStrDump() << "# " << getElapsedUs() << " usec " << Tag << "\n";
}
}
} // end of namespace Ice
//===- subzero/src/IceGlobalContext.h - Global context defs -----*- C++ -*-===//
//
// The Subzero Code Generator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file declares aspects of the compilation that persist across
// multiple functions.
//
//===----------------------------------------------------------------------===//
#ifndef SUBZERO_SRC_ICEGLOBALCONTEXT_H
#define SUBZERO_SRC_ICEGLOBALCONTEXT_H
#include "llvm/ADT/OwningPtr.h"
#include "llvm/Support/Allocator.h"
#include "llvm/Support/raw_ostream.h"
#include "IceDefs.h"
#include "IceTypes.h"
namespace Ice {
// TODO: Accesses to all non-const fields of GlobalContext need to
// be synchronized, especially the constant pool, the allocator, and
// the output streams.
class GlobalContext {
public:
GlobalContext(llvm::raw_ostream *OsDump, llvm::raw_ostream *OsEmit,
VerboseMask Mask, IceString TestPrefix);
~GlobalContext();
// Returns true if any of the specified options in the verbose mask
// are set. If the argument is omitted, it checks if any verbose
// options at all are set. IceV_Timing is treated specially, so
// that running with just IceV_Timing verbosity doesn't trigger an
// avalanche of extra output.
bool isVerbose(VerboseMask Mask = (IceV_All & ~IceV_Timing)) const {
return VMask & Mask;
}
void setVerbose(VerboseMask Mask) { VMask = Mask; }
void addVerbose(VerboseMask Mask) { VMask |= Mask; }
void subVerbose(VerboseMask Mask) { VMask &= ~Mask; }
Ostream &getStrDump() { return StrDump; }
Ostream &getStrEmit() { return StrEmit; }
// When emitting assembly, we allow a string to be prepended to
// names of translated functions. This makes it easier to create an
// execution test against a reference translator like llc, with both
// translators using the same bitcode as input.
IceString getTestPrefix() const { return TestPrefix; }
IceString mangleName(const IceString &Name) const;
// Manage Constants.
// getConstant*() functions are not const because they might add
// something to the constant pool.
Constant *getConstantInt(Type Ty, uint64_t ConstantInt64);
Constant *getConstantFloat(float Value);
Constant *getConstantDouble(double Value);
// Returns a symbolic constant.
Constant *getConstantSym(Type Ty, int64_t Offset, const IceString &Name = "",
bool SuppressMangling = false);
// Allocate data of type T using the global allocator.
template <typename T> T *allocate() { return Allocator.Allocate<T>(); }
private:
Ostream StrDump; // Stream for dumping / diagnostics
Ostream StrEmit; // Stream for code emission
llvm::BumpPtrAllocator Allocator;
VerboseMask VMask;
llvm::OwningPtr<class ConstantPool> ConstPool;
const IceString TestPrefix;
GlobalContext(const GlobalContext &) LLVM_DELETED_FUNCTION;
GlobalContext &operator=(const GlobalContext &) LLVM_DELETED_FUNCTION;
};
} // end of namespace Ice
#endif // SUBZERO_SRC_ICEGLOBALCONTEXT_H
This diff is collapsed. Click to expand it.
//===- subzero/src/IceInst.def - X-macros for ICE instructions -*- C++ -*-===//
//
// The Subzero Code Generator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines properties of ICE instructions in the form of
// x-macros.
//
//===----------------------------------------------------------------------===//
#ifndef SUBZERO_SRC_ICEINST_DEF
#define SUBZERO_SRC_ICEINST_DEF
#define ICEINSTARITHMETIC_TABLE \
/* enum value, printable string, commutative */ \
X(Add, "add", 1) \
X(Fadd, "fadd", 0) \
X(Sub, "sub", 0) \
X(Fsub, "fsub", 0) \
X(Mul, "mul", 1) \
X(Fmul, "fmul", 0) \
X(Udiv, "udiv", 0) \
X(Sdiv, "sdiv", 0) \
X(Fdiv, "fdiv", 0) \
X(Urem, "urem", 0) \
X(Srem, "srem", 0) \
X(Frem, "frem", 0) \
X(Shl, "shl", 0) \
X(Lshr, "lshr", 0) \
X(Ashr, "ashr", 0) \
X(And, "and", 1) \
X(Or, "or", 1) \
X(Xor, "xor", 1)
//#define X(tag, str, commutative)
#define ICEINSTCAST_TABLE \
/* enum value, printable string */ \
X(Trunc, "trunc") \
X(Zext, "zext") \
X(Sext, "sext") \
X(Fptrunc, "fptrunc") \
X(Fpext, "fpext") \
X(Fptoui, "fptoui") \
X(Fptosi, "fptosi") \
X(Uitofp, "uitofp") \
X(Sitofp, "sitofp") \
X(Bitcast, "bitcast")
//#define X(tag, str)
#define ICEINSTFCMP_TABLE \
/* enum value, printable string */ \
X(False, "false") \
X(Oeq, "oeq") \
X(Ogt, "ogt") \
X(Oge, "oge") \
X(Olt, "olt") \
X(Ole, "ole") \
X(One, "one") \
X(Ord, "ord") \
X(Ueq, "ueq") \
X(Ugt, "ugt") \
X(Uge, "uge") \
X(Ult, "ult") \
X(Ule, "ule") \
X(Une, "une") \
X(Uno, "uno") \
X(True, "true")
//#define X(tag, str)
#define ICEINSTICMP_TABLE \
/* enum value, printable string */ \
X(Eq, "eq") \
X(Ne, "ne") \
X(Ugt, "ugt") \
X(Uge, "uge") \
X(Ult, "ult") \
X(Ule, "ule") \
X(Sgt, "sgt") \
X(Sge, "sge") \
X(Slt, "slt") \
X(Sle, "sle")
//#define X(tag, str)
#endif // SUBZERO_SRC_ICEINST_DEF
This diff is collapsed. Click to expand it.
//===- subzero/src/IceOperand.cpp - High-level operand implementation -----===//
//
// The Subzero Code Generator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements the Operand class and its
// target-independent subclasses, primarily for the methods of the
// Variable class.
//
//===----------------------------------------------------------------------===//
#include "IceCfg.h"
#include "IceInst.h"
#include "IceOperand.h"
namespace Ice {
bool operator<(const RelocatableTuple &A, const RelocatableTuple &B) {
if (A.Offset != B.Offset)
return A.Offset < B.Offset;
if (A.SuppressMangling != B.SuppressMangling)
return A.SuppressMangling < B.SuppressMangling;
return A.Name < B.Name;
}
void Variable::setUse(const Inst *Inst, const CfgNode *Node) {
if (DefNode == NULL)
return;
if (llvm::isa<InstPhi>(Inst) || Node != DefNode)
DefNode = NULL;
}
void Variable::setDefinition(Inst *Inst, const CfgNode *Node) {
if (DefNode == NULL)
return;
// Can first check preexisting DefInst if we care about multi-def vars.
DefInst = Inst;
if (Node != DefNode)
DefNode = NULL;
}
void Variable::replaceDefinition(Inst *Inst, const CfgNode *Node) {
DefInst = NULL;
setDefinition(Inst, Node);
}
void Variable::setIsArg(Cfg *Func) {
IsArgument = true;
if (DefNode == NULL)
return;
CfgNode *Entry = Func->getEntryNode();
if (DefNode == Entry)
return;
DefNode = NULL;
}
IceString Variable::getName() const {
if (!Name.empty())
return Name;
char buf[30];
snprintf(buf, llvm::array_lengthof(buf), "__%u", getIndex());
return buf;
}
// ======================== dump routines ======================== //
void Variable::dump(const Cfg *Func) const {
Ostream &Str = Func->getContext()->getStrDump();
const CfgNode *CurrentNode = Func->getCurrentNode();
(void)CurrentNode; // used only in assert()
assert(CurrentNode == NULL || DefNode == NULL || DefNode == CurrentNode);
Str << "%" << getName();
}
void Operand::dump(const Cfg *Func) const {
Ostream &Str = Func->getContext()->getStrDump();
Str << "Operand<?>";
}
void ConstantRelocatable::dump(const Cfg *Func) const {
Ostream &Str = Func->getContext()->getStrDump();
Str << "@" << Name;
if (Offset)
Str << "+" << Offset;
}
} // end of namespace Ice
//===- subzero/src/IceOperand.h - High-level operands -----------*- C++ -*-===//
//
// The Subzero Code Generator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file declares the Operand class and its target-independent
// subclasses. The main classes are Variable, which represents an
// LLVM variable that is either register- or stack-allocated, and the
// Constant hierarchy, which represents integer, floating-point,
// and/or symbolic constants.
//
//===----------------------------------------------------------------------===//
#ifndef SUBZERO_SRC_ICEOPERAND_H
#define SUBZERO_SRC_ICEOPERAND_H
#include "IceDefs.h"
#include "IceTypes.h"
namespace Ice {
class Operand {
public:
enum OperandKind {
kConst_Base,
kConstInteger,
kConstFloat,
kConstDouble,
kConstRelocatable,
kConst_Num,
kVariable,
// Target-specific operand classes use kTarget as the starting
// point for their Kind enum space.
kTarget
};
OperandKind getKind() const { return Kind; }
Type getType() const { return Ty; }
// Every Operand keeps an array of the Variables referenced in
// the operand. This is so that the liveness operations can get
// quick access to the variables of interest, without having to dig
// so far into the operand.
SizeT getNumVars() const { return NumVars; }
Variable *getVar(SizeT I) const {
assert(I < getNumVars());
return Vars[I];
}
virtual void dump(const Cfg *Func) const = 0;
// Query whether this object was allocated in isolation, or added to
// some higher-level pool. This determines whether a containing
// object's destructor should delete this object. Generally,
// constants are pooled globally, variables are pooled per-CFG, and
// target-specific operands are not pooled.
virtual bool isPooled() const { return false; }
virtual ~Operand() {}
protected:
Operand(OperandKind Kind, Type Ty)
: Ty(Ty), Kind(Kind), NumVars(0), Vars(NULL) {}
const Type Ty;
const OperandKind Kind;
// Vars and NumVars are initialized by the derived class.
SizeT NumVars;
Variable **Vars;
private:
Operand(const Operand &) LLVM_DELETED_FUNCTION;
Operand &operator=(const Operand &) LLVM_DELETED_FUNCTION;
};
// Constant is the abstract base class for constants. All
// constants are allocated from a global arena and are pooled.
class Constant : public Operand {
public:
virtual void dump(const Cfg *Func) const = 0;
static bool classof(const Operand *Operand) {
OperandKind Kind = Operand->getKind();
return Kind >= kConst_Base && Kind <= kConst_Num;
}
protected:
Constant(OperandKind Kind, Type Ty) : Operand(Kind, Ty) {
Vars = NULL;
NumVars = 0;
}
virtual ~Constant() {}
private:
Constant(const Constant &) LLVM_DELETED_FUNCTION;
Constant &operator=(const Constant &) LLVM_DELETED_FUNCTION;
};
// ConstantPrimitive<> wraps a primitive type.
template <typename T, Operand::OperandKind K>
class ConstantPrimitive : public Constant {
public:
static ConstantPrimitive *create(GlobalContext *Ctx, Type Ty, T Value) {
return new (Ctx->allocate<ConstantPrimitive>())
ConstantPrimitive(Ty, Value);
}
T getValue() const { return Value; }
virtual void dump(const Cfg *Func) const {
Ostream &Str = Func->getContext()->getStrDump();
Str << getValue();
}
static bool classof(const Operand *Operand) {
return Operand->getKind() == K;
}
private:
ConstantPrimitive(Type Ty, T Value) : Constant(K, Ty), Value(Value) {}
ConstantPrimitive(const ConstantPrimitive &) LLVM_DELETED_FUNCTION;
ConstantPrimitive &operator=(const ConstantPrimitive &) LLVM_DELETED_FUNCTION;
virtual ~ConstantPrimitive() {}
const T Value;
};
typedef ConstantPrimitive<uint64_t, Operand::kConstInteger> ConstantInteger;
typedef ConstantPrimitive<float, Operand::kConstFloat> ConstantFloat;
typedef ConstantPrimitive<double, Operand::kConstDouble> ConstantDouble;
// RelocatableTuple bundles the parameters that are used to
// construct an ConstantRelocatable. It is done this way so that
// ConstantRelocatable can fit into the global constant pool
// template mechanism.
class RelocatableTuple {
RelocatableTuple &operator=(const RelocatableTuple &) LLVM_DELETED_FUNCTION;
public:
RelocatableTuple(const int64_t Offset, const IceString &Name,
bool SuppressMangling)
: Offset(Offset), Name(Name), SuppressMangling(SuppressMangling) {}
RelocatableTuple(const RelocatableTuple &Other)
: Offset(Other.Offset), Name(Other.Name),
SuppressMangling(Other.SuppressMangling) {}
const int64_t Offset;
const IceString Name;
bool SuppressMangling;
};
bool operator<(const RelocatableTuple &A, const RelocatableTuple &B);
// ConstantRelocatable represents a symbolic constant combined with
// a fixed offset.
class ConstantRelocatable : public Constant {
public:
static ConstantRelocatable *create(GlobalContext *Ctx, Type Ty,
const RelocatableTuple &Tuple) {
return new (Ctx->allocate<ConstantRelocatable>()) ConstantRelocatable(
Ty, Tuple.Offset, Tuple.Name, Tuple.SuppressMangling);
}
int64_t getOffset() const { return Offset; }
IceString getName() const { return Name; }
void setSuppressMangling(bool Value) { SuppressMangling = Value; }
bool getSuppressMangling() const { return SuppressMangling; }
virtual void dump(const Cfg *Func) const;
static bool classof(const Operand *Operand) {
OperandKind Kind = Operand->getKind();
return Kind == kConstRelocatable;
}
private:
ConstantRelocatable(Type Ty, int64_t Offset, const IceString &Name,
bool SuppressMangling)
: Constant(kConstRelocatable, Ty), Offset(Offset), Name(Name),
SuppressMangling(SuppressMangling) {}
ConstantRelocatable(const ConstantRelocatable &) LLVM_DELETED_FUNCTION;
ConstantRelocatable &
operator=(const ConstantRelocatable &) LLVM_DELETED_FUNCTION;
virtual ~ConstantRelocatable() {}
const int64_t Offset; // fixed offset to add
const IceString Name; // optional for debug/dump
bool SuppressMangling;
};
// Variable represents an operand that is register-allocated or
// stack-allocated. If it is register-allocated, it will ultimately
// have a non-negative RegNum field.
class Variable : public Operand {
public:
static Variable *create(Cfg *Func, Type Ty, const CfgNode *Node, SizeT Index,
const IceString &Name) {
return new (Func->allocate<Variable>()) Variable(Ty, Node, Index, Name);
}
SizeT getIndex() const { return Number; }
IceString getName() const;
Inst *getDefinition() const { return DefInst; }
void setDefinition(Inst *Inst, const CfgNode *Node);
void replaceDefinition(Inst *Inst, const CfgNode *Node);
const CfgNode *getLocalUseNode() const { return DefNode; }
bool isMultiblockLife() const { return (DefNode == NULL); }
void setUse(const Inst *Inst, const CfgNode *Node);
bool getIsArg() const { return IsArgument; }
void setIsArg(Cfg *Func);
virtual void dump(const Cfg *Func) const;
static bool classof(const Operand *Operand) {
return Operand->getKind() == kVariable;
}
private:
Variable(Type Ty, const CfgNode *Node, SizeT Index, const IceString &Name)
: Operand(kVariable, Ty), Number(Index), Name(Name), DefInst(NULL),
DefNode(Node), IsArgument(false) {
Vars = VarsReal;
Vars[0] = this;
NumVars = 1;
}
Variable(const Variable &) LLVM_DELETED_FUNCTION;
Variable &operator=(const Variable &) LLVM_DELETED_FUNCTION;
virtual ~Variable() {}
// Number is unique across all variables, and is used as a
// (bit)vector index for liveness analysis.
const SizeT Number;
// Name is optional.
const IceString Name;
// DefInst is the instruction that produces this variable as its
// dest.
Inst *DefInst;
// DefNode is the node where this variable was produced, and is
// reset to NULL if it is used outside that node. This is used for
// detecting isMultiblockLife(). TODO: Collapse this to a single
// bit and use a separate pass to calculate the values across the
// Cfg. This saves space in the Variable, and removes the fragility
// of incrementally computing and maintaining the information.
const CfgNode *DefNode;
bool IsArgument;
// VarsReal (and Operand::Vars) are set up such that Vars[0] ==
// this.
Variable *VarsReal[1];
};
} // end of namespace Ice
#endif // SUBZERO_SRC_ICEOPERAND_H
//===- subzero/src/IceTypes.cpp - Primitive type properties ---------------===//
//
// The Subzero Code Generator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines a few attributes of Subzero primitive types.
//
//===----------------------------------------------------------------------===//
#include "IceDefs.h"
#include "IceTypes.h"
namespace Ice {
namespace {
const struct {
size_t TypeWidthInBytes;
size_t TypeAlignInBytes;
const char *DisplayString;
} TypeAttributes[] = {
#define X(tag, size, align, str) \
{ size, align, str } \
,
ICETYPE_TABLE
#undef X
};
const size_t TypeAttributesSize =
sizeof(TypeAttributes) / sizeof(*TypeAttributes);
} // end anonymous namespace
size_t typeWidthInBytes(Type Ty) {
size_t Width = 0;
size_t Index = static_cast<size_t>(Ty);
if (Index < TypeAttributesSize) {
Width = TypeAttributes[Index].TypeWidthInBytes;
} else {
assert(0 && "Invalid type for typeWidthInBytes()");
}
return Width;
}
size_t typeAlignInBytes(Type Ty) {
size_t Align = 0;
size_t Index = static_cast<size_t>(Ty);
if (Index < TypeAttributesSize) {
Align = TypeAttributes[Index].TypeAlignInBytes;
} else {
assert(0 && "Invalid type for typeAlignInBytes()");
}
return Align;
}
// ======================== Dump routines ======================== //
template <> Ostream &operator<<(Ostream &Str, const Type &Ty) {
size_t Index = static_cast<size_t>(Ty);
if (Index < TypeAttributesSize) {
Str << TypeAttributes[Index].DisplayString;
} else {
Str << "???";
assert(0 && "Invalid type for printing");
}
return Str;
}
} // end of namespace Ice
//===- subzero/src/IceTypes.def - X-macros for ICE types --------*- C++ -*-===//
//
// The Subzero Code Generator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines properties of ICE primitive types in the form of
// x-macros.
//
//===----------------------------------------------------------------------===//
#ifndef SUBZERO_SRC_ICETYPES_DEF
#define SUBZERO_SRC_ICETYPES_DEF
#define ICETYPE_TABLE \
/* enum value, size, align, printable string */ \
/* (size and alignment in bytes) */ \
X(IceType_void, 0, 0, "void") \
X(IceType_i1, 1, 1, "i1") \
X(IceType_i8, 1, 1, "i8") \
X(IceType_i16, 2, 1, "i16") \
X(IceType_i32, 4, 1, "i32") \
X(IceType_i64, 8, 1, "i64") \
X(IceType_f32, 4, 4, "float") \
X(IceType_f64, 8, 8, "double")
//#define X(tag, size, align, str)
#endif // SUBZERO_SRC_ICETYPES_DEF
//===- subzero/src/IceTypes.h - Primitive ICE types -------------*- C++ -*-===//
//
// The Subzero Code Generator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file declares a few properties of the primitive types allowed
// in Subzero. Every Subzero source file is expected to include
// IceTypes.h.
//
//===----------------------------------------------------------------------===//
#ifndef SUBZERO_SRC_ICETYPES_H
#define SUBZERO_SRC_ICETYPES_H
#include "IceTypes.def"
namespace Ice {
enum Type {
#define X(tag, size, align, str) tag,
ICETYPE_TABLE
#undef X
};
size_t typeWidthInBytes(Type Ty);
size_t typeAlignInBytes(Type Ty);
template <> Ostream &operator<<(class Ostream &Str, const Type &Ty);
} // end of namespace Ice
#endif // SUBZERO_SRC_ICETYPES_H
#!/usr/bin/env python2
import argparse
import itertools
import subprocess
import re
if __name__ == '__main__':
"""Runs llvm2ice on an input .ll file, and compares the output
against the input.
Before comparing, the input file is massaged to remove comments,
blank lines, global variable definitions, external function
declarations, and possibly other patterns that llvm2ice does not
handle.
The output file and the massaged input file are compared line by
line for differences. However, there is a regex defined such that
if the regex matches a line in the input file, that line and the
corresponding line in the output file are ignored. This lets us
ignore minor differences such as inttoptr and ptrtoint, and
printing of floating-point constants.
On success, no output is produced. On failure, each mismatch is
printed as two lines, one starting with 'SZ' and one starting with
'LL'.
"""
desc = 'Compare llvm2ice output against bitcode input.'
argparser = argparse.ArgumentParser(description=desc)
argparser.add_argument(
'llfile', nargs='?', default='-',
type=argparse.FileType('r'), metavar='FILE',
help='Textual bitcode file [default stdin]')
argparser.add_argument(
'--llvm2ice', required=False, default='./llvm2ice', metavar='LLVM2ICE',
help='Path to llvm2ice driver program [default ./llvm2ice]')
args = argparser.parse_args()
bitcode = args.llfile.readlines()
# Run llvm2ice and collect its output lines into sz_out.
command = [args.llvm2ice, '-verbose', 'inst', '-notranslate', '-']
p = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
sz_out = p.communicate(input=''.join(bitcode))[0].splitlines()
# Filter certain lines and patterns from the input, and collect
# the remainder into llc_out.
llc_out = []
tail_call = re.compile(' tail call ');
trailing_comment = re.compile(';.*')
ignore_pattern = re.compile('^ *$|^declare|^@')
for line in bitcode:
# Convert tail call into regular (non-tail) call.
line = tail_call.sub(' call ', line)
# Remove trailing comments and spaces.
line = trailing_comment.sub('', line).rstrip()
# Ignore blanks lines, forward declarations, and variable definitions.
if not ignore_pattern.search(line):
llc_out.append(line)
# Compare sz_out and llc_out line by line, but ignore pairs of
# lines where the llc line matches a certain pattern.
return_code = 0
lines_total = 0
lines_diff = 0
ignore_pattern = re.compile(
'|'.join([' -[0-9]', # negative constants
' (float|double) [-0-9]', # FP constants
' (float|double) %\w+, [-0-9]',
' inttoptr ', # inttoptr pointer types
' ptrtoint ' # ptrtoint pointer types
]))
for (sz_line, llc_line) in itertools.izip_longest(sz_out, llc_out):
lines_total += 1
if sz_line == llc_line:
continue
if llc_line and ignore_pattern.search(llc_line):
lines_diff += 1
continue
if sz_line: print 'SZ>' + sz_line
if llc_line: print 'LL>' + llc_line
return_code = 1
if return_code == 0:
message = 'Success (ignored %d diffs out of %d lines)'
print message % (lines_diff, lines_total)
exit(return_code)
# Taken from utils/lit/tests in the LLVM tree and hacked together to support
# our tests.
# -*- Python -*-
import os
import sys
import lit.formats
# name: The name of this test suite.
config.name = 'subzero'
# testFormat: The test format to use to interpret tests.
config.test_format = lit.formats.ShTest()
# suffixes: A list of file extensions to treat as test files.
config.suffixes = ['.ll']
# test_source_root: The root path where tests are located.
config.test_source_root = os.path.dirname(__file__)
config.test_exec_root = config.test_source_root
config.target_triple = '(unused)'
src_root = os.path.abspath(os.path.join(config.test_source_root, '..'))
bin_root = src_root
config.substitutions.append(('%{src_root}', src_root))
config.substitutions.append(('%{python}', sys.executable))
# Finding LLVM binary tools. All tools used in the tests must be listed in
# the llvmbintools list.
llvmbinpath = os.path.abspath(os.environ.get('LLVM_BIN_PATH'))
# Finding Subzero tools
config.substitutions.append(('%llvm2ice', os.path.join(bin_root, 'llvm2ice')))
config.substitutions.append(('%szdiff', os.path.join(bin_root, 'szdiff.py')))
llvmbintools = ['FileCheck']
for tool in llvmbintools:
config.substitutions.append((tool, os.path.join(llvmbinpath, tool)))
# Add a feature to detect the Python version.
config.available_features.add("python%d.%d" % (sys.version_info[0],
sys.version_info[1]))
# Debugging output
def dbg(s):
print '[DBG] %s' % s
dbg('bin_root = %s' % bin_root)
dbg('llvmbinpath = %s' % llvmbinpath)
; RUIN: %llvm2ice --verbose none %s | FileCheck %s
; RUIN: %llvm2ice --verbose none %s | FileCheck --check-prefix=ERRORS %s
; RUN: %szdiff --llvm2ice=%llvm2ice %s | FileCheck --check-prefix=DUMP %s
define void @fixed_400(i32 %n) {
entry:
%array = alloca i8, i32 400, align 16
%array.asint = ptrtoint i8* %array to i32
call void @f1(i32 %array.asint)
ret void
; CHECK: sub esp, 400
; CHECK-NEXT: mov eax, esp
; CHECK-NEXT: push eax
; CHECK-NEXT: call f1
}
declare void @f1(i32)
define void @variable_n(i32 %n) {
entry:
%array = alloca i8, i32 %n, align 16
%array.asint = ptrtoint i8* %array to i32
call void @f2(i32 %array.asint)
ret void
; CHECK: mov eax, dword ptr [ebp+8]
; CHECK-NEXT: sub esp, eax
; CHECK-NEXT: mov eax, esp
; CHECK-NEXT: push eax
; CHECK-NEXT: call f2
}
declare void @f2(i32)
; ERRORS-NOT: ICE translation error
; DUMP-NOT: SZ
; RUIN: %llvm2ice -verbose inst %s | FileCheck %s
; RUIN: %llvm2ice --verbose none %s | FileCheck --check-prefix=ERRORS %s
; RUN: %szdiff --llvm2ice=%llvm2ice %s | FileCheck --check-prefix=DUMP %s
define i32 @Add(i32 %a, i32 %b) {
; CHECK: define i32 @Add
entry:
%add = add i32 %b, %a
; CHECK: add
tail call void @Use(i32 %add)
; CHECK: call Use
ret i32 %add
}
declare void @Use(i32)
define i32 @And(i32 %a, i32 %b) {
; CHECK: define i32 @And
entry:
%and = and i32 %b, %a
; CHECK: and
tail call void @Use(i32 %and)
; CHECK: call Use
ret i32 %and
}
define i32 @Or(i32 %a, i32 %b) {
; CHECK: define i32 @Or
entry:
%or = or i32 %b, %a
; CHECK: or
tail call void @Use(i32 %or)
; CHECK: call Use
ret i32 %or
}
define i32 @Xor(i32 %a, i32 %b) {
; CHECK: define i32 @Xor
entry:
%xor = xor i32 %b, %a
; CHECK: xor
tail call void @Use(i32 %xor)
; CHECK: call Use
ret i32 %xor
}
define i32 @Sub(i32 %a, i32 %b) {
; CHECK: define i32 @Sub
entry:
%sub = sub i32 %a, %b
; CHECK: sub
tail call void @Use(i32 %sub)
; CHECK: call Use
ret i32 %sub
}
define i32 @Mul(i32 %a, i32 %b) {
; CHECK: define i32 @Mul
entry:
%mul = mul i32 %b, %a
; CHECK: imul
tail call void @Use(i32 %mul)
; CHECK: call Use
ret i32 %mul
}
define i32 @Sdiv(i32 %a, i32 %b) {
; CHECK: define i32 @Sdiv
entry:
%div = sdiv i32 %a, %b
; CHECK: cdq
; CHECK: idiv
tail call void @Use(i32 %div)
; CHECK: call Use
ret i32 %div
}
define i32 @Srem(i32 %a, i32 %b) {
; CHECK: define i32 @Srem
entry:
%rem = srem i32 %a, %b
; CHECK: cdq
; CHECK: idiv
tail call void @Use(i32 %rem)
; CHECK: call Use
ret i32 %rem
}
define i32 @Udiv(i32 %a, i32 %b) {
; CHECK: define i32 @Udiv
entry:
%div = udiv i32 %a, %b
; CHECK: div
tail call void @Use(i32 %div)
; CHECK: call Use
ret i32 %div
}
define i32 @Urem(i32 %a, i32 %b) {
; CHECK: define i32 @Urem
entry:
%rem = urem i32 %a, %b
; CHECK: div
tail call void @Use(i32 %rem)
; CHECK: call Use
ret i32 %rem
}
; ERRORS-NOT: ICE translation error
; DUMP-NOT: SZ
; RUIN: %llvm2ice -verbose inst %s | FileCheck %s
; RUIN: %llvm2ice --verbose none %s | FileCheck --check-prefix=ERRORS %s
; RUN: %szdiff --llvm2ice=%llvm2ice %s | FileCheck --check-prefix=DUMP %s
define i64 @arithmetic_chain(i64 %foo, i64 %bar) {
entry:
%r1 = add i64 %foo, %bar
%r2 = add i64 %foo, %r1
%r3 = mul i64 %bar, %r1
%r4 = shl i64 %r3, %r2
%r5 = add i64 %r4, 8
ret i64 %r5
; CHECK: entry:
; CHECK-NEXT: %r1 = add i64 %foo, %bar
; CHECK-NEXT: %r2 = add i64 %foo, %r1
; CHECK-NEXT: %r3 = mul i64 %bar, %r1
; CHECK-NEXT: %r4 = shl i64 %r3, %r2
; CHECK-NEXT: %r5 = add i64 %r4, 8
; CHECK-NEXT: ret i64 %r5
}
; ERRORS-NOT: ICE translation error
; DUMP-NOT: SZ
; RUIN: %llvm2ice -verbose inst %s | FileCheck %s
; RUIN: %llvm2ice --verbose none %s | FileCheck --check-prefix=ERRORS %s
; RUN: %szdiff --llvm2ice=%llvm2ice %s | FileCheck --check-prefix=DUMP %s
define internal i32 @cast_f2i(float %f) {
entry:
%v0 = bitcast float %f to i32
ret i32 %v0
}
define internal float @cast_i2f(i32 %i) {
entry:
%v0 = bitcast i32 %i to float
ret float %v0
}
define internal i64 @cast_d2ll(double %d) {
entry:
%v0 = bitcast double %d to i64
ret i64 %v0
}
define internal double @cast_ll2d(i64 %ll) {
entry:
%v0 = bitcast i64 %ll to double
ret double %v0
}
; ERRORS-NOT: ICE translation error
; DUMP-NOT: SZ
; RUIN: %llvm2ice -verbose inst %s | FileCheck %s
; RUIN: %llvm2ice --verbose none %s | FileCheck --check-prefix=ERRORS %s
; RUN: %szdiff --llvm2ice=%llvm2ice %s | FileCheck --check-prefix=DUMP %s
define void @testBool(i32 %a, i32 %b) {
entry:
%cmp = icmp eq i32 %a, %b
tail call void @use(i1 %cmp)
ret void
}
declare void @use(i1 zeroext) #1
; CHECK-NOT: ICE translation error
; ERRORS-NOT: ICE translation error
; DUMP-NOT: SZ
; RUIN: %llvm2ice %s -verbose inst | FileCheck %s
; RUIN: %llvm2ice --verbose none %s | FileCheck --check-prefix=ERRORS %s
; RUN: %szdiff --llvm2ice=%llvm2ice %s | FileCheck --check-prefix=DUMP %s
define i32 @simple_cond_branch(i32 %foo, i32 %bar) {
entry:
%r1 = icmp eq i32 %foo, %bar
br i1 %r1, label %Equal, label %Unequal
Equal:
ret i32 %foo
Unequal:
ret i32 %bar
; CHECK: br i1 %r1, label %Equal, label %Unequal
; CHECK: Equal:
; CHECK: ret i32 %foo
; CHECK: Unequal:
; CHECK: ret i32 %bar
}
; ERRORS-NOT: ICE translation error
; DUMP-NOT: SZ
; RUIN: %llvm2ice -verbose inst %s | FileCheck %s
; RUIN: %llvm2ice --verbose none %s | FileCheck --check-prefix=ERRORS %s
; RUN: %szdiff --llvm2ice=%llvm2ice %s | FileCheck --check-prefix=DUMP %s
define i32 @fib(i32 %n) {
; CHECK: define i32 @fib
entry:
%cmp = icmp slt i32 %n, 2
br i1 %cmp, label %return, label %if.end
if.end: ; preds = %entry
%sub = add i32 %n, -1
%call = tail call i32 @fib(i32 %sub)
%sub1 = add i32 %n, -2
%call2 = tail call i32 @fib(i32 %sub1)
%add = add i32 %call2, %call
ret i32 %add
return: ; preds = %entry
ret i32 %n
}
define i32 @fact(i32 %n) {
; CHECK: define i32 @fact
entry:
%cmp = icmp slt i32 %n, 2
br i1 %cmp, label %return, label %if.end
if.end: ; preds = %entry
%sub = add i32 %n, -1
%call = tail call i32 @fact(i32 %sub)
%mul = mul i32 %call, %n
ret i32 %mul
return: ; preds = %entry
ret i32 %n
}
define i32 @redirect(i32 %n) {
; CHECK: define i32 @redirect
entry:
%call = tail call i32 @redirect_target(i32 %n)
ret i32 %call
}
declare i32 @redirect_target(i32)
define void @call_void(i32 %n) {
; CHECK: define void @call_void
entry:
%cmp2 = icmp sgt i32 %n, 0
br i1 %cmp2, label %if.then, label %if.end
if.then: ; preds = %entry, %if.then
%n.tr3 = phi i32 [ %call.i, %if.then ], [ %n, %entry ]
%sub = add i32 %n.tr3, -1
%call.i = tail call i32 @redirect_target(i32 %sub)
%cmp = icmp sgt i32 %call.i, 0
br i1 %cmp, label %if.then, label %if.end
if.end: ; preds = %if.then, %entry
ret void
}
; ERRORS-NOT: ICE translation error
; DUMP-NOT: SZ
; RUIN: %llvm2ice --verbose none %s | FileCheck %s
; RUIN: %llvm2ice --verbose none %s | FileCheck --check-prefix=ERRORS %s
; RUN: %szdiff --llvm2ice=%llvm2ice %s | FileCheck --check-prefix=DUMP %s
@__init_array_start = internal constant [0 x i8] zeroinitializer, align 4
@__fini_array_start = internal constant [0 x i8] zeroinitializer, align 4
@__tls_template_start = internal constant [0 x i8] zeroinitializer, align 8
@__tls_template_alignment = internal constant [4 x i8] c"\01\00\00\00", align 4
define internal void @CallIndirect(i32 %f) {
entry:
%f.asptr = inttoptr i32 %f to void ()*
call void %f.asptr()
call void %f.asptr()
call void %f.asptr()
call void %f.asptr()
call void %f.asptr()
ret void
}
; CHECK: call [[REGISTER:[a-z]+]]
; CHECK: call [[REGISTER]]
; CHECK: call [[REGISTER]]
; CHECK: call [[REGISTER]]
; CHECK: call [[REGISTER]]
; ERRORS-NOT: ICE translation error
; DUMP-NOT: SZ
; RUIN: %llvm2ice -verbose inst %s | FileCheck %s
; RUIN: %llvm2ice --verbose none %s | FileCheck --check-prefix=ERRORS %s
; RUN: %szdiff --llvm2ice=%llvm2ice %s | FileCheck --check-prefix=DUMP %s
define i64 @simple_zext(i32 %arg) {
entry:
%c = zext i32 %arg to i64
ret i64 %c
; CHECK: entry:
; CHECK-NEXT: %c = zext i32 %arg to i64
; CHECK-NEXT: ret i64 %c
}
; ERRORS-NOT: ICE translation error
; DUMP-NOT: SZ
; RUIN: %llvm2ice %s | FileCheck %s
; RUIN: %llvm2ice --verbose none %s | FileCheck --check-prefix=ERRORS %s
; RUN: %szdiff --llvm2ice=%llvm2ice %s | FileCheck --check-prefix=DUMP %s
define void @testBool(i32 %a, i32 %b) {
entry:
%cmp = icmp slt i32 %a, %b
%cmp1 = icmp sgt i32 %a, %b
br i1 %cmp, label %if.then, label %if.end
if.then: ; preds = %entry
tail call void @use(i1 %cmp)
br label %if.end
if.end: ; preds = %if.then, %entry
br i1 %cmp1, label %if.then5, label %if.end7
if.then5: ; preds = %if.end
tail call void @use(i1 %cmp1)
br label %if.end7
if.end7: ; preds = %if.then5, %if.end
ret void
}
declare void @use(i1 zeroext)
; ERRORS-NOT: ICE translation error
; CHECK: .globl testBool
; Two bool computations
; CHECK: cmp
; CHECK: cmp
; Test first bool
; CHECK: cmp
; CHECK: call
; Test second bool
; CHECK: cmp
; CHECK: call
; CHECK: ret
; DUMP-NOT: SZ
; RUIN: %llvm2ice %s | FileCheck %s
; RUIN: %llvm2ice --verbose none %s | FileCheck --check-prefix=ERRORS %s
; RUN: %szdiff --llvm2ice=%llvm2ice %s | FileCheck --check-prefix=DUMP %s
@i8v = common global i8 0, align 1
@i16v = common global i16 0, align 2
@i32v = common global i32 0, align 4
@i64v = common global i64 0, align 8
@u8v = common global i8 0, align 1
@u16v = common global i16 0, align 2
@u32v = common global i32 0, align 4
@u64v = common global i64 0, align 8
@i1 = common global i32 0, align 4
@i2 = common global i32 0, align 4
@u1 = common global i32 0, align 4
@u2 = common global i32 0, align 4
define void @from_int8() {
entry:
%v0 = load i8* @i8v, align 1
%v1 = sext i8 %v0 to i16
store i16 %v1, i16* @i16v, align 1
%v2 = sext i8 %v0 to i32
store i32 %v2, i32* @i32v, align 1
%v3 = sext i8 %v0 to i64
store i64 %v3, i64* @i64v, align 1
ret void
; CHECK: mov al, byte ptr [
; CHECK-NEXT: movsx cx, al
; CHECK-NEXT: mov word ptr [
; CHECK-NEXT: movsx ecx, al
; CHECK-NEXT: mov dword ptr [
; CHECK-NEXT: movsx ecx, al
; CHECK-NEXT: sar eax, 31
; CHECK-NEXT: mov dword ptr [i64v+4],
; CHECK-NEXT: mov dword ptr [i64v],
}
define void @from_int16() {
entry:
%v0 = load i16* @i16v, align 1
%v1 = trunc i16 %v0 to i8
store i8 %v1, i8* @i8v, align 1
%v2 = sext i16 %v0 to i32
store i32 %v2, i32* @i32v, align 1
%v3 = sext i16 %v0 to i64
store i64 %v3, i64* @i64v, align 1
ret void
; CHECK: mov ax, word ptr [
; CHECK-NEXT: mov cx, ax
; CHECK-NEXT: mov byte ptr [
; CHECK-NEXT: movsx ecx, ax
; CHECK-NEXT: mov dword ptr [
; CHECK-NEXT: movsx ecx, ax
; CHECK-NEXT: sar eax, 31
; CHECK-NEXT: mov dword ptr [i64v+4],
; CHECK-NEXT: mov dword ptr [i64v],
}
define void @from_int32() {
entry:
%v0 = load i32* @i32v, align 1
%v1 = trunc i32 %v0 to i8
store i8 %v1, i8* @i8v, align 1
%v2 = trunc i32 %v0 to i16
store i16 %v2, i16* @i16v, align 1
%v3 = sext i32 %v0 to i64
store i64 %v3, i64* @i64v, align 1
ret void
; CHECK: mov eax, dword ptr [
; CHECK-NEXT: mov ecx, eax
; CHECK-NEXT: mov byte ptr [
; CHECK-NEXT: mov ecx, eax
; CHECK-NEXT: mov word ptr [
; CHECK-NEXT: mov ecx, eax
; CHECK-NEXT: sar eax, 31
; CHECK-NEXT: mov dword ptr [i64v+4],
; CHECK-NEXT: mov dword ptr [i64v],
}
define void @from_int64() {
entry:
%v0 = load i64* @i64v, align 1
%v1 = trunc i64 %v0 to i8
store i8 %v1, i8* @i8v, align 1
%v2 = trunc i64 %v0 to i16
store i16 %v2, i16* @i16v, align 1
%v3 = trunc i64 %v0 to i32
store i32 %v3, i32* @i32v, align 1
ret void
; CHECK: mov eax, dword ptr [
; CHECK-NEXT: mov ecx, eax
; CHECK-NEXT: mov byte ptr [
; CHECK-NEXT: mov ecx, eax
; CHECK-NEXT: mov word ptr [
; CHECK-NEXT: mov dword ptr [
}
define void @from_uint8() {
entry:
%v0 = load i8* @u8v, align 1
%v1 = zext i8 %v0 to i16
store i16 %v1, i16* @i16v, align 1
%v2 = zext i8 %v0 to i32
store i32 %v2, i32* @i32v, align 1
%v3 = zext i8 %v0 to i64
store i64 %v3, i64* @i64v, align 1
ret void
; CHECK: mov al, byte ptr [
; CHECK-NEXT: movzx cx, al
; CHECK-NEXT: mov word ptr [
; CHECK-NEXT: movzx ecx, al
; CHECK-NEXT: mov dword ptr [
; CHECK-NEXT: movzx eax, al
; CHECK-NEXT: mov ecx, 0
; CHECK-NEXT: mov dword ptr [i64v+4],
; CHECK-NEXT: mov dword ptr [i64v],
}
define void @from_uint16() {
entry:
%v0 = load i16* @u16v, align 1
%v1 = trunc i16 %v0 to i8
store i8 %v1, i8* @i8v, align 1
%v2 = zext i16 %v0 to i32
store i32 %v2, i32* @i32v, align 1
%v3 = zext i16 %v0 to i64
store i64 %v3, i64* @i64v, align 1
ret void
; CHECK: mov ax, word ptr [
; CHECK-NEXT: mov cx, ax
; CHECK-NEXT: mov byte ptr [
; CHECK-NEXT: movzx ecx, ax
; CHECK-NEXT: mov dword ptr [
; CHECK-NEXT: movzx eax, ax
; CHECK-NEXT: mov ecx, 0
; CHECK-NEXT: mov dword ptr [i64v+4],
; CHECK-NEXT: mov dword ptr [i64v],
}
define void @from_uint32() {
entry:
%v0 = load i32* @u32v, align 1
%v1 = trunc i32 %v0 to i8
store i8 %v1, i8* @i8v, align 1
%v2 = trunc i32 %v0 to i16
store i16 %v2, i16* @i16v, align 1
%v3 = zext i32 %v0 to i64
store i64 %v3, i64* @i64v, align 1
ret void
; CHECK: mov eax, dword ptr [
; CHECK-NEXT: mov ecx, eax
; CHECK-NEXT: mov byte ptr [
; CHECK-NEXT: mov ecx, eax
; CHECK-NEXT: mov word ptr [
; CHECK-NEXT: mov ecx, 0
; CHECK-NEXT: mov dword ptr [i64v+4],
; CHECK-NEXT: mov dword ptr [i64v],
}
define void @from_uint64() {
entry:
%v0 = load i64* @u64v, align 1
%v1 = trunc i64 %v0 to i8
store i8 %v1, i8* @i8v, align 1
%v2 = trunc i64 %v0 to i16
store i16 %v2, i16* @i16v, align 1
%v3 = trunc i64 %v0 to i32
store i32 %v3, i32* @i32v, align 1
ret void
; CHECK: mov eax, dword ptr [
; CHECK-NEXT: mov ecx, eax
; CHECK-NEXT: mov byte ptr [
; CHECK-NEXT: mov ecx, eax
; CHECK-NEXT: mov word ptr [
; CHECK-NEXT: mov dword ptr [
}
; ERRORS-NOT: ICE translation error
; DUMP-NOT: SZ
; RUIN: %llvm2ice -verbose inst %s | FileCheck %s
; RUIN: %llvm2ice --verbose none %s | FileCheck --check-prefix=ERRORS %s
; RUN: %szdiff --llvm2ice=%llvm2ice %s | FileCheck --check-prefix=DUMP %s
define void @foo() {
; CHECK: define void @foo()
entry:
ret void
; CHECK: entry
; CHECK-NEXT: ret void
}
; ERRORS-NOT: ICE translation error
; DUMP-NOT: SZ
; RUIN: %llvm2ice -verbose inst %s | FileCheck %s
; RUIN: %llvm2ice --verbose none %s | FileCheck --check-prefix=ERRORS %s
; RUN: %szdiff --llvm2ice=%llvm2ice %s | FileCheck --check-prefix=DUMP %s
@intern_global = global i32 12, align 4
@extern_global = external global i32
define i32 @test_intern_global() {
; CHECK: define i32 @test_intern_global
entry:
%v0 = load i32* @intern_global, align 1
ret i32 %v0
}
define i32 @test_extern_global() {
; CHECK: define i32 @test_extern_global
entry:
%v0 = load i32* @extern_global, align 1
ret i32 %v0
}
; ERRORS-NOT: ICE translation error
; DUMP-NOT: SZ
; RUIN: %llvm2ice -verbose inst %s | FileCheck %s
; RUIN: %llvm2ice --verbose none %s | FileCheck --check-prefix=ERRORS %s
; RUN: %szdiff --llvm2ice=%llvm2ice %s | FileCheck --check-prefix=DUMP %s
define void @dummy_icmp(i64 %foo, i64 %bar) {
; CHECK: define void @dummy_icmp
entry:
%r1 = icmp eq i64 %foo, %bar
%r2 = icmp slt i64 %foo, %bar
ret void
; CHECK: entry:
; CHECK-NEXT: %r1 = icmp eq i64 %foo, %bar
; CHECK-NEXT: %r2 = icmp slt i64 %foo, %bar
; CHECK-NEXT: ret void
}
; ERRORS-NOT: ICE translation error
; DUMP-NOT: SZ
; RUIN: %llvm2ice -verbose inst %s | FileCheck %s
; RUIN: %llvm2ice --verbose none %s | FileCheck --check-prefix=ERRORS %s
; RUN: %szdiff --llvm2ice=%llvm2ice %s | FileCheck --check-prefix=DUMP %s
define void @dummy_inttoptr(i32 %addr_arg) {
entry:
%ptr = inttoptr i32 %addr_arg to i32*
ret void
; CHECK: %ptr = i32 %addr_arg
}
; ERRORS-NOT: ICE translation error
; DUMP-NOT: SZ
; RUIN: %llvm2ice -verbose inst %s | FileCheck %s
; RUIN: %llvm2ice --verbose none %s | FileCheck --check-prefix=ERRORS %s
; RUN: %szdiff --llvm2ice=%llvm2ice %s | FileCheck --check-prefix=DUMP %s
define void @load_i64(i32 %addr_arg) {
entry:
%ptr64 = inttoptr i32 %addr_arg to i64*
%iv = load i64* %ptr64, align 1
ret void
; CHECK: %ptr64 = i32 %addr_arg
; CHECK-NEXT: %iv = load i64* {{.*}}, align 1
; CHECK-NEXT: ret void
}
define void @load_i32(i32 %addr_arg) {
entry:
%ptr32 = inttoptr i32 %addr_arg to i32*
%iv = load i32* %ptr32, align 1
ret void
; CHECK: %ptr32 = i32 %addr_arg
; CHECK-NEXT: %iv = load i32* {{.*}}, align 1
; CHECK-NEXT: ret void
}
define void @load_i16(i32 %addr_arg) {
entry:
%ptr16 = inttoptr i32 %addr_arg to i16*
%iv = load i16* %ptr16, align 1
ret void
; CHECK: %ptr16 = i32 %addr_arg
; CHECK-NEXT: %iv = load i16* {{.*}}, align 1
; CHECK-NEXT: ret void
}
define void @load_i8(i32 %addr_arg) {
entry:
%ptr8 = inttoptr i32 %addr_arg to i8*
%iv = load i8* %ptr8, align 1
ret void
; CHECK: %ptr8 = i32 %addr_arg
; CHECK-NEXT: %iv = load i8* {{.*}}, align 1
; CHECK-NEXT: ret void
}
; ERRORS-NOT: ICE translation error
; DUMP-NOT: SZ
; RUIN: %llvm2ice -verbose inst %s | FileCheck %s
; RUIN: %llvm2ice --verbose none %s | FileCheck --check-prefix=ERRORS %s
; RUN: %szdiff --llvm2ice=%llvm2ice %s | FileCheck --check-prefix=DUMP %s
define i32 @func_single_arg(i32 %a) {
; CHECK: define i32 @func_single_arg
entry:
ret i32 %a
; CHECK: ret i32 %a
}
define i32 @func_multiple_args(i32 %a, i32 %b, i32 %c) {
; CHECK: func_multiple_args
entry:
ret i32 %c
; CHECK: ret i32 %c
}
; ERRORS-NOT: ICE translation error
; DUMP-NOT: SZ
; RUIN: %llvm2ice %s | FileCheck %s
; RUIN: %llvm2ice --verbose none %s | FileCheck --check-prefix=ERRORS %s
; RUN: %szdiff --llvm2ice=%llvm2ice %s | FileCheck --check-prefix=DUMP %s
define void @testSelect(i32 %a, i32 %b) {
entry:
%cmp = icmp slt i32 %a, %b
%cond = select i1 %cmp, i32 %a, i32 %b
tail call void @useInt(i32 %cond)
%cmp1 = icmp sgt i32 %a, %b
%cond2 = select i1 %cmp1, i32 10, i32 20
tail call void @useInt(i32 %cond2)
ret void
}
declare void @useInt(i32)
; CHECK: .globl testSelect
; CHECK: cmp
; CHECK: cmp
; CHECK: call useInt
; CHECK: cmp
; CHECK: cmp
; CHECK: call useInt
; CHECK: ret
; ERRORS-NOT: ICE translation error
; DUMP-NOT: SZ
; RUIN: %llvm2ice -verbose inst %s | FileCheck %s
; RUIN: %llvm2ice --verbose none %s | FileCheck --check-prefix=ERRORS %s
; RUN: %szdiff --llvm2ice=%llvm2ice %s | FileCheck --check-prefix=DUMP %s
@i1 = common global i32 0, align 4
@i2 = common global i32 0, align 4
@u1 = common global i32 0, align 4
@u2 = common global i32 0, align 4
define void @conv1() {
entry:
%v0 = load i32* @u1, align 1
%sext = shl i32 %v0, 24
%v1 = ashr i32 %sext, 24
store i32 %v1, i32* @i1, align 1
ret void
; CHECK: shl eax, 24
; CHECK-NEXT: sar eax, 24
}
define void @conv2() {
entry:
%v0 = load i32* @u1, align 1
%sext1 = shl i32 %v0, 16
%v1 = ashr i32 %sext1, 16
store i32 %v1, i32* @i2, align 1
ret void
; CHECK: shl eax, 16
; CHECK-NEXT: sar eax, 16
}
; ERRORS-NOT: ICE translation error
; DUMP-NOT: SZ
; RUIN: %llvm2ice -verbose inst %s | FileCheck %s
; RUIN: %llvm2ice --verbose none %s | FileCheck --check-prefix=ERRORS %s
; RUN: %szdiff --llvm2ice=%llvm2ice %s | FileCheck --check-prefix=DUMP %s
define i64 @add_args_i64(i64 %arg1, i64 %arg2) {
entry:
%add = add i64 %arg2, %arg1
ret i64 %add
}
; Checks for verbose instruction output
; CHECK: define i64 @add_args
; CHECK: %add = add i64 %arg2, %arg1
; CHECK-NEXT: ret i64 %add
define i32 @add_args_i32(i32 %arg1, i32 %arg2) {
entry:
%add = add i32 %arg2, %arg1
ret i32 %add
}
; Checks for emitted assembly
; CHECK: .globl add_args_i32
; CHECK: mov eax, dword ptr [esp+4]
; CHECK-NEXT: mov ecx, dword ptr [esp+8]
; CHECK-NEXT: add ecx, eax
; CHECK-NEXT: mov eax, ecx
; CHECK-NEXT: ret
; ERRORS-NOT: ICE translation error
; DUMP-NOT: SZ
; RUIN: %llvm2ice -verbose inst %s | FileCheck %s
; RUIN: %llvm2ice --verbose none %s | FileCheck --check-prefix=ERRORS %s
; RUN: %szdiff --llvm2ice=%llvm2ice %s | FileCheck --check-prefix=DUMP %s
define internal i32 @simple_cond(i32 %a, i32 %n) {
entry:
%cmp = icmp slt i32 %n, 0
; CHECK: %cmp = icmp slt i32 %n, 0
br i1 %cmp, label %if.then, label %if.else
; CHECK-NEXT: br i1 %cmp, label %if.then, label %if.else
if.then:
%sub = sub i32 1, %n
br label %if.end
if.else:
%gep_array = mul i32 %n, 4
%gep = add i32 %a, %gep_array
%gep.asptr = inttoptr i32 %gep to i32*
%v0 = load i32* %gep.asptr, align 1
br label %if.end
if.end:
%result.0 = phi i32 [ %sub, %if.then ], [ %v0, %if.else ]
; CHECK: %result.0 = phi i32 [ %sub, %if.then ], [ %v0, %if.else ]
ret i32 %result.0
}
; ERRORS-NOT: ICE translation error
; DUMP-NOT: SZ
; RUIN: %llvm2ice -verbose inst %s | FileCheck %s
; RUIN: %llvm2ice --verbose none %s | FileCheck --check-prefix=ERRORS %s
; RUN: %szdiff --llvm2ice=%llvm2ice %s | FileCheck --check-prefix=DUMP %s
define i32 @simple_loop(i32 %a, i32 %n) {
entry:
%cmp4 = icmp sgt i32 %n, 0
br i1 %cmp4, label %for.body, label %for.end
for.body:
%i.06 = phi i32 [ %inc, %for.body ], [ 0, %entry ]
%sum.05 = phi i32 [ %add, %for.body ], [ 0, %entry ]
%gep_array = mul i32 %i.06, 4
%gep = add i32 %a, %gep_array
%gep.asptr = inttoptr i32 %gep to i32*
%v0 = load i32* %gep.asptr, align 1
%add = add i32 %v0, %sum.05
%inc = add i32 %i.06, 1
%cmp = icmp slt i32 %inc, %n
br i1 %cmp, label %for.body, label %for.end
for.end:
%sum.0.lcssa = phi i32 [ 0, %entry ], [ %add, %for.body ]
ret i32 %sum.0.lcssa
}
; Checks for verbose instruction output
; CHECK: br i1 %cmp4, label %for.body, label %for.end
; CHECK-NEXT: for.body
; CHECK: %i.06 = phi i32 [ %inc, %for.body ], [ 0, %entry ]
; CHECK-NEXT: %sum.05 = phi i32 [ %add, %for.body ], [ 0, %entry ]
; Checks for emitted assembly
; CHECK: .globl simple_loop
; CHECK: mov ecx, dword ptr [esp+{{[0-9]+}}]
; CHECK: cmp ecx, 0
; CHECK-NEXT: jg {{.*}}for.body
; CHECK-NEXT: jmp {{.*}}for.end
; TODO: the mov from ebx to esi seems redundant here - so this may need to be
; modified later
; CHECK: add [[IREG:[a-z]+]], 1
; CHECK-NEXT: mov [[ICMPREG:[a-z]+]], [[IREG]]
; CHECK: cmp [[ICMPREG]], ecx
; CHECK-NEXT: jl {{.*}}for.body
; ERRORS-NOT: ICE translation error
; DUMP-NOT: SZ
; RUIN: %llvm2ice %s -verbose inst | FileCheck %s
; RUIN: %llvm2ice --verbose none %s | FileCheck --check-prefix=ERRORS %s
; RUN: %szdiff --llvm2ice=%llvm2ice %s | FileCheck --check-prefix=DUMP %s
define void @store_i64(i32 %addr_arg) {
entry:
%ptr64 = inttoptr i32 %addr_arg to i64*
store i64 1, i64* %ptr64, align 1
ret void
; CHECK: %ptr64 = i32 %addr_arg
; CHECK-NEXT: store i64 1, {{.*}}, align 1
; CHECK-NEXT: ret void
}
define void @store_i32(i32 %addr_arg) {
entry:
%ptr32 = inttoptr i32 %addr_arg to i32*
store i32 1, i32* %ptr32, align 1
ret void
; CHECK: %ptr32 = i32 %addr_arg
; CHECK-NEXT: store i32 1, {{.*}}, align 1
; CHECK-NEXT: ret void
}
define void @store_i16(i32 %addr_arg) {
entry:
%ptr16 = inttoptr i32 %addr_arg to i16*
store i16 1, i16* %ptr16, align 1
ret void
; CHECK: %ptr16 = i32 %addr_arg
; CHECK-NEXT: store i16 1, {{.*}}, align 1
; CHECK-NEXT: ret void
}
define void @store_i8(i32 %addr_arg) {
entry:
%ptr8 = inttoptr i32 %addr_arg to i8*
store i8 1, i8* %ptr8, align 1
ret void
; CHECK: %ptr8 = i32 %addr_arg
; CHECK-NEXT: store i8 1, {{.*}}, align 1
; CHECK-NEXT: ret void
}
; ERRORS-NOT: ICE translation error
; DUMP-NOT: SZ
; RUIN: %llvm2ice -verbose inst %s | FileCheck %s
; RUIN: %llvm2ice --verbose none %s | FileCheck --check-prefix=ERRORS %s
; RUN: %szdiff --llvm2ice=%llvm2ice %s | FileCheck --check-prefix=DUMP %s
; This file is lowered from C code that does some simple aritmetic with
; struct members. It's also built with the PNaCl toolchain so this is the
; stable ABI subset of LLVM IR (structs are gone, pointers turned into i32,
; geps gone, etc.)
define internal i32 @compute_important_function(i32 %v1, i32 %v2) {
entry:
%v1.asptr = inttoptr i32 %v1 to i32*
%_v0 = load i32* %v1.asptr, align 1
; CHECK: entry:
; CHECK-NEXT: %v1.asptr = i32 %v1
; CHECK-NEXT: %_v0 = load i32* {{.*}}, align 1
%v2.asptr = inttoptr i32 %v2 to i32*
%_v1 = load i32* %v2.asptr, align 1
%gep = add i32 %v2, 12
%gep.asptr = inttoptr i32 %gep to i32*
%_v2 = load i32* %gep.asptr, align 1
%mul = mul i32 %_v2, %_v1
%gep6 = add i32 %v1, 4
%gep6.asptr = inttoptr i32 %gep6 to i32*
%_v3 = load i32* %gep6.asptr, align 1
%gep8 = add i32 %v2, 8
%gep8.asptr = inttoptr i32 %gep8 to i32*
%_v4 = load i32* %gep8.asptr, align 1
%gep10 = add i32 %v2, 4
%gep10.asptr = inttoptr i32 %gep10 to i32*
%_v5 = load i32* %gep10.asptr, align 1
%mul3 = mul i32 %_v5, %_v4
%gep12 = add i32 %v1, 8
%gep12.asptr = inttoptr i32 %gep12 to i32*
%_v6 = load i32* %gep12.asptr, align 1
%mul7 = mul i32 %_v6, %_v3
%mul9 = mul i32 %mul7, %_v6
%gep14 = add i32 %v1, 12
%gep14.asptr = inttoptr i32 %gep14 to i32*
%_v7 = load i32* %gep14.asptr, align 1
%mul11 = mul i32 %mul9, %_v7
%add4.neg = add i32 %mul, %_v0
%add = sub i32 %add4.neg, %_v3
%sub = sub i32 %add, %mul3
%sub12 = sub i32 %sub, %mul11
ret i32 %sub12
; CHECK: %sub12 = sub i32 %sub, %mul11
; CHECK-NEXT: ret i32 %sub12
}
; ERRORS-NOT: ICE translation error
; DUMP-NOT: SZ
; RUIN: %llvm2ice %s | FileCheck %s
; RUIN: %llvm2ice --verbose none %s | FileCheck --check-prefix=ERRORS %s
; RUN: %szdiff --llvm2ice=%llvm2ice %s | FileCheck --check-prefix=DUMP %s
define i32 @testSwitch(i32 %a) {
entry:
switch i32 %a, label %sw.default [
i32 1, label %sw.epilog
i32 2, label %sw.epilog
i32 3, label %sw.epilog
i32 7, label %sw.bb1
i32 8, label %sw.bb1
i32 15, label %sw.bb2
i32 14, label %sw.bb2
]
sw.default: ; preds = %entry
%add = add i32 %a, 27
br label %sw.epilog
sw.bb1: ; preds = %entry, %entry
%phitmp = sub i32 21, %a
br label %sw.bb2
sw.bb2: ; preds = %sw.bb1, %entry, %entry
%result.0 = phi i32 [ 1, %entry ], [ 1, %entry ], [ %phitmp, %sw.bb1 ]
br label %sw.epilog
sw.epilog: ; preds = %sw.bb2, %sw.default, %entry, %entry, %entry
%result.1 = phi i32 [ %add, %sw.default ], [ %result.0, %sw.bb2 ], [ 17, %entry ], [ 17, %entry ], [ 17, %entry ]
ret i32 %result.1
}
; CHECK-NOT: ICE translation error
; ERRORS-NOT: ICE translation error
; DUMP-NOT: SZ
; RUIN: %llvm2ice -verbose inst %s | FileCheck %s
; RUIN: %llvm2ice --verbose none %s | FileCheck --check-prefix=ERRORS %s
; RUN: %szdiff --llvm2ice=%llvm2ice %s | FileCheck --check-prefix=DUMP %s
define internal i32 @divide(i32 %num, i32 %den) {
entry:
%cmp = icmp ne i32 %den, 0
br i1 %cmp, label %return, label %abort
abort: ; preds = %entry
unreachable
return: ; preds = %entry
%div = sdiv i32 %num, %den
ret i32 %div
}
; ERRORS-NOT: ICE translation error
; DUMP-NOT: SZ
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