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
//===- subzero/src/IceInst.cpp - High-level instruction 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 Inst class, primarily the various
// subclass constructors and dump routines.
//
//===----------------------------------------------------------------------===//
#include "IceCfg.h"
#include "IceCfgNode.h"
#include "IceInst.h"
#include "IceOperand.h"
namespace Ice {
namespace {
// Using non-anonymous struct so that array_lengthof works.
const struct _InstArithmeticAttributes {
const char *DisplayString;
bool IsCommutative;
} InstArithmeticAttributes[] = {
#define X(tag, str, commutative) \
{ str, commutative } \
,
ICEINSTARITHMETIC_TABLE
#undef X
};
const size_t InstArithmeticAttributesSize =
llvm::array_lengthof(InstArithmeticAttributes);
// Using non-anonymous struct so that array_lengthof works.
const struct _InstCastAttributes {
const char *DisplayString;
} InstCastAttributes[] = {
#define X(tag, str) \
{ str } \
,
ICEINSTCAST_TABLE
#undef X
};
const size_t InstCastAttributesSize = llvm::array_lengthof(InstCastAttributes);
// Using non-anonymous struct so that array_lengthof works.
const struct _InstFcmpAttributes {
const char *DisplayString;
} InstFcmpAttributes[] = {
#define X(tag, str) \
{ str } \
,
ICEINSTFCMP_TABLE
#undef X
};
const size_t InstFcmpAttributesSize = llvm::array_lengthof(InstFcmpAttributes);
// Using non-anonymous struct so that array_lengthof works.
const struct _InstIcmpAttributes {
const char *DisplayString;
} InstIcmpAttributes[] = {
#define X(tag, str) \
{ str } \
,
ICEINSTICMP_TABLE
#undef X
};
const size_t InstIcmpAttributesSize = llvm::array_lengthof(InstIcmpAttributes);
} // end of anonymous namespace
Inst::Inst(Cfg *Func, InstKind Kind, SizeT MaxSrcs, Variable *Dest)
: Kind(Kind), Number(Func->newInstNumber()), Deleted(false),
HasSideEffects(false), Dest(Dest), MaxSrcs(MaxSrcs), NumSrcs(0),
Srcs(Func->allocateArrayOf<Operand *>(MaxSrcs)) {}
void Inst::updateVars(CfgNode *Node) {
if (Dest)
Dest->setDefinition(this, Node);
SizeT VarIndex = 0;
for (SizeT I = 0; I < getSrcSize(); ++I) {
Operand *Src = getSrc(I);
SizeT NumVars = Src->getNumVars();
for (SizeT J = 0; J < NumVars; ++J, ++VarIndex) {
Variable *Var = Src->getVar(J);
Var->setUse(this, Node);
}
}
}
InstAlloca::InstAlloca(Cfg *Func, Operand *ByteCount, uint32_t AlignInBytes,
Variable *Dest)
: Inst(Func, Inst::Alloca, 1, Dest), AlignInBytes(AlignInBytes) {
// Verify AlignInBytes is 0 or a power of 2.
assert(AlignInBytes == 0 || llvm::isPowerOf2_32(AlignInBytes));
addSource(ByteCount);
}
InstArithmetic::InstArithmetic(Cfg *Func, OpKind Op, Variable *Dest,
Operand *Source1, Operand *Source2)
: Inst(Func, Inst::Arithmetic, 2, Dest), Op(Op) {
addSource(Source1);
addSource(Source2);
}
bool InstArithmetic::isCommutative() const {
return InstArithmeticAttributes[getOp()].IsCommutative;
}
InstAssign::InstAssign(Cfg *Func, Variable *Dest, Operand *Source)
: Inst(Func, Inst::Assign, 1, Dest) {
addSource(Source);
}
// If TargetTrue==TargetFalse, we turn it into an unconditional
// branch. This ensures that, along with the 'switch' instruction
// semantics, there is at most one edge from one node to another.
InstBr::InstBr(Cfg *Func, Operand *Source, CfgNode *TargetTrue,
CfgNode *TargetFalse)
: Inst(Func, Inst::Br, 1, NULL), TargetFalse(TargetFalse),
TargetTrue(TargetTrue) {
if (TargetTrue == TargetFalse) {
TargetTrue = NULL; // turn into unconditional version
} else {
addSource(Source);
}
}
InstBr::InstBr(Cfg *Func, CfgNode *Target)
: Inst(Func, Inst::Br, 0, NULL), TargetFalse(Target), TargetTrue(NULL) {}
NodeList InstBr::getTerminatorEdges() const {
NodeList OutEdges;
OutEdges.push_back(TargetFalse);
if (TargetTrue)
OutEdges.push_back(TargetTrue);
return OutEdges;
}
InstCast::InstCast(Cfg *Func, OpKind CastKind, Variable *Dest, Operand *Source)
: Inst(Func, Inst::Cast, 1, Dest), CastKind(CastKind) {
addSource(Source);
}
InstFcmp::InstFcmp(Cfg *Func, FCond Condition, Variable *Dest, Operand *Source1,
Operand *Source2)
: Inst(Func, Inst::Fcmp, 2, Dest), Condition(Condition) {
addSource(Source1);
addSource(Source2);
}
InstIcmp::InstIcmp(Cfg *Func, ICond Condition, Variable *Dest, Operand *Source1,
Operand *Source2)
: Inst(Func, Inst::Icmp, 2, Dest), Condition(Condition) {
addSource(Source1);
addSource(Source2);
}
InstLoad::InstLoad(Cfg *Func, Variable *Dest, Operand *SourceAddr)
: Inst(Func, Inst::Load, 1, Dest) {
addSource(SourceAddr);
}
InstPhi::InstPhi(Cfg *Func, SizeT MaxSrcs, Variable *Dest)
: Inst(Func, Phi, MaxSrcs, Dest) {
Labels = Func->allocateArrayOf<CfgNode *>(MaxSrcs);
}
// TODO: A Switch instruction (and maybe others) can add duplicate
// edges. We may want to de-dup Phis and validate consistency (i.e.,
// the source operands are the same for duplicate edges), though it
// seems the current lowering code is OK with this situation.
void InstPhi::addArgument(Operand *Source, CfgNode *Label) {
Labels[getSrcSize()] = Label;
addSource(Source);
}
InstRet::InstRet(Cfg *Func, Operand *RetValue)
: Inst(Func, Ret, RetValue ? 1 : 0, NULL) {
if (RetValue)
addSource(RetValue);
}
InstSelect::InstSelect(Cfg *Func, Variable *Dest, Operand *Condition,
Operand *SourceTrue, Operand *SourceFalse)
: Inst(Func, Inst::Select, 3, Dest) {
assert(Condition->getType() == IceType_i1);
addSource(Condition);
addSource(SourceTrue);
addSource(SourceFalse);
}
InstStore::InstStore(Cfg *Func, Operand *Data, Operand *Addr)
: Inst(Func, Inst::Store, 2, NULL) {
addSource(Data);
addSource(Addr);
}
InstSwitch::InstSwitch(Cfg *Func, SizeT NumCases, Operand *Source,
CfgNode *LabelDefault)
: Inst(Func, Inst::Switch, 1, NULL), LabelDefault(LabelDefault),
NumCases(NumCases) {
addSource(Source);
Values = Func->allocateArrayOf<uint64_t>(NumCases);
Labels = Func->allocateArrayOf<CfgNode *>(NumCases);
// Initialize in case buggy code doesn't set all entries
for (SizeT I = 0; I < NumCases; ++I) {
Values[I] = 0;
Labels[I] = NULL;
}
}
void InstSwitch::addBranch(SizeT CaseIndex, uint64_t Value, CfgNode *Label) {
assert(CaseIndex < NumCases);
Values[CaseIndex] = Value;
Labels[CaseIndex] = Label;
}
NodeList InstSwitch::getTerminatorEdges() const {
NodeList OutEdges;
OutEdges.push_back(LabelDefault);
for (SizeT I = 0; I < NumCases; ++I) {
OutEdges.push_back(Labels[I]);
}
return OutEdges;
}
InstUnreachable::InstUnreachable(Cfg *Func)
: Inst(Func, Inst::Unreachable, 0, NULL) {}
// ======================== Dump routines ======================== //
void Inst::dumpDecorated(const Cfg *Func) const {
Ostream &Str = Func->getContext()->getStrDump();
if (!Func->getContext()->isVerbose(IceV_Deleted) && isDeleted())
return;
if (Func->getContext()->isVerbose(IceV_InstNumbers)) {
char buf[30];
int32_t Number = getNumber();
if (Number < 0)
snprintf(buf, llvm::array_lengthof(buf), "[XXX]");
else
snprintf(buf, llvm::array_lengthof(buf), "[%3d]", Number);
Str << buf;
}
Str << " ";
if (isDeleted())
Str << " //";
dump(Func);
Str << "\n";
}
void Inst::dump(const Cfg *Func) const {
Ostream &Str = Func->getContext()->getStrDump();
dumpDest(Func);
Str << " =~ ";
dumpSources(Func);
}
void Inst::dumpSources(const Cfg *Func) const {
Ostream &Str = Func->getContext()->getStrDump();
for (SizeT I = 0; I < getSrcSize(); ++I) {
if (I > 0)
Str << ", ";
getSrc(I)->dump(Func);
}
}
void Inst::dumpDest(const Cfg *Func) const {
if (getDest())
getDest()->dump(Func);
}
void InstAlloca::dump(const Cfg *Func) const {
Ostream &Str = Func->getContext()->getStrDump();
dumpDest(Func);
Str << " = alloca i8, i32 ";
getSizeInBytes()->dump(Func);
Str << ", align " << getAlignInBytes();
}
void InstArithmetic::dump(const Cfg *Func) const {
Ostream &Str = Func->getContext()->getStrDump();
dumpDest(Func);
Str << " = " << InstArithmeticAttributes[getOp()].DisplayString << " "
<< getDest()->getType() << " ";
dumpSources(Func);
}
void InstAssign::dump(const Cfg *Func) const {
Ostream &Str = Func->getContext()->getStrDump();
dumpDest(Func);
Str << " = " << getDest()->getType() << " ";
dumpSources(Func);
}
void InstBr::dump(const Cfg *Func) const {
Ostream &Str = Func->getContext()->getStrDump();
dumpDest(Func);
Str << "br ";
if (!isUnconditional()) {
Str << "i1 ";
getCondition()->dump(Func);
Str << ", label %" << getTargetTrue()->getName() << ", ";
}
Str << "label %" << getTargetFalse()->getName();
}
void InstCall::dump(const Cfg *Func) const {
Ostream &Str = Func->getContext()->getStrDump();
if (getDest()) {
dumpDest(Func);
Str << " = ";
}
Str << "call ";
if (getDest())
Str << getDest()->getType();
else
Str << "void";
Str << " ";
getCallTarget()->dump(Func);
Str << "(";
for (SizeT I = 0; I < getNumArgs(); ++I) {
if (I > 0)
Str << ", ";
Str << getArg(I)->getType() << " ";
getArg(I)->dump(Func);
}
Str << ")";
}
void InstCast::dump(const Cfg *Func) const {
Ostream &Str = Func->getContext()->getStrDump();
dumpDest(Func);
Str << " = " << InstCastAttributes[getCastKind()].DisplayString << " "
<< getSrc(0)->getType() << " ";
dumpSources(Func);
Str << " to " << getDest()->getType();
}
void InstIcmp::dump(const Cfg *Func) const {
Ostream &Str = Func->getContext()->getStrDump();
dumpDest(Func);
Str << " = icmp " << InstIcmpAttributes[getCondition()].DisplayString << " "
<< getSrc(0)->getType() << " ";
dumpSources(Func);
}
void InstFcmp::dump(const Cfg *Func) const {
Ostream &Str = Func->getContext()->getStrDump();
dumpDest(Func);
Str << " = fcmp " << InstFcmpAttributes[getCondition()].DisplayString << " "
<< getSrc(0)->getType() << " ";
dumpSources(Func);
}
void InstLoad::dump(const Cfg *Func) const {
Ostream &Str = Func->getContext()->getStrDump();
dumpDest(Func);
Type Ty = getDest()->getType();
Str << " = load " << Ty << "* ";
dumpSources(Func);
Str << ", align " << typeAlignInBytes(Ty);
}
void InstStore::dump(const Cfg *Func) const {
Ostream &Str = Func->getContext()->getStrDump();
Type Ty = getData()->getType();
Str << "store " << Ty << " ";
getData()->dump(Func);
Str << ", " << Ty << "* ";
getAddr()->dump(Func);
Str << ", align " << typeAlignInBytes(Ty);
}
void InstSwitch::dump(const Cfg *Func) const {
Ostream &Str = Func->getContext()->getStrDump();
Type Ty = getComparison()->getType();
Str << "switch " << Ty << " ";
getSrc(0)->dump(Func);
Str << ", label %" << getLabelDefault()->getName() << " [\n";
for (SizeT I = 0; I < getNumCases(); ++I) {
Str << " " << Ty << " " << getValue(I) << ", label %"
<< getLabel(I)->getName() << "\n";
}
Str << " ]";
}
void InstPhi::dump(const Cfg *Func) const {
Ostream &Str = Func->getContext()->getStrDump();
dumpDest(Func);
Str << " = phi " << getDest()->getType() << " ";
for (SizeT I = 0; I < getSrcSize(); ++I) {
if (I > 0)
Str << ", ";
Str << "[ ";
getSrc(I)->dump(Func);
Str << ", %" << Labels[I]->getName() << " ]";
}
}
void InstRet::dump(const Cfg *Func) const {
Ostream &Str = Func->getContext()->getStrDump();
Type Ty = hasRetValue() ? getSrc(0)->getType() : IceType_void;
Str << "ret " << Ty;
if (hasRetValue()) {
Str << " ";
dumpSources(Func);
}
}
void InstSelect::dump(const Cfg *Func) const {
Ostream &Str = Func->getContext()->getStrDump();
dumpDest(Func);
Operand *Condition = getCondition();
Operand *TrueOp = getTrueOperand();
Operand *FalseOp = getFalseOperand();
Str << " = select " << Condition->getType() << " ";
Condition->dump(Func);
Str << ", " << TrueOp->getType() << " ";
TrueOp->dump(Func);
Str << ", " << FalseOp->getType() << " ";
FalseOp->dump(Func);
}
void InstUnreachable::dump(const Cfg *Func) const {
Ostream &Str = Func->getContext()->getStrDump();
Str << "unreachable";
}
} // end of namespace Ice
//===- 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
//===- subzero/src/IceInst.h - High-level 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 declares the Inst class and its target-independent
// subclasses, which represent the high-level Vanilla ICE instructions
// and map roughly 1:1 to LLVM instructions.
//
//===----------------------------------------------------------------------===//
#ifndef SUBZERO_SRC_ICEINST_H
#define SUBZERO_SRC_ICEINST_H
#include "IceDefs.h"
#include "IceInst.def"
#include "IceTypes.h"
// TODO: The Cfg structure, and instructions in particular, need to be
// validated for things like valid operand types, valid branch
// targets, proper ordering of Phi and non-Phi instructions, etc.
// Most of the validity checking will be done in the bitcode reader.
// We need a list of everything that should be validated, and tests
// for each.
namespace Ice {
class Inst {
public:
enum InstKind {
// Arbitrary (alphabetical) order, except put Unreachable first.
Unreachable,
Alloca,
Arithmetic,
Assign, // not part of LLVM/PNaCl bitcode
Br,
Call,
Cast,
Fcmp,
Icmp,
Load,
Phi,
Ret,
Select,
Store,
Switch
};
InstKind getKind() const { return Kind; }
int32_t getNumber() const { return Number; }
bool isDeleted() const { return Deleted; }
void setDeleted() { Deleted = true; }
bool hasSideEffects() const { return HasSideEffects; }
Variable *getDest() const { return Dest; }
SizeT getSrcSize() const { return NumSrcs; }
Operand *getSrc(SizeT I) const {
assert(I < getSrcSize());
return Srcs[I];
}
// Returns a list of out-edges corresponding to a terminator
// instruction, which is the last instruction of the block.
virtual NodeList getTerminatorEdges() const {
// All valid terminator instructions override this method. For
// the default implementation, we assert in case some CfgNode
// is constructed without a terminator instruction at the end.
llvm_unreachable(
"getTerminatorEdges() called on a non-terminator instruction");
return NodeList();
}
// Updates the status of the Variables contained within the
// instruction. In particular, it marks where the Dest variable is
// first assigned, and it tracks whether variables are live across
// basic blocks, i.e. used in a different block from their definition.
void updateVars(CfgNode *Node);
virtual void dump(const Cfg *Func) const;
void dumpDecorated(const Cfg *Func) const;
void dumpSources(const Cfg *Func) const;
void dumpDest(const Cfg *Func) const;
virtual ~Inst() {}
protected:
Inst(Cfg *Func, InstKind Kind, SizeT MaxSrcs, Variable *Dest);
void addSource(Operand *Src) {
assert(Src);
assert(NumSrcs < MaxSrcs);
Srcs[NumSrcs++] = Src;
}
// The destroy() method lets the instruction cleanly release any
// memory that was allocated via the Cfg's allocator.
virtual void destroy(Cfg *Func) { Func->deallocateArrayOf<Operand *>(Srcs); }
const InstKind Kind;
// Number is the instruction number for describing live ranges.
int32_t Number;
// Deleted means irrevocably deleted.
bool Deleted;
// HasSideEffects means the instruction is something like a function
// call or a volatile load that can't be removed even if its Dest
// variable is not live.
bool HasSideEffects;
Variable *Dest;
const SizeT MaxSrcs; // only used for assert
SizeT NumSrcs;
Operand **Srcs;
private:
Inst(const Inst &) LLVM_DELETED_FUNCTION;
Inst &operator=(const Inst &) LLVM_DELETED_FUNCTION;
};
// Alloca instruction. This captures the size in bytes as getSrc(0),
// and the required alignment in bytes. The alignment must be either
// 0 (no alignment required) or a power of 2.
class InstAlloca : public Inst {
public:
static InstAlloca *create(Cfg *Func, Operand *ByteCount,
uint32_t AlignInBytes, Variable *Dest) {
return new (Func->allocateInst<InstAlloca>())
InstAlloca(Func, ByteCount, AlignInBytes, Dest);
}
uint32_t getAlignInBytes() const { return AlignInBytes; }
Operand *getSizeInBytes() const { return getSrc(0); }
virtual void dump(const Cfg *Func) const;
static bool classof(const Inst *Inst) { return Inst->getKind() == Alloca; }
private:
InstAlloca(Cfg *Func, Operand *ByteCount, uint32_t AlignInBytes,
Variable *Dest);
InstAlloca(const InstAlloca &) LLVM_DELETED_FUNCTION;
InstAlloca &operator=(const InstAlloca &) LLVM_DELETED_FUNCTION;
virtual ~InstAlloca() {}
const uint32_t AlignInBytes;
};
// Binary arithmetic instruction. The source operands are captured in
// getSrc(0) and getSrc(1).
class InstArithmetic : public Inst {
public:
enum OpKind {
#define X(tag, str, commutative) tag,
ICEINSTARITHMETIC_TABLE
#undef X
};
static InstArithmetic *create(Cfg *Func, OpKind Op, Variable *Dest,
Operand *Source1, Operand *Source2) {
return new (Func->allocateInst<InstArithmetic>())
InstArithmetic(Func, Op, Dest, Source1, Source2);
}
OpKind getOp() const { return Op; }
bool isCommutative() const;
virtual void dump(const Cfg *Func) const;
static bool classof(const Inst *Inst) {
return Inst->getKind() == Arithmetic;
}
private:
InstArithmetic(Cfg *Func, OpKind Op, Variable *Dest, Operand *Source1,
Operand *Source2);
InstArithmetic(const InstArithmetic &) LLVM_DELETED_FUNCTION;
InstArithmetic &operator=(const InstArithmetic &) LLVM_DELETED_FUNCTION;
virtual ~InstArithmetic() {}
const OpKind Op;
};
// Assignment instruction. The source operand is captured in
// getSrc(0). This is not part of the LLVM bitcode, but is a useful
// abstraction for some of the lowering. E.g., if Phi instruction
// lowering happens before target lowering, or for representing an
// Inttoptr instruction, or as an intermediate step for lowering a
// Load instruction.
class InstAssign : public Inst {
public:
static InstAssign *create(Cfg *Func, Variable *Dest, Operand *Source) {
return new (Func->allocateInst<InstAssign>())
InstAssign(Func, Dest, Source);
}
virtual void dump(const Cfg *Func) const;
static bool classof(const Inst *Inst) { return Inst->getKind() == Assign; }
private:
InstAssign(Cfg *Func, Variable *Dest, Operand *Source);
InstAssign(const InstAssign &) LLVM_DELETED_FUNCTION;
InstAssign &operator=(const InstAssign &) LLVM_DELETED_FUNCTION;
virtual ~InstAssign() {}
};
// Branch instruction. This represents both conditional and
// unconditional branches.
class InstBr : public Inst {
public:
// Create a conditional branch. If TargetTrue==TargetFalse, it is
// optimized to an unconditional branch.
static InstBr *create(Cfg *Func, Operand *Source, CfgNode *TargetTrue,
CfgNode *TargetFalse) {
return new (Func->allocateInst<InstBr>())
InstBr(Func, Source, TargetTrue, TargetFalse);
}
// Create an unconditional branch.
static InstBr *create(Cfg *Func, CfgNode *Target) {
return new (Func->allocateInst<InstBr>()) InstBr(Func, Target);
}
bool isUnconditional() const { return getTargetTrue() == NULL; }
Operand *getCondition() const {
assert(!isUnconditional());
return getSrc(0);
}
CfgNode *getTargetTrue() const { return TargetTrue; }
CfgNode *getTargetFalse() const { return TargetFalse; }
CfgNode *getTargetUnconditional() const {
assert(isUnconditional());
return getTargetFalse();
}
virtual NodeList getTerminatorEdges() const;
virtual void dump(const Cfg *Func) const;
static bool classof(const Inst *Inst) { return Inst->getKind() == Br; }
private:
// Conditional branch
InstBr(Cfg *Func, Operand *Source, CfgNode *TargetTrue, CfgNode *TargetFalse);
// Unconditional branch
InstBr(Cfg *Func, CfgNode *Target);
InstBr(const InstBr &) LLVM_DELETED_FUNCTION;
InstBr &operator=(const InstBr &) LLVM_DELETED_FUNCTION;
virtual ~InstBr() {}
CfgNode *const TargetFalse; // Doubles as unconditional branch target
CfgNode *const TargetTrue; // NULL if unconditional branch
};
// Call instruction. The call target is captured as getSrc(0), and
// arg I is captured as getSrc(I+1).
class InstCall : public Inst {
public:
static InstCall *create(Cfg *Func, SizeT NumArgs, Variable *Dest,
Operand *CallTarget) {
return new (Func->allocateInst<InstCall>())
InstCall(Func, NumArgs, Dest, CallTarget);
}
void addArg(Operand *Arg) { addSource(Arg); }
Operand *getCallTarget() const { return getSrc(0); }
Operand *getArg(SizeT I) const { return getSrc(I + 1); }
SizeT getNumArgs() const { return getSrcSize() - 1; }
virtual void dump(const Cfg *Func) const;
static bool classof(const Inst *Inst) { return Inst->getKind() == Call; }
private:
InstCall(Cfg *Func, SizeT NumArgs, Variable *Dest, Operand *CallTarget)
: Inst(Func, Inst::Call, NumArgs + 1, Dest) {
// Set HasSideEffects so that the call instruction can't be
// dead-code eliminated. Don't set this for a deletable intrinsic
// call.
HasSideEffects = true;
addSource(CallTarget);
}
InstCall(const InstCall &) LLVM_DELETED_FUNCTION;
InstCall &operator=(const InstCall &) LLVM_DELETED_FUNCTION;
virtual ~InstCall() {}
};
// Cast instruction (a.k.a. conversion operation).
class InstCast : public Inst {
public:
enum OpKind {
#define X(tag, str) tag,
ICEINSTCAST_TABLE
#undef X
};
static InstCast *create(Cfg *Func, OpKind CastKind, Variable *Dest,
Operand *Source) {
return new (Func->allocateInst<InstCast>())
InstCast(Func, CastKind, Dest, Source);
}
OpKind getCastKind() const { return CastKind; }
virtual void dump(const Cfg *Func) const;
static bool classof(const Inst *Inst) { return Inst->getKind() == Cast; }
private:
InstCast(Cfg *Func, OpKind CastKind, Variable *Dest, Operand *Source);
InstCast(const InstCast &) LLVM_DELETED_FUNCTION;
InstCast &operator=(const InstCast &) LLVM_DELETED_FUNCTION;
virtual ~InstCast() {}
const OpKind CastKind;
};
// Floating-point comparison instruction. The source operands are
// captured in getSrc(0) and getSrc(1).
class InstFcmp : public Inst {
public:
enum FCond {
#define X(tag, str) tag,
ICEINSTFCMP_TABLE
#undef X
};
static InstFcmp *create(Cfg *Func, FCond Condition, Variable *Dest,
Operand *Source1, Operand *Source2) {
return new (Func->allocateInst<InstFcmp>())
InstFcmp(Func, Condition, Dest, Source1, Source2);
}
FCond getCondition() const { return Condition; }
virtual void dump(const Cfg *Func) const;
static bool classof(const Inst *Inst) { return Inst->getKind() == Fcmp; }
private:
InstFcmp(Cfg *Func, FCond Condition, Variable *Dest, Operand *Source1,
Operand *Source2);
InstFcmp(const InstFcmp &) LLVM_DELETED_FUNCTION;
InstFcmp &operator=(const InstFcmp &) LLVM_DELETED_FUNCTION;
virtual ~InstFcmp() {}
const FCond Condition;
};
// Integer comparison instruction. The source operands are captured
// in getSrc(0) and getSrc(1).
class InstIcmp : public Inst {
public:
enum ICond {
#define X(tag, str) tag,
ICEINSTICMP_TABLE
#undef X
};
static InstIcmp *create(Cfg *Func, ICond Condition, Variable *Dest,
Operand *Source1, Operand *Source2) {
return new (Func->allocateInst<InstIcmp>())
InstIcmp(Func, Condition, Dest, Source1, Source2);
}
ICond getCondition() const { return Condition; }
virtual void dump(const Cfg *Func) const;
static bool classof(const Inst *Inst) { return Inst->getKind() == Icmp; }
private:
InstIcmp(Cfg *Func, ICond Condition, Variable *Dest, Operand *Source1,
Operand *Source2);
InstIcmp(const InstIcmp &) LLVM_DELETED_FUNCTION;
InstIcmp &operator=(const InstIcmp &) LLVM_DELETED_FUNCTION;
virtual ~InstIcmp() {}
const ICond Condition;
};
// Load instruction. The source address is captured in getSrc(0).
class InstLoad : public Inst {
public:
static InstLoad *create(Cfg *Func, Variable *Dest, Operand *SourceAddr) {
return new (Func->allocateInst<InstLoad>())
InstLoad(Func, Dest, SourceAddr);
}
Operand *getSourceAddress() const { return getSrc(0); }
virtual void dump(const Cfg *Func) const;
static bool classof(const Inst *Inst) { return Inst->getKind() == Load; }
private:
InstLoad(Cfg *Func, Variable *Dest, Operand *SourceAddr);
InstLoad(const InstLoad &) LLVM_DELETED_FUNCTION;
InstLoad &operator=(const InstLoad &) LLVM_DELETED_FUNCTION;
virtual ~InstLoad() {}
};
// Phi instruction. For incoming edge I, the node is Labels[I] and
// the Phi source operand is getSrc(I).
class InstPhi : public Inst {
public:
static InstPhi *create(Cfg *Func, SizeT MaxSrcs, Variable *Dest) {
return new (Func->allocateInst<InstPhi>()) InstPhi(Func, MaxSrcs, Dest);
}
void addArgument(Operand *Source, CfgNode *Label);
virtual void dump(const Cfg *Func) const;
static bool classof(const Inst *Inst) { return Inst->getKind() == Phi; }
private:
InstPhi(Cfg *Func, SizeT MaxSrcs, Variable *Dest);
InstPhi(const InstPhi &) LLVM_DELETED_FUNCTION;
InstPhi &operator=(const InstPhi &) LLVM_DELETED_FUNCTION;
virtual void destroy(Cfg *Func) {
Func->deallocateArrayOf<CfgNode *>(Labels);
Inst::destroy(Func);
}
virtual ~InstPhi() {}
// Labels[] duplicates the InEdges[] information in the enclosing
// CfgNode, but the Phi instruction is created before InEdges[]
// is available, so it's more complicated to share the list.
CfgNode **Labels;
};
// Ret instruction. The return value is captured in getSrc(0), but if
// there is no return value (void-type function), then
// getSrcSize()==0 and hasRetValue()==false.
class InstRet : public Inst {
public:
static InstRet *create(Cfg *Func, Operand *RetValue = NULL) {
return new (Func->allocateInst<InstRet>()) InstRet(Func, RetValue);
}
bool hasRetValue() const { return getSrcSize(); }
Operand *getRetValue() const {
assert(hasRetValue());
return getSrc(0);
}
virtual NodeList getTerminatorEdges() const { return NodeList(); }
virtual void dump(const Cfg *Func) const;
static bool classof(const Inst *Inst) { return Inst->getKind() == Ret; }
private:
InstRet(Cfg *Func, Operand *RetValue);
InstRet(const InstRet &) LLVM_DELETED_FUNCTION;
InstRet &operator=(const InstRet &) LLVM_DELETED_FUNCTION;
virtual ~InstRet() {}
};
// Select instruction. The condition, true, and false operands are captured.
class InstSelect : public Inst {
public:
static InstSelect *create(Cfg *Func, Variable *Dest, Operand *Condition,
Operand *SourceTrue, Operand *SourceFalse) {
return new (Func->allocateInst<InstSelect>())
InstSelect(Func, Dest, Condition, SourceTrue, SourceFalse);
}
Operand *getCondition() const { return getSrc(0); }
Operand *getTrueOperand() const { return getSrc(1); }
Operand *getFalseOperand() const { return getSrc(2); }
virtual void dump(const Cfg *Func) const;
static bool classof(const Inst *Inst) { return Inst->getKind() == Select; }
private:
InstSelect(Cfg *Func, Variable *Dest, Operand *Condition, Operand *Source1,
Operand *Source2);
InstSelect(const InstSelect &) LLVM_DELETED_FUNCTION;
InstSelect &operator=(const InstSelect &) LLVM_DELETED_FUNCTION;
virtual ~InstSelect() {}
};
// Store instruction. The address operand is captured, along with the
// data operand to be stored into the address.
class InstStore : public Inst {
public:
static InstStore *create(Cfg *Func, Operand *Data, Operand *Addr) {
return new (Func->allocateInst<InstStore>()) InstStore(Func, Data, Addr);
}
Operand *getAddr() const { return getSrc(1); }
Operand *getData() const { return getSrc(0); }
virtual void dump(const Cfg *Func) const;
static bool classof(const Inst *Inst) { return Inst->getKind() == Store; }
private:
InstStore(Cfg *Func, Operand *Data, Operand *Addr);
InstStore(const InstStore &) LLVM_DELETED_FUNCTION;
InstStore &operator=(const InstStore &) LLVM_DELETED_FUNCTION;
virtual ~InstStore() {}
};
// Switch instruction. The single source operand is captured as
// getSrc(0).
class InstSwitch : public Inst {
public:
static InstSwitch *create(Cfg *Func, SizeT NumCases, Operand *Source,
CfgNode *LabelDefault) {
return new (Func->allocateInst<InstSwitch>())
InstSwitch(Func, NumCases, Source, LabelDefault);
}
Operand *getComparison() const { return getSrc(0); }
CfgNode *getLabelDefault() const { return LabelDefault; }
SizeT getNumCases() const { return NumCases; }
uint64_t getValue(SizeT I) const {
assert(I < NumCases);
return Values[I];
}
CfgNode *getLabel(SizeT I) const {
assert(I < NumCases);
return Labels[I];
}
void addBranch(SizeT CaseIndex, uint64_t Value, CfgNode *Label);
virtual NodeList getTerminatorEdges() const;
virtual void dump(const Cfg *Func) const;
static bool classof(const Inst *Inst) { return Inst->getKind() == Switch; }
private:
InstSwitch(Cfg *Func, SizeT NumCases, Operand *Source, CfgNode *LabelDefault);
InstSwitch(const InstSwitch &) LLVM_DELETED_FUNCTION;
InstSwitch &operator=(const InstSwitch &) LLVM_DELETED_FUNCTION;
virtual void destroy(Cfg *Func) {
Func->deallocateArrayOf<uint64_t>(Values);
Func->deallocateArrayOf<CfgNode *>(Labels);
Inst::destroy(Func);
}
virtual ~InstSwitch() {}
CfgNode *LabelDefault;
SizeT NumCases; // not including the default case
uint64_t *Values; // size is NumCases
CfgNode **Labels; // size is NumCases
};
// Unreachable instruction. This is a terminator instruction with no
// operands.
class InstUnreachable : public Inst {
public:
static InstUnreachable *create(Cfg *Func) {
return new (Func->allocateInst<InstUnreachable>()) InstUnreachable(Func);
}
virtual NodeList getTerminatorEdges() const { return NodeList(); }
virtual void dump(const Cfg *Func) const;
static bool classof(const Inst *Inst) {
return Inst->getKind() == Unreachable;
}
private:
InstUnreachable(Cfg *Func);
InstUnreachable(const InstUnreachable &) LLVM_DELETED_FUNCTION;
InstUnreachable &operator=(const InstUnreachable &) LLVM_DELETED_FUNCTION;
virtual ~InstUnreachable() {}
};
} // end of namespace Ice
#endif // SUBZERO_SRC_ICEINST_H
//===- 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
//===- subzero/src/llvm2ice.cpp - Driver for testing ----------------------===//
//
// 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 driver that uses LLVM capabilities to parse a
// bitcode file and build the LLVM IR, and then convert the LLVM basic
// blocks, instructions, and operands into their Subzero equivalents.
//
//===----------------------------------------------------------------------===//
#include "IceCfg.h"
#include "IceCfgNode.h"
#include "IceDefs.h"
#include "IceGlobalContext.h"
#include "IceInst.h"
#include "IceOperand.h"
#include "IceTypes.h"
#include "llvm/IR/Constant.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/Instruction.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IRReader/IRReader.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_os_ostream.h"
#include "llvm/Support/SourceMgr.h"
#include <fstream>
#include <iostream>
using namespace llvm;
// Debugging helper
template <typename T> static std::string LLVMObjectAsString(const T *O) {
std::string Dump;
raw_string_ostream Stream(Dump);
O->print(Stream);
return Stream.str();
}
// Converter from LLVM to ICE. The entry point is the convertFunction method.
//
// Note: this currently assumes that the given IR was verified to be valid PNaCl
// bitcode:
// https://developers.google.com/native-client/dev/reference/pnacl-bitcode-abi
// If not, all kinds of assertions may fire.
//
class LLVM2ICEConverter {
public:
LLVM2ICEConverter(Ice::GlobalContext *Ctx)
: Ctx(Ctx), Func(NULL), CurrentNode(NULL) {
// All PNaCl pointer widths are 32 bits because of the sandbox
// model.
SubzeroPointerType = Ice::IceType_i32;
}
Ice::Cfg *convertFunction(const Function *F) {
VarMap.clear();
NodeMap.clear();
Func = new Ice::Cfg(Ctx);
Func->setFunctionName(F->getName());
Func->setReturnType(convertType(F->getReturnType()));
Func->setInternal(F->hasInternalLinkage());
// The initial definition/use of each arg is the entry node.
CurrentNode = mapBasicBlockToNode(&F->getEntryBlock());
for (Function::const_arg_iterator ArgI = F->arg_begin(),
ArgE = F->arg_end();
ArgI != ArgE; ++ArgI) {
Func->addArg(mapValueToIceVar(ArgI));
}
// Make an initial pass through the block list just to resolve the
// blocks in the original linearized order. Otherwise the ICE
// linearized order will be affected by branch targets in
// terminator instructions.
for (Function::const_iterator BBI = F->begin(), BBE = F->end(); BBI != BBE;
++BBI) {
mapBasicBlockToNode(BBI);
}
for (Function::const_iterator BBI = F->begin(), BBE = F->end(); BBI != BBE;
++BBI) {
CurrentNode = mapBasicBlockToNode(BBI);
convertBasicBlock(BBI);
}
Func->setEntryNode(mapBasicBlockToNode(&F->getEntryBlock()));
Func->computePredecessors();
return Func;
}
private:
// LLVM values (instructions, etc.) are mapped directly to ICE variables.
// mapValueToIceVar has a version that forces an ICE type on the variable,
// and a version that just uses convertType on V.
Ice::Variable *mapValueToIceVar(const Value *V, Ice::Type IceTy) {
if (IceTy == Ice::IceType_void)
return NULL;
if (VarMap.find(V) == VarMap.end()) {
assert(CurrentNode);
VarMap[V] = Func->makeVariable(IceTy, CurrentNode, V->getName());
}
return VarMap[V];
}
Ice::Variable *mapValueToIceVar(const Value *V) {
return mapValueToIceVar(V, convertType(V->getType()));
}
Ice::CfgNode *mapBasicBlockToNode(const BasicBlock *BB) {
if (NodeMap.find(BB) == NodeMap.end()) {
NodeMap[BB] = Func->makeNode(BB->getName());
}
return NodeMap[BB];
}
Ice::Type convertIntegerType(const IntegerType *IntTy) const {
switch (IntTy->getBitWidth()) {
case 1:
return Ice::IceType_i1;
case 8:
return Ice::IceType_i8;
case 16:
return Ice::IceType_i16;
case 32:
return Ice::IceType_i32;
case 64:
return Ice::IceType_i64;
default:
report_fatal_error(std::string("Invalid PNaCl int type: ") +
LLVMObjectAsString(IntTy));
return Ice::IceType_void;
}
}
Ice::Type convertType(const Type *Ty) const {
switch (Ty->getTypeID()) {
case Type::VoidTyID:
return Ice::IceType_void;
case Type::IntegerTyID:
return convertIntegerType(cast<IntegerType>(Ty));
case Type::FloatTyID:
return Ice::IceType_f32;
case Type::DoubleTyID:
return Ice::IceType_f64;
case Type::PointerTyID:
return SubzeroPointerType;
case Type::FunctionTyID:
return SubzeroPointerType;
default:
report_fatal_error(std::string("Invalid PNaCl type: ") +
LLVMObjectAsString(Ty));
}
llvm_unreachable("convertType");
return Ice::IceType_void;
}
// Given a LLVM instruction and an operand number, produce the Operand this
// refers to. If there's no such operand, return NULL.
Ice::Operand *convertOperand(const Instruction *Inst, unsigned OpNum) {
if (OpNum >= Inst->getNumOperands()) {
return NULL;
}
const Value *Op = Inst->getOperand(OpNum);
return convertValue(Op);
}
Ice::Operand *convertValue(const Value *Op) {
if (const Constant *Const = dyn_cast<Constant>(Op)) {
if (const GlobalValue *GV = dyn_cast<GlobalValue>(Const)) {
return Ctx->getConstantSym(convertType(GV->getType()), 0,
GV->getName());
} else if (const ConstantInt *CI = dyn_cast<ConstantInt>(Const)) {
return Ctx->getConstantInt(convertIntegerType(CI->getType()),
CI->getZExtValue());
} else if (const ConstantFP *CFP = dyn_cast<ConstantFP>(Const)) {
Ice::Type Type = convertType(CFP->getType());
if (Type == Ice::IceType_f32)
return Ctx->getConstantFloat(CFP->getValueAPF().convertToFloat());
else if (Type == Ice::IceType_f64)
return Ctx->getConstantDouble(CFP->getValueAPF().convertToDouble());
assert(0 && "Unexpected floating point type");
return NULL;
} else {
assert(0 && "Unhandled constant type");
return NULL;
}
} else {
return mapValueToIceVar(Op);
}
}
// Note: this currently assumes a 1x1 mapping between LLVM IR and Ice
// instructions.
Ice::Inst *convertInstruction(const Instruction *Inst) {
switch (Inst->getOpcode()) {
case Instruction::PHI:
return convertPHINodeInstruction(cast<PHINode>(Inst));
case Instruction::Br:
return convertBrInstruction(cast<BranchInst>(Inst));
case Instruction::Ret:
return convertRetInstruction(cast<ReturnInst>(Inst));
case Instruction::IntToPtr:
return convertIntToPtrInstruction(cast<IntToPtrInst>(Inst));
case Instruction::PtrToInt:
return convertPtrToIntInstruction(cast<PtrToIntInst>(Inst));
case Instruction::ICmp:
return convertICmpInstruction(cast<ICmpInst>(Inst));
case Instruction::FCmp:
return convertFCmpInstruction(cast<FCmpInst>(Inst));
case Instruction::Select:
return convertSelectInstruction(cast<SelectInst>(Inst));
case Instruction::Switch:
return convertSwitchInstruction(cast<SwitchInst>(Inst));
case Instruction::Load:
return convertLoadInstruction(cast<LoadInst>(Inst));
case Instruction::Store:
return convertStoreInstruction(cast<StoreInst>(Inst));
case Instruction::ZExt:
return convertCastInstruction(cast<ZExtInst>(Inst), Ice::InstCast::Zext);
case Instruction::SExt:
return convertCastInstruction(cast<SExtInst>(Inst), Ice::InstCast::Sext);
case Instruction::Trunc:
return convertCastInstruction(cast<TruncInst>(Inst),
Ice::InstCast::Trunc);
case Instruction::FPTrunc:
return convertCastInstruction(cast<FPTruncInst>(Inst),
Ice::InstCast::Fptrunc);
case Instruction::FPExt:
return convertCastInstruction(cast<FPExtInst>(Inst),
Ice::InstCast::Fpext);
case Instruction::FPToSI:
return convertCastInstruction(cast<FPToSIInst>(Inst),
Ice::InstCast::Fptosi);
case Instruction::FPToUI:
return convertCastInstruction(cast<FPToUIInst>(Inst),
Ice::InstCast::Fptoui);
case Instruction::SIToFP:
return convertCastInstruction(cast<SIToFPInst>(Inst),
Ice::InstCast::Sitofp);
case Instruction::UIToFP:
return convertCastInstruction(cast<UIToFPInst>(Inst),
Ice::InstCast::Uitofp);
case Instruction::BitCast:
return convertCastInstruction(cast<BitCastInst>(Inst),
Ice::InstCast::Bitcast);
case Instruction::Add:
return convertArithInstruction(Inst, Ice::InstArithmetic::Add);
case Instruction::Sub:
return convertArithInstruction(Inst, Ice::InstArithmetic::Sub);
case Instruction::Mul:
return convertArithInstruction(Inst, Ice::InstArithmetic::Mul);
case Instruction::UDiv:
return convertArithInstruction(Inst, Ice::InstArithmetic::Udiv);
case Instruction::SDiv:
return convertArithInstruction(Inst, Ice::InstArithmetic::Sdiv);
case Instruction::URem:
return convertArithInstruction(Inst, Ice::InstArithmetic::Urem);
case Instruction::SRem:
return convertArithInstruction(Inst, Ice::InstArithmetic::Srem);
case Instruction::Shl:
return convertArithInstruction(Inst, Ice::InstArithmetic::Shl);
case Instruction::LShr:
return convertArithInstruction(Inst, Ice::InstArithmetic::Lshr);
case Instruction::AShr:
return convertArithInstruction(Inst, Ice::InstArithmetic::Ashr);
case Instruction::FAdd:
return convertArithInstruction(Inst, Ice::InstArithmetic::Fadd);
case Instruction::FSub:
return convertArithInstruction(Inst, Ice::InstArithmetic::Fsub);
case Instruction::FMul:
return convertArithInstruction(Inst, Ice::InstArithmetic::Fmul);
case Instruction::FDiv:
return convertArithInstruction(Inst, Ice::InstArithmetic::Fdiv);
case Instruction::FRem:
return convertArithInstruction(Inst, Ice::InstArithmetic::Frem);
case Instruction::And:
return convertArithInstruction(Inst, Ice::InstArithmetic::And);
case Instruction::Or:
return convertArithInstruction(Inst, Ice::InstArithmetic::Or);
case Instruction::Xor:
return convertArithInstruction(Inst, Ice::InstArithmetic::Xor);
case Instruction::Call:
return convertCallInstruction(cast<CallInst>(Inst));
case Instruction::Alloca:
return convertAllocaInstruction(cast<AllocaInst>(Inst));
case Instruction::Unreachable:
return convertUnreachableInstruction(cast<UnreachableInst>(Inst));
default:
report_fatal_error(std::string("Invalid PNaCl instruction: ") +
LLVMObjectAsString(Inst));
}
llvm_unreachable("convertInstruction");
return NULL;
}
Ice::Inst *convertLoadInstruction(const LoadInst *Inst) {
Ice::Operand *Src = convertOperand(Inst, 0);
Ice::Variable *Dest = mapValueToIceVar(Inst);
return Ice::InstLoad::create(Func, Dest, Src);
}
Ice::Inst *convertStoreInstruction(const StoreInst *Inst) {
Ice::Operand *Addr = convertOperand(Inst, 1);
Ice::Operand *Val = convertOperand(Inst, 0);
return Ice::InstStore::create(Func, Val, Addr);
}
Ice::Inst *convertArithInstruction(const Instruction *Inst,
Ice::InstArithmetic::OpKind Opcode) {
const BinaryOperator *BinOp = cast<BinaryOperator>(Inst);
Ice::Operand *Src0 = convertOperand(Inst, 0);
Ice::Operand *Src1 = convertOperand(Inst, 1);
Ice::Variable *Dest = mapValueToIceVar(BinOp);
return Ice::InstArithmetic::create(Func, Opcode, Dest, Src0, Src1);
}
Ice::Inst *convertPHINodeInstruction(const PHINode *Inst) {
unsigned NumValues = Inst->getNumIncomingValues();
Ice::InstPhi *IcePhi =
Ice::InstPhi::create(Func, NumValues, mapValueToIceVar(Inst));
for (unsigned N = 0, E = NumValues; N != E; ++N) {
IcePhi->addArgument(convertOperand(Inst, N),
mapBasicBlockToNode(Inst->getIncomingBlock(N)));
}
return IcePhi;
}
Ice::Inst *convertBrInstruction(const BranchInst *Inst) {
if (Inst->isConditional()) {
Ice::Operand *Src = convertOperand(Inst, 0);
BasicBlock *BBThen = Inst->getSuccessor(0);
BasicBlock *BBElse = Inst->getSuccessor(1);
Ice::CfgNode *NodeThen = mapBasicBlockToNode(BBThen);
Ice::CfgNode *NodeElse = mapBasicBlockToNode(BBElse);
return Ice::InstBr::create(Func, Src, NodeThen, NodeElse);
} else {
BasicBlock *BBSucc = Inst->getSuccessor(0);
return Ice::InstBr::create(Func, mapBasicBlockToNode(BBSucc));
}
}
Ice::Inst *convertIntToPtrInstruction(const IntToPtrInst *Inst) {
Ice::Operand *Src = convertOperand(Inst, 0);
Ice::Variable *Dest = mapValueToIceVar(Inst, SubzeroPointerType);
return Ice::InstAssign::create(Func, Dest, Src);
}
Ice::Inst *convertPtrToIntInstruction(const PtrToIntInst *Inst) {
Ice::Operand *Src = convertOperand(Inst, 0);
Ice::Variable *Dest = mapValueToIceVar(Inst);
return Ice::InstAssign::create(Func, Dest, Src);
}
Ice::Inst *convertRetInstruction(const ReturnInst *Inst) {
Ice::Operand *RetOperand = convertOperand(Inst, 0);
if (RetOperand) {
return Ice::InstRet::create(Func, RetOperand);
} else {
return Ice::InstRet::create(Func);
}
}
Ice::Inst *convertCastInstruction(const Instruction *Inst,
Ice::InstCast::OpKind CastKind) {
Ice::Operand *Src = convertOperand(Inst, 0);
Ice::Variable *Dest = mapValueToIceVar(Inst);
return Ice::InstCast::create(Func, CastKind, Dest, Src);
}
Ice::Inst *convertICmpInstruction(const ICmpInst *Inst) {
Ice::Operand *Src0 = convertOperand(Inst, 0);
Ice::Operand *Src1 = convertOperand(Inst, 1);
Ice::Variable *Dest = mapValueToIceVar(Inst);
Ice::InstIcmp::ICond Cond;
switch (Inst->getPredicate()) {
default:
llvm_unreachable("ICmpInst predicate");
case CmpInst::ICMP_EQ:
Cond = Ice::InstIcmp::Eq;
break;
case CmpInst::ICMP_NE:
Cond = Ice::InstIcmp::Ne;
break;
case CmpInst::ICMP_UGT:
Cond = Ice::InstIcmp::Ugt;
break;
case CmpInst::ICMP_UGE:
Cond = Ice::InstIcmp::Uge;
break;
case CmpInst::ICMP_ULT:
Cond = Ice::InstIcmp::Ult;
break;
case CmpInst::ICMP_ULE:
Cond = Ice::InstIcmp::Ule;
break;
case CmpInst::ICMP_SGT:
Cond = Ice::InstIcmp::Sgt;
break;
case CmpInst::ICMP_SGE:
Cond = Ice::InstIcmp::Sge;
break;
case CmpInst::ICMP_SLT:
Cond = Ice::InstIcmp::Slt;
break;
case CmpInst::ICMP_SLE:
Cond = Ice::InstIcmp::Sle;
break;
}
return Ice::InstIcmp::create(Func, Cond, Dest, Src0, Src1);
}
Ice::Inst *convertFCmpInstruction(const FCmpInst *Inst) {
Ice::Operand *Src0 = convertOperand(Inst, 0);
Ice::Operand *Src1 = convertOperand(Inst, 1);
Ice::Variable *Dest = mapValueToIceVar(Inst);
Ice::InstFcmp::FCond Cond;
switch (Inst->getPredicate()) {
default:
llvm_unreachable("FCmpInst predicate");
case CmpInst::FCMP_FALSE:
Cond = Ice::InstFcmp::False;
break;
case CmpInst::FCMP_OEQ:
Cond = Ice::InstFcmp::Oeq;
break;
case CmpInst::FCMP_OGT:
Cond = Ice::InstFcmp::Ogt;
break;
case CmpInst::FCMP_OGE:
Cond = Ice::InstFcmp::Oge;
break;
case CmpInst::FCMP_OLT:
Cond = Ice::InstFcmp::Olt;
break;
case CmpInst::FCMP_OLE:
Cond = Ice::InstFcmp::Ole;
break;
case CmpInst::FCMP_ONE:
Cond = Ice::InstFcmp::One;
break;
case CmpInst::FCMP_ORD:
Cond = Ice::InstFcmp::Ord;
break;
case CmpInst::FCMP_UEQ:
Cond = Ice::InstFcmp::Ueq;
break;
case CmpInst::FCMP_UGT:
Cond = Ice::InstFcmp::Ugt;
break;
case CmpInst::FCMP_UGE:
Cond = Ice::InstFcmp::Uge;
break;
case CmpInst::FCMP_ULT:
Cond = Ice::InstFcmp::Ult;
break;
case CmpInst::FCMP_ULE:
Cond = Ice::InstFcmp::Ule;
break;
case CmpInst::FCMP_UNE:
Cond = Ice::InstFcmp::Une;
break;
case CmpInst::FCMP_UNO:
Cond = Ice::InstFcmp::Uno;
break;
case CmpInst::FCMP_TRUE:
Cond = Ice::InstFcmp::True;
break;
}
return Ice::InstFcmp::create(Func, Cond, Dest, Src0, Src1);
}
Ice::Inst *convertSelectInstruction(const SelectInst *Inst) {
Ice::Variable *Dest = mapValueToIceVar(Inst);
Ice::Operand *Cond = convertValue(Inst->getCondition());
Ice::Operand *Source1 = convertValue(Inst->getTrueValue());
Ice::Operand *Source2 = convertValue(Inst->getFalseValue());
return Ice::InstSelect::create(Func, Dest, Cond, Source1, Source2);
}
Ice::Inst *convertSwitchInstruction(const SwitchInst *Inst) {
Ice::Operand *Source = convertValue(Inst->getCondition());
Ice::CfgNode *LabelDefault = mapBasicBlockToNode(Inst->getDefaultDest());
unsigned NumCases = Inst->getNumCases();
Ice::InstSwitch *Switch =
Ice::InstSwitch::create(Func, NumCases, Source, LabelDefault);
unsigned CurrentCase = 0;
for (SwitchInst::ConstCaseIt I = Inst->case_begin(), E = Inst->case_end();
I != E; ++I, ++CurrentCase) {
uint64_t CaseValue = I.getCaseValue()->getZExtValue();
Ice::CfgNode *CaseSuccessor = mapBasicBlockToNode(I.getCaseSuccessor());
Switch->addBranch(CurrentCase, CaseValue, CaseSuccessor);
}
return Switch;
}
Ice::Inst *convertCallInstruction(const CallInst *Inst) {
Ice::Variable *Dest = mapValueToIceVar(Inst);
Ice::Operand *CallTarget = convertValue(Inst->getCalledValue());
unsigned NumArgs = Inst->getNumArgOperands();
// Note: Subzero doesn't (yet) do anything special with the Tail
// flag in the bitcode, i.e. CallInst::isTailCall().
Ice::InstCall *NewInst =
Ice::InstCall::create(Func, NumArgs, Dest, CallTarget);
for (unsigned i = 0; i < NumArgs; ++i) {
NewInst->addArg(convertOperand(Inst, i));
}
return NewInst;
}
Ice::Inst *convertAllocaInstruction(const AllocaInst *Inst) {
// PNaCl bitcode only contains allocas of byte-granular objects.
Ice::Operand *ByteCount = convertValue(Inst->getArraySize());
uint32_t Align = Inst->getAlignment();
Ice::Variable *Dest = mapValueToIceVar(Inst, SubzeroPointerType);
return Ice::InstAlloca::create(Func, ByteCount, Align, Dest);
}
Ice::Inst *convertUnreachableInstruction(const UnreachableInst *Inst) {
return Ice::InstUnreachable::create(Func);
}
Ice::CfgNode *convertBasicBlock(const BasicBlock *BB) {
Ice::CfgNode *Node = mapBasicBlockToNode(BB);
for (BasicBlock::const_iterator II = BB->begin(), II_e = BB->end();
II != II_e; ++II) {
Ice::Inst *Inst = convertInstruction(II);
Node->appendInst(Inst);
}
return Node;
}
private:
// Data
Ice::GlobalContext *Ctx;
Ice::Cfg *Func;
Ice::CfgNode *CurrentNode;
Ice::Type SubzeroPointerType;
std::map<const Value *, Ice::Variable *> VarMap;
std::map<const BasicBlock *, Ice::CfgNode *> NodeMap;
};
static cl::list<Ice::VerboseItem> VerboseList(
"verbose", cl::CommaSeparated,
cl::desc("Verbose options (can be comma-separated):"),
cl::values(
clEnumValN(Ice::IceV_Instructions, "inst", "Print basic instructions"),
clEnumValN(Ice::IceV_Deleted, "del", "Include deleted instructions"),
clEnumValN(Ice::IceV_InstNumbers, "instnum",
"Print instruction numbers"),
clEnumValN(Ice::IceV_Preds, "pred", "Show predecessors"),
clEnumValN(Ice::IceV_Succs, "succ", "Show successors"),
clEnumValN(Ice::IceV_Liveness, "live", "Liveness information"),
clEnumValN(Ice::IceV_RegManager, "rmgr", "Register manager status"),
clEnumValN(Ice::IceV_RegOrigins, "orig", "Physical register origins"),
clEnumValN(Ice::IceV_LinearScan, "regalloc", "Linear scan details"),
clEnumValN(Ice::IceV_Frame, "frame", "Stack frame layout details"),
clEnumValN(Ice::IceV_Timing, "time", "Pass timing details"),
clEnumValN(Ice::IceV_All, "all", "Use all verbose options"),
clEnumValN(Ice::IceV_None, "none", "No verbosity"), clEnumValEnd));
static cl::opt<std::string> IRFilename(cl::Positional, cl::desc("<IR file>"),
cl::Required);
static cl::opt<std::string> OutputFilename("o",
cl::desc("Override output filename"),
cl::init("-"),
cl::value_desc("filename"));
static cl::opt<std::string>
TestPrefix("prefix", cl::desc("Prepend a prefix to symbol names for testing"),
cl::init(""), cl::value_desc("prefix"));
static cl::opt<bool>
DisableInternal("external",
cl::desc("Disable 'internal' linkage type for testing"));
static cl::opt<bool>
DisableTranslation("notranslate", cl::desc("Disable Subzero translation"));
static cl::opt<bool> SubzeroTimingEnabled(
"timing", cl::desc("Enable breakdown timing of Subzero translation"));
int main(int argc, char **argv) {
cl::ParseCommandLineOptions(argc, argv);
// Parse the input LLVM IR file into a module.
SMDiagnostic Err;
Module *Mod;
{
Ice::Timer T;
Mod = ParseIRFile(IRFilename, Err, getGlobalContext());
if (SubzeroTimingEnabled) {
std::cerr << "[Subzero timing] IR Parsing: " << T.getElapsedSec()
<< " sec\n";
}
}
if (!Mod) {
Err.print(argv[0], errs());
return 1;
}
Ice::VerboseMask VMask = Ice::IceV_None;
for (unsigned i = 0; i != VerboseList.size(); ++i)
VMask |= VerboseList[i];
std::ofstream Ofs;
if (OutputFilename != "-") {
Ofs.open(OutputFilename.c_str(), std::ofstream::out);
}
raw_os_ostream *Os =
new raw_os_ostream(OutputFilename == "-" ? std::cout : Ofs);
Os->SetUnbuffered();
Ice::GlobalContext Ctx(Os, Os, VMask, TestPrefix);
for (Module::const_iterator I = Mod->begin(), E = Mod->end(); I != E; ++I) {
if (I->empty())
continue;
LLVM2ICEConverter FunctionConverter(&Ctx);
Ice::Timer TConvert;
Ice::Cfg *Func = FunctionConverter.convertFunction(I);
if (DisableInternal)
Func->setInternal(false);
if (SubzeroTimingEnabled) {
std::cerr << "[Subzero timing] Convert function "
<< Func->getFunctionName() << ": " << TConvert.getElapsedSec()
<< " sec\n";
}
if (DisableTranslation) {
Func->dump();
}
}
return 0;
}
#!/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
@__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 i32 @ignore64BitArg(i64 %a, i32 %b, i64 %c) {
entry:
ret i32 %b
}
define internal i32 @pass64BitArg(i64 %a, i64 %b, i64 %c, i64 %d, i64 %e, i64 %f) {
entry:
%call = call i32 @ignore64BitArgNoInline(i64 %a, i32 123, i64 %b)
%call1 = call i32 @ignore64BitArgNoInline(i64 %c, i32 123, i64 %d)
%call2 = call i32 @ignore64BitArgNoInline(i64 %e, i32 123, i64 %f)
%add = add i32 %call1, %call
%add3 = add i32 %add, %call2
ret i32 %add3
}
; CHECK: pass64BitArg:
; CHECK: push 123
; CHECK-NEXT: push
; CHECK-NEXT: push
; CHECK-NEXT: call ignore64BitArgNoInline
; CHECK: push
; CHECK-NEXT: push
; CHECK-NEXT: push 123
; CHECK-NEXT: push
; CHECK-NEXT: push
; CHECK-NEXT: call ignore64BitArgNoInline
; CHECK: push
; CHECK-NEXT: push
; CHECK-NEXT: push 123
; CHECK-NEXT: push
; CHECK-NEXT: push
; CHECK-NEXT: call ignore64BitArgNoInline
declare i32 @ignore64BitArgNoInline(i64, i32, i64)
define internal i32 @pass64BitConstArg(i64 %a, i64 %b) {
entry:
%call = call i32 @ignore64BitArgNoInline(i64 %a, i32 123, i64 -2401053092306725256)
ret i32 %call
}
; CHECK: pass64BitConstArg:
; CHECK: push 3735928559
; CHECK-NEXT: push 305419896
; CHECK-NEXT: push 123
; CHECK-NEXT: push ecx
; CHECK-NEXT: push eax
; CHECK-NEXT: call ignore64BitArgNoInline
define internal i64 @return64BitArg(i64 %a) {
entry:
ret i64 %a
}
; CHECK: return64BitArg:
; CHECK: mov {{.*}}, dword ptr [esp+4]
; CHECK: mov {{.*}}, dword ptr [esp+8]
; CHECK: ret
define internal i64 @return64BitConst() {
entry:
ret i64 -2401053092306725256
}
; CHECK: return64BitConst:
; CHECK: mov eax, 305419896
; CHECK: mov edx, 3735928559
; CHECK: ret
define internal i64 @add64BitSigned(i64 %a, i64 %b) {
entry:
%add = add i64 %b, %a
ret i64 %add
}
; CHECK: add64BitSigned:
; CHECK: add
; CHECK: adc
; CHECK: ret
define internal i64 @add64BitUnsigned(i64 %a, i64 %b) {
entry:
%add = add i64 %b, %a
ret i64 %add
}
; CHECK: add64BitUnsigned:
; CHECK: add
; CHECK: adc
; CHECK: ret
define internal i64 @sub64BitSigned(i64 %a, i64 %b) {
entry:
%sub = sub i64 %a, %b
ret i64 %sub
}
; CHECK: sub64BitSigned:
; CHECK: sub
; CHECK: sbb
; CHECK: ret
define internal i64 @sub64BitUnsigned(i64 %a, i64 %b) {
entry:
%sub = sub i64 %a, %b
ret i64 %sub
}
; CHECK: sub64BitUnsigned:
; CHECK: sub
; CHECK: sbb
; CHECK: ret
define internal i64 @mul64BitSigned(i64 %a, i64 %b) {
entry:
%mul = mul i64 %b, %a
ret i64 %mul
}
; CHECK: mul64BitSigned:
; CHECK: imul
; CHECK: imul
; CHECK: mul
; CHECK: add
; CHECK: add
; CHECK: ret
define internal i64 @mul64BitUnsigned(i64 %a, i64 %b) {
entry:
%mul = mul i64 %b, %a
ret i64 %mul
}
; CHECK: mul64BitUnsigned:
; CHECK: imul
; CHECK: imul
; CHECK: mul
; CHECK: add
; CHECK: add
; CHECK: ret
define internal i64 @div64BitSigned(i64 %a, i64 %b) {
entry:
%div = sdiv i64 %a, %b
ret i64 %div
}
; CHECK: div64BitSigned:
; CHECK: call __divdi3
; CHECK: ret
define internal i64 @div64BitUnsigned(i64 %a, i64 %b) {
entry:
%div = udiv i64 %a, %b
ret i64 %div
}
; CHECK: div64BitUnsigned:
; CHECK: call __udivdi3
; CHECK: ret
define internal i64 @rem64BitSigned(i64 %a, i64 %b) {
entry:
%rem = srem i64 %a, %b
ret i64 %rem
}
; CHECK: rem64BitSigned:
; CHECK: call __moddi3
; CHECK: ret
define internal i64 @rem64BitUnsigned(i64 %a, i64 %b) {
entry:
%rem = urem i64 %a, %b
ret i64 %rem
}
; CHECK: rem64BitUnsigned:
; CHECK: call __umoddi3
; CHECK: ret
define internal i64 @shl64BitSigned(i64 %a, i64 %b) {
entry:
%shl = shl i64 %a, %b
ret i64 %shl
}
; CHECK: shl64BitSigned:
; CHECK: shld
; CHECK: shl e
; CHECK: test {{.*}}, 32
; CHECK: je
define internal i64 @shl64BitUnsigned(i64 %a, i64 %b) {
entry:
%shl = shl i64 %a, %b
ret i64 %shl
}
; CHECK: shl64BitUnsigned:
; CHECK: shld
; CHECK: shl e
; CHECK: test {{.*}}, 32
; CHECK: je
define internal i64 @shr64BitSigned(i64 %a, i64 %b) {
entry:
%shr = ashr i64 %a, %b
ret i64 %shr
}
; CHECK: shr64BitSigned:
; CHECK: shrd
; CHECK: sar
; CHECK: test {{.*}}, 32
; CHECK: je
; CHECK: sar {{.*}}, 31
define internal i64 @shr64BitUnsigned(i64 %a, i64 %b) {
entry:
%shr = lshr i64 %a, %b
ret i64 %shr
}
; CHECK: shr64BitUnsigned:
; CHECK: shrd
; CHECK: shr
; CHECK: test {{.*}}, 32
; CHECK: je
define internal i64 @and64BitSigned(i64 %a, i64 %b) {
entry:
%and = and i64 %b, %a
ret i64 %and
}
; CHECK: and64BitSigned:
; CHECK: and
; CHECK: and
define internal i64 @and64BitUnsigned(i64 %a, i64 %b) {
entry:
%and = and i64 %b, %a
ret i64 %and
}
; CHECK: and64BitUnsigned:
; CHECK: and
; CHECK: and
define internal i64 @or64BitSigned(i64 %a, i64 %b) {
entry:
%or = or i64 %b, %a
ret i64 %or
}
; CHECK: or64BitSigned:
; CHECK: or
; CHECK: or
define internal i64 @or64BitUnsigned(i64 %a, i64 %b) {
entry:
%or = or i64 %b, %a
ret i64 %or
}
; CHECK: or64BitUnsigned:
; CHECK: or
; CHECK: or
define internal i64 @xor64BitSigned(i64 %a, i64 %b) {
entry:
%xor = xor i64 %b, %a
ret i64 %xor
}
; CHECK: xor64BitSigned:
; CHECK: xor
; CHECK: xor
define internal i64 @xor64BitUnsigned(i64 %a, i64 %b) {
entry:
%xor = xor i64 %b, %a
ret i64 %xor
}
; CHECK: xor64BitUnsigned:
; CHECK: xor
; CHECK: xor
define internal i32 @trunc64To32Signed(i64 %a) {
entry:
%conv = trunc i64 %a to i32
ret i32 %conv
}
; CHECK: trunc64To32Signed:
; CHECK: mov eax, dword ptr [esp+4]
; CHECK-NEXT: ret
define internal i32 @trunc64To16Signed(i64 %a) {
entry:
%conv = trunc i64 %a to i16
%conv.ret_ext = sext i16 %conv to i32
ret i32 %conv.ret_ext
}
; CHECK: trunc64To16Signed:
; CHECK: mov eax, dword ptr [esp+4]
; CHECK-NEXT: movsx eax, ax
; CHECK-NEXT: ret
define internal i32 @trunc64To8Signed(i64 %a) {
entry:
%conv = trunc i64 %a to i8
%conv.ret_ext = sext i8 %conv to i32
ret i32 %conv.ret_ext
}
; CHECK: trunc64To8Signed:
; CHECK: mov eax, dword ptr [esp+4]
; CHECK-NEXT: movsx eax, al
; CHECK-NEXT: ret
define internal i32 @trunc64To32Unsigned(i64 %a) {
entry:
%conv = trunc i64 %a to i32
ret i32 %conv
}
; CHECK: trunc64To32Unsigned:
; CHECK: mov eax, dword ptr [esp+4]
; CHECK-NEXT: ret
define internal i32 @trunc64To16Unsigned(i64 %a) {
entry:
%conv = trunc i64 %a to i16
%conv.ret_ext = zext i16 %conv to i32
ret i32 %conv.ret_ext
}
; CHECK: trunc64To16Unsigned:
; CHECK: mov eax, dword ptr [esp+4]
; CHECK-NEXT: movzx eax, ax
; CHECK-NEXT: ret
define internal i32 @trunc64To8Unsigned(i64 %a) {
entry:
%conv = trunc i64 %a to i8
%conv.ret_ext = zext i8 %conv to i32
ret i32 %conv.ret_ext
}
; CHECK: trunc64To8Unsigned:
; CHECK: mov eax, dword ptr [esp+4]
; CHECK-NEXT: movzx eax, al
; CHECK-NEXT: ret
define internal i32 @trunc64To1(i64 %a) {
entry:
; %tobool = icmp ne i64 %a, 0
%tobool = trunc i64 %a to i1
%tobool.ret_ext = zext i1 %tobool to i32
ret i32 %tobool.ret_ext
}
; CHECK: trunc64To1:
; CHECK: mov eax, dword ptr [esp+4]
; CHECK: and eax, 1
; CHECK-NEXT: ret
define internal i64 @sext32To64(i32 %a) {
entry:
%conv = sext i32 %a to i64
ret i64 %conv
}
; CHECK: sext32To64:
; CHECK: mov
; CHECK: sar {{.*}}, 31
define internal i64 @sext16To64(i32 %a) {
entry:
%a.arg_trunc = trunc i32 %a to i16
%conv = sext i16 %a.arg_trunc to i64
ret i64 %conv
}
; CHECK: sext16To64:
; CHECK: movsx
; CHECK: sar {{.*}}, 31
define internal i64 @sext8To64(i32 %a) {
entry:
%a.arg_trunc = trunc i32 %a to i8
%conv = sext i8 %a.arg_trunc to i64
ret i64 %conv
}
; CHECK: sext8To64:
; CHECK: movsx
; CHECK: sar {{.*}}, 31
define internal i64 @zext32To64(i32 %a) {
entry:
%conv = zext i32 %a to i64
ret i64 %conv
}
; CHECK: zext32To64:
; CHECK: mov
; CHECK: mov {{.*}}, 0
define internal i64 @zext16To64(i32 %a) {
entry:
%a.arg_trunc = trunc i32 %a to i16
%conv = zext i16 %a.arg_trunc to i64
ret i64 %conv
}
; CHECK: zext16To64:
; CHECK: movzx
; CHECK: mov {{.*}}, 0
define internal i64 @zext8To64(i32 %a) {
entry:
%a.arg_trunc = trunc i32 %a to i8
%conv = zext i8 %a.arg_trunc to i64
ret i64 %conv
}
; CHECK: zext8To64:
; CHECK: movzx
; CHECK: mov {{.*}}, 0
define internal i64 @zext1To64(i32 %a) {
entry:
%a.arg_trunc = trunc i32 %a to i1
%conv = zext i1 %a.arg_trunc to i64
ret i64 %conv
}
; CHECK: zext1To64:
; CHECK: movzx
; CHECK: mov {{.*}}, 0
define internal void @icmpEq64(i64 %a, i64 %b, i64 %c, i64 %d) {
entry:
%cmp = icmp eq i64 %a, %b
br i1 %cmp, label %if.then, label %if.end
if.then: ; preds = %entry
call void @func()
br label %if.end
if.end: ; preds = %if.then, %entry
%cmp1 = icmp eq i64 %c, %d
br i1 %cmp1, label %if.then2, label %if.end3
if.then2: ; preds = %if.end
call void @func()
br label %if.end3
if.end3: ; preds = %if.then2, %if.end
ret void
}
; CHECK: icmpEq64:
; CHECK: jne
; CHECK: jne
; CHECK: call
; CHECK: jne
; CHECK: jne
; CHECK: call
declare void @func()
define internal void @icmpNe64(i64 %a, i64 %b, i64 %c, i64 %d) {
entry:
%cmp = icmp ne i64 %a, %b
br i1 %cmp, label %if.then, label %if.end
if.then: ; preds = %entry
call void @func()
br label %if.end
if.end: ; preds = %if.then, %entry
%cmp1 = icmp ne i64 %c, %d
br i1 %cmp1, label %if.then2, label %if.end3
if.then2: ; preds = %if.end
call void @func()
br label %if.end3
if.end3: ; preds = %if.end, %if.then2
ret void
}
; CHECK: icmpNe64:
; CHECK: jne
; CHECK: jne
; CHECK: call
; CHECK: jne
; CHECK: jne
; CHECK: call
define internal void @icmpGt64(i64 %a, i64 %b, i64 %c, i64 %d) {
entry:
%cmp = icmp ugt i64 %a, %b
br i1 %cmp, label %if.then, label %if.end
if.then: ; preds = %entry
call void @func()
br label %if.end
if.end: ; preds = %if.then, %entry
%cmp1 = icmp sgt i64 %c, %d
br i1 %cmp1, label %if.then2, label %if.end3
if.then2: ; preds = %if.end
call void @func()
br label %if.end3
if.end3: ; preds = %if.then2, %if.end
ret void
}
; CHECK: icmpGt64:
; CHECK: ja
; CHECK: jb
; CHECK: ja
; CHECK: call
; CHECK: jg
; CHECK: jl
; CHECK: ja
; CHECK: call
define internal void @icmpGe64(i64 %a, i64 %b, i64 %c, i64 %d) {
entry:
%cmp = icmp uge i64 %a, %b
br i1 %cmp, label %if.then, label %if.end
if.then: ; preds = %entry
call void @func()
br label %if.end
if.end: ; preds = %if.then, %entry
%cmp1 = icmp sge i64 %c, %d
br i1 %cmp1, label %if.then2, label %if.end3
if.then2: ; preds = %if.end
call void @func()
br label %if.end3
if.end3: ; preds = %if.end, %if.then2
ret void
}
; CHECK: icmpGe64:
; CHECK: ja
; CHECK: jb
; CHECK: jae
; CHECK: call
; CHECK: jg
; CHECK: jl
; CHECK: jae
; CHECK: call
define internal void @icmpLt64(i64 %a, i64 %b, i64 %c, i64 %d) {
entry:
%cmp = icmp ult i64 %a, %b
br i1 %cmp, label %if.then, label %if.end
if.then: ; preds = %entry
call void @func()
br label %if.end
if.end: ; preds = %if.then, %entry
%cmp1 = icmp slt i64 %c, %d
br i1 %cmp1, label %if.then2, label %if.end3
if.then2: ; preds = %if.end
call void @func()
br label %if.end3
if.end3: ; preds = %if.then2, %if.end
ret void
}
; CHECK: icmpLt64:
; CHECK: jb
; CHECK: ja
; CHECK: jb
; CHECK: call
; CHECK: jl
; CHECK: jg
; CHECK: jb
; CHECK: call
define internal void @icmpLe64(i64 %a, i64 %b, i64 %c, i64 %d) {
entry:
%cmp = icmp ule i64 %a, %b
br i1 %cmp, label %if.then, label %if.end
if.then: ; preds = %entry
call void @func()
br label %if.end
if.end: ; preds = %if.then, %entry
%cmp1 = icmp sle i64 %c, %d
br i1 %cmp1, label %if.then2, label %if.end3
if.then2: ; preds = %if.end
call void @func()
br label %if.end3
if.end3: ; preds = %if.end, %if.then2
ret void
}
; CHECK: icmpLe64:
; CHECK: jb
; CHECK: ja
; CHECK: jbe
; CHECK: call
; CHECK: jl
; CHECK: jg
; CHECK: jbe
; CHECK: call
define internal i32 @icmpEq64Bool(i64 %a, i64 %b) {
entry:
%cmp = icmp eq i64 %a, %b
%cmp.ret_ext = zext i1 %cmp to i32
ret i32 %cmp.ret_ext
}
; CHECK: icmpEq64Bool:
; CHECK: jne
; CHECK: jne
define internal i32 @icmpNe64Bool(i64 %a, i64 %b) {
entry:
%cmp = icmp ne i64 %a, %b
%cmp.ret_ext = zext i1 %cmp to i32
ret i32 %cmp.ret_ext
}
; CHECK: icmpNe64Bool:
; CHECK: jne
; CHECK: jne
define internal i32 @icmpSgt64Bool(i64 %a, i64 %b) {
entry:
%cmp = icmp sgt i64 %a, %b
%cmp.ret_ext = zext i1 %cmp to i32
ret i32 %cmp.ret_ext
}
; CHECK: icmpSgt64Bool:
; CHECK: cmp
; CHECK: jg
; CHECK: jl
; CHECK: cmp
; CHECK: ja
define internal i32 @icmpUgt64Bool(i64 %a, i64 %b) {
entry:
%cmp = icmp ugt i64 %a, %b
%cmp.ret_ext = zext i1 %cmp to i32
ret i32 %cmp.ret_ext
}
; CHECK: icmpUgt64Bool:
; CHECK: cmp
; CHECK: ja
; CHECK: jb
; CHECK: cmp
; CHECK: ja
define internal i32 @icmpSge64Bool(i64 %a, i64 %b) {
entry:
%cmp = icmp sge i64 %a, %b
%cmp.ret_ext = zext i1 %cmp to i32
ret i32 %cmp.ret_ext
}
; CHECK: icmpSge64Bool:
; CHECK: cmp
; CHECK: jg
; CHECK: jl
; CHECK: cmp
; CHECK: jae
define internal i32 @icmpUge64Bool(i64 %a, i64 %b) {
entry:
%cmp = icmp uge i64 %a, %b
%cmp.ret_ext = zext i1 %cmp to i32
ret i32 %cmp.ret_ext
}
; CHECK: icmpUge64Bool:
; CHECK: cmp
; CHECK: ja
; CHECK: jb
; CHECK: cmp
; CHECK: jae
define internal i32 @icmpSlt64Bool(i64 %a, i64 %b) {
entry:
%cmp = icmp slt i64 %a, %b
%cmp.ret_ext = zext i1 %cmp to i32
ret i32 %cmp.ret_ext
}
; CHECK: icmpSlt64Bool:
; CHECK: cmp
; CHECK: jl
; CHECK: jg
; CHECK: cmp
; CHECK: jb
define internal i32 @icmpUlt64Bool(i64 %a, i64 %b) {
entry:
%cmp = icmp ult i64 %a, %b
%cmp.ret_ext = zext i1 %cmp to i32
ret i32 %cmp.ret_ext
}
; CHECK: icmpUlt64Bool:
; CHECK: cmp
; CHECK: jb
; CHECK: ja
; CHECK: cmp
; CHECK: jb
define internal i32 @icmpSle64Bool(i64 %a, i64 %b) {
entry:
%cmp = icmp sle i64 %a, %b
%cmp.ret_ext = zext i1 %cmp to i32
ret i32 %cmp.ret_ext
}
; CHECK: icmpSle64Bool:
; CHECK: cmp
; CHECK: jl
; CHECK: jg
; CHECK: cmp
; CHECK: jbe
define internal i32 @icmpUle64Bool(i64 %a, i64 %b) {
entry:
%cmp = icmp ule i64 %a, %b
%cmp.ret_ext = zext i1 %cmp to i32
ret i32 %cmp.ret_ext
}
; CHECK: icmpUle64Bool:
; CHECK: cmp
; CHECK: jb
; CHECK: ja
; CHECK: cmp
; CHECK: jbe
define internal i64 @load64(i32 %a) {
entry:
%a.asptr = inttoptr i32 %a to i64*
%v0 = load i64* %a.asptr, align 1
ret i64 %v0
}
; CHECK: load64:
; CHECK: mov e[[REGISTER:[a-z]+]], dword ptr [esp+4]
; CHECK-NEXT: mov {{.*}}, dword ptr [e[[REGISTER]]]
; CHECK-NEXT: mov {{.*}}, dword ptr [e[[REGISTER]]+4]
define internal void @store64(i32 %a, i64 %value) {
entry:
%a.asptr = inttoptr i32 %a to i64*
store i64 %value, i64* %a.asptr, align 1
ret void
}
; CHECK: store64:
; CHECK: mov e[[REGISTER:[a-z]+]], dword ptr [esp+4]
; CHECK: mov dword ptr [e[[REGISTER]]+4],
; CHECK: mov dword ptr [e[[REGISTER]]],
define internal void @store64Const(i32 %a) {
entry:
%a.asptr = inttoptr i32 %a to i64*
store i64 -2401053092306725256, i64* %a.asptr, align 1
ret void
}
; CHECK: store64Const:
; CHECK: mov e[[REGISTER:[a-z]+]], dword ptr [esp+4]
; CHECK: mov dword ptr [e[[REGISTER]]+4], 3735928559
; CHECK: mov dword ptr [e[[REGISTER]]], 305419896
define internal i64 @select64VarVar(i64 %a, i64 %b) {
entry:
%cmp = icmp ult i64 %a, %b
%cond = select i1 %cmp, i64 %a, i64 %b
ret i64 %cond
}
; CHECK: select64VarVar:
; CHECK: cmp
; CHECK: jb
; CHECK: ja
; CHECK: cmp
; CHECK: jb
; CHECK: cmp
; CHECK: jne
define internal i64 @select64VarConst(i64 %a, i64 %b) {
entry:
%cmp = icmp ult i64 %a, %b
%cond = select i1 %cmp, i64 %a, i64 -2401053092306725256
ret i64 %cond
}
; CHECK: select64VarConst:
; CHECK: cmp
; CHECK: jb
; CHECK: ja
; CHECK: cmp
; CHECK: jb
; CHECK: cmp
; CHECK: jne
define internal i64 @select64ConstVar(i64 %a, i64 %b) {
entry:
%cmp = icmp ult i64 %a, %b
%cond = select i1 %cmp, i64 -2401053092306725256, i64 %b
ret i64 %cond
}
; CHECK: select64ConstVar:
; CHECK: cmp
; CHECK: jb
; CHECK: ja
; CHECK: cmp
; CHECK: jb
; CHECK: cmp
; CHECK: jne
; 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
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 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 i32 @doubleArgs(double %a, i32 %b, double %c) {
entry:
ret i32 %b
}
; CHECK: doubleArgs:
; CHECK: mov eax, dword ptr [esp+12]
; CHECK-NEXT: ret
define internal i32 @floatArgs(float %a, i32 %b, float %c) {
entry:
ret i32 %b
}
; CHECK: floatArgs:
; CHECK: mov eax, dword ptr [esp+8]
; CHECK-NEXT: ret
define internal i32 @passFpArgs(float %a, double %b, float %c, double %d, float %e, double %f) {
entry:
%call = call i32 @ignoreFpArgsNoInline(float %a, i32 123, double %b)
%call1 = call i32 @ignoreFpArgsNoInline(float %c, i32 123, double %d)
%call2 = call i32 @ignoreFpArgsNoInline(float %e, i32 123, double %f)
%add = add i32 %call1, %call
%add3 = add i32 %add, %call2
ret i32 %add3
}
; CHECK: passFpArgs:
; CHECK: push 123
; CHECK: call ignoreFpArgsNoInline
; CHECK: push 123
; CHECK: call ignoreFpArgsNoInline
; CHECK: push 123
; CHECK: call ignoreFpArgsNoInline
declare i32 @ignoreFpArgsNoInline(float, i32, double)
define internal i32 @passFpConstArg(float %a, double %b) {
entry:
%call = call i32 @ignoreFpArgsNoInline(float %a, i32 123, double 2.340000e+00)
ret i32 %call
}
; CHECK: passFpConstArg:
; CHECK: push 123
; CHECK: call ignoreFpArgsNoInline
define internal float @returnFloatArg(float %a) {
entry:
ret float %a
}
; CHECK: returnFloatArg:
; CHECK: fld dword ptr [esp
define internal double @returnDoubleArg(double %a) {
entry:
ret double %a
}
; CHECK: returnDoubleArg:
; CHECK: fld qword ptr [esp
define internal float @returnFloatConst() {
entry:
ret float 0x3FF3AE1480000000
}
; CHECK: returnFloatConst:
; CHECK: fld
define internal double @returnDoubleConst() {
entry:
ret double 1.230000e+00
}
; CHECK: returnDoubleConst:
; CHECK: fld
define internal float @addFloat(float %a, float %b) {
entry:
%add = fadd float %a, %b
ret float %add
}
; CHECK: addFloat:
; CHECK: addss
; CHECK: fld
define internal double @addDouble(double %a, double %b) {
entry:
%add = fadd double %a, %b
ret double %add
}
; CHECK: addDouble:
; CHECK: addsd
; CHECK: fld
define internal float @subFloat(float %a, float %b) {
entry:
%sub = fsub float %a, %b
ret float %sub
}
; CHECK: subFloat:
; CHECK: subss
; CHECK: fld
define internal double @subDouble(double %a, double %b) {
entry:
%sub = fsub double %a, %b
ret double %sub
}
; CHECK: subDouble:
; CHECK: subsd
; CHECK: fld
define internal float @mulFloat(float %a, float %b) {
entry:
%mul = fmul float %a, %b
ret float %mul
}
; CHECK: mulFloat:
; CHECK: mulss
; CHECK: fld
define internal double @mulDouble(double %a, double %b) {
entry:
%mul = fmul double %a, %b
ret double %mul
}
; CHECK: mulDouble:
; CHECK: mulsd
; CHECK: fld
define internal float @divFloat(float %a, float %b) {
entry:
%div = fdiv float %a, %b
ret float %div
}
; CHECK: divFloat:
; CHECK: divss
; CHECK: fld
define internal double @divDouble(double %a, double %b) {
entry:
%div = fdiv double %a, %b
ret double %div
}
; CHECK: divDouble:
; CHECK: divsd
; CHECK: fld
define internal float @remFloat(float %a, float %b) {
entry:
%div = frem float %a, %b
ret float %div
}
; CHECK: remFloat:
; CHECK: call fmodf
define internal double @remDouble(double %a, double %b) {
entry:
%div = frem double %a, %b
ret double %div
}
; CHECK: remDouble:
; CHECK: call fmod
define internal float @fptrunc(double %a) {
entry:
%conv = fptrunc double %a to float
ret float %conv
}
; CHECK: fptrunc:
; CHECK: cvtsd2ss
; CHECK: fld
define internal double @fpext(float %a) {
entry:
%conv = fpext float %a to double
ret double %conv
}
; CHECK: fpext:
; CHECK: cvtss2sd
; CHECK: fld
define internal i64 @doubleToSigned64(double %a) {
entry:
%conv = fptosi double %a to i64
ret i64 %conv
}
; CHECK: doubleToSigned64:
; CHECK: call cvtdtosi64
define internal i64 @floatToSigned64(float %a) {
entry:
%conv = fptosi float %a to i64
ret i64 %conv
}
; CHECK: floatToSigned64:
; CHECK: call cvtftosi64
define internal i64 @doubleToUnsigned64(double %a) {
entry:
%conv = fptoui double %a to i64
ret i64 %conv
}
; CHECK: doubleToUnsigned64:
; CHECK: call cvtdtoui64
define internal i64 @floatToUnsigned64(float %a) {
entry:
%conv = fptoui float %a to i64
ret i64 %conv
}
; CHECK: floatToUnsigned64:
; CHECK: call cvtftoui64
define internal i32 @doubleToSigned32(double %a) {
entry:
%conv = fptosi double %a to i32
ret i32 %conv
}
; CHECK: doubleToSigned32:
; CHECK: cvtsd2si
define internal i32 @floatToSigned32(float %a) {
entry:
%conv = fptosi float %a to i32
ret i32 %conv
}
; CHECK: floatToSigned32:
; CHECK: cvtss2si
define internal i32 @doubleToUnsigned32(double %a) {
entry:
%conv = fptoui double %a to i32
ret i32 %conv
}
; CHECK: doubleToUnsigned32:
; CHECK: call cvtdtoui32
define internal i32 @floatToUnsigned32(float %a) {
entry:
%conv = fptoui float %a to i32
ret i32 %conv
}
; CHECK: floatToUnsigned32:
; CHECK: call cvtftoui32
define internal i32 @doubleToSigned16(double %a) {
entry:
%conv = fptosi double %a to i16
%conv.ret_ext = sext i16 %conv to i32
ret i32 %conv.ret_ext
}
; CHECK: doubleToSigned16:
; CHECK: cvtsd2si
; CHECK: movsx
define internal i32 @floatToSigned16(float %a) {
entry:
%conv = fptosi float %a to i16
%conv.ret_ext = sext i16 %conv to i32
ret i32 %conv.ret_ext
}
; CHECK: floatToSigned16:
; CHECK: cvtss2si
; CHECK: movsx
define internal i32 @doubleToUnsigned16(double %a) {
entry:
%conv = fptoui double %a to i16
%conv.ret_ext = zext i16 %conv to i32
ret i32 %conv.ret_ext
}
; CHECK: doubleToUnsigned16:
; CHECK: cvtsd2si
; CHECK: movzx
define internal i32 @floatToUnsigned16(float %a) {
entry:
%conv = fptoui float %a to i16
%conv.ret_ext = zext i16 %conv to i32
ret i32 %conv.ret_ext
}
; CHECK: floatToUnsigned16:
; CHECK: cvtss2si
; CHECK: movzx
define internal i32 @doubleToSigned8(double %a) {
entry:
%conv = fptosi double %a to i8
%conv.ret_ext = sext i8 %conv to i32
ret i32 %conv.ret_ext
}
; CHECK: doubleToSigned8:
; CHECK: cvtsd2si
; CHECK: movsx
define internal i32 @floatToSigned8(float %a) {
entry:
%conv = fptosi float %a to i8
%conv.ret_ext = sext i8 %conv to i32
ret i32 %conv.ret_ext
}
; CHECK: floatToSigned8:
; CHECK: cvtss2si
; CHECK: movsx
define internal i32 @doubleToUnsigned8(double %a) {
entry:
%conv = fptoui double %a to i8
%conv.ret_ext = zext i8 %conv to i32
ret i32 %conv.ret_ext
}
; CHECK: doubleToUnsigned8:
; CHECK: cvtsd2si
; CHECK: movzx
define internal i32 @floatToUnsigned8(float %a) {
entry:
%conv = fptoui float %a to i8
%conv.ret_ext = zext i8 %conv to i32
ret i32 %conv.ret_ext
}
; CHECK: floatToUnsigned8:
; CHECK: cvtss2si
; CHECK: movzx
define internal i32 @doubleToUnsigned1(double %a) {
entry:
%tobool = fptoui double %a to i1
%tobool.ret_ext = zext i1 %tobool to i32
ret i32 %tobool.ret_ext
}
; CHECK: doubleToUnsigned1:
; CHECK: cvtsd2si
; CHECK: and eax, 1
define internal i32 @floatToUnsigned1(float %a) {
entry:
%tobool = fptoui float %a to i1
%tobool.ret_ext = zext i1 %tobool to i32
ret i32 %tobool.ret_ext
}
; CHECK: floatToUnsigned1:
; CHECK: cvtss2si
; CHECK: and eax, 1
define internal double @signed64ToDouble(i64 %a) {
entry:
%conv = sitofp i64 %a to double
ret double %conv
}
; CHECK: signed64ToDouble:
; CHECK: call cvtsi64tod
; CHECK: fstp
define internal float @signed64ToFloat(i64 %a) {
entry:
%conv = sitofp i64 %a to float
ret float %conv
}
; CHECK: signed64ToFloat:
; CHECK: call cvtsi64tof
; CHECK: fstp
define internal double @unsigned64ToDouble(i64 %a) {
entry:
%conv = uitofp i64 %a to double
ret double %conv
}
; CHECK: unsigned64ToDouble:
; CHECK: call cvtui64tod
; CHECK: fstp
define internal float @unsigned64ToFloat(i64 %a) {
entry:
%conv = uitofp i64 %a to float
ret float %conv
}
; CHECK: unsigned64ToFloat:
; CHECK: call cvtui64tof
; CHECK: fstp
define internal double @signed32ToDouble(i32 %a) {
entry:
%conv = sitofp i32 %a to double
ret double %conv
}
; CHECK: signed32ToDouble:
; CHECK: cvtsi2sd
; CHECK: fld
define internal float @signed32ToFloat(i32 %a) {
entry:
%conv = sitofp i32 %a to float
ret float %conv
}
; CHECK: signed32ToFloat:
; CHECK: cvtsi2ss
; CHECK: fld
define internal double @unsigned32ToDouble(i32 %a) {
entry:
%conv = uitofp i32 %a to double
ret double %conv
}
; CHECK: unsigned32ToDouble:
; CHECK: call cvtui32tod
; CHECK: fstp
define internal float @unsigned32ToFloat(i32 %a) {
entry:
%conv = uitofp i32 %a to float
ret float %conv
}
; CHECK: unsigned32ToFloat:
; CHECK: call cvtui32tof
; CHECK: fstp
define internal double @signed16ToDouble(i32 %a) {
entry:
%a.arg_trunc = trunc i32 %a to i16
%conv = sitofp i16 %a.arg_trunc to double
ret double %conv
}
; CHECK: signed16ToDouble:
; CHECK: cvtsi2sd
; CHECK: fld
define internal float @signed16ToFloat(i32 %a) {
entry:
%a.arg_trunc = trunc i32 %a to i16
%conv = sitofp i16 %a.arg_trunc to float
ret float %conv
}
; CHECK: signed16ToFloat:
; CHECK: cvtsi2ss
; CHECK: fld
define internal double @unsigned16ToDouble(i32 %a) {
entry:
%a.arg_trunc = trunc i32 %a to i16
%conv = uitofp i16 %a.arg_trunc to double
ret double %conv
}
; CHECK: unsigned16ToDouble:
; CHECK: cvtsi2sd
; CHECK: fld
define internal float @unsigned16ToFloat(i32 %a) {
entry:
%a.arg_trunc = trunc i32 %a to i16
%conv = uitofp i16 %a.arg_trunc to float
ret float %conv
}
; CHECK: unsigned16ToFloat:
; CHECK: cvtsi2ss
; CHECK: fld
define internal double @signed8ToDouble(i32 %a) {
entry:
%a.arg_trunc = trunc i32 %a to i8
%conv = sitofp i8 %a.arg_trunc to double
ret double %conv
}
; CHECK: signed8ToDouble:
; CHECK: cvtsi2sd
; CHECK: fld
define internal float @signed8ToFloat(i32 %a) {
entry:
%a.arg_trunc = trunc i32 %a to i8
%conv = sitofp i8 %a.arg_trunc to float
ret float %conv
}
; CHECK: signed8ToFloat:
; CHECK: cvtsi2ss
; CHECK: fld
define internal double @unsigned8ToDouble(i32 %a) {
entry:
%a.arg_trunc = trunc i32 %a to i8
%conv = uitofp i8 %a.arg_trunc to double
ret double %conv
}
; CHECK: unsigned8ToDouble:
; CHECK: cvtsi2sd
; CHECK: fld
define internal float @unsigned8ToFloat(i32 %a) {
entry:
%a.arg_trunc = trunc i32 %a to i8
%conv = uitofp i8 %a.arg_trunc to float
ret float %conv
}
; CHECK: unsigned8ToFloat:
; CHECK: cvtsi2ss
; CHECK: fld
define internal double @unsigned1ToDouble(i32 %a) {
entry:
%a.arg_trunc = trunc i32 %a to i1
%conv = uitofp i1 %a.arg_trunc to double
ret double %conv
}
; CHECK: unsigned1ToDouble:
; CHECK: cvtsi2sd
; CHECK: fld
define internal float @unsigned1ToFloat(i32 %a) {
entry:
%a.arg_trunc = trunc i32 %a to i1
%conv = uitofp i1 %a.arg_trunc to float
ret float %conv
}
; CHECK: unsigned1ToFloat:
; CHECK: cvtsi2ss
; CHECK: fld
define internal void @fcmpEq(float %a, float %b, double %c, double %d) {
entry:
%cmp = fcmp oeq float %a, %b
br i1 %cmp, label %if.then, label %if.end
if.then: ; preds = %entry
call void @func()
br label %if.end
if.end: ; preds = %if.then, %entry
%cmp1 = fcmp oeq double %c, %d
br i1 %cmp1, label %if.then2, label %if.end3
if.then2: ; preds = %if.end
call void @func()
br label %if.end3
if.end3: ; preds = %if.then2, %if.end
ret void
}
; CHECK: fcmpEq:
; CHECK: ucomiss
; CHECK: jne .
; CHECK-NEXT: jp .
; CHECK: call func
; CHECK: ucomisd
; CHECK: jne .
; CHECK-NEXT: jp .
; CHECK: call func
declare void @func()
define internal void @fcmpNe(float %a, float %b, double %c, double %d) {
entry:
%cmp = fcmp une float %a, %b
br i1 %cmp, label %if.then, label %if.end
if.then: ; preds = %entry
call void @func()
br label %if.end
if.end: ; preds = %if.then, %entry
%cmp1 = fcmp une double %c, %d
br i1 %cmp1, label %if.then2, label %if.end3
if.then2: ; preds = %if.end
call void @func()
br label %if.end3
if.end3: ; preds = %if.then2, %if.end
ret void
}
; CHECK: fcmpNe:
; CHECK: ucomiss
; CHECK: jne .
; CHECK-NEXT: jp .
; CHECK: call func
; CHECK: ucomisd
; CHECK: jne .
; CHECK-NEXT: jp .
; CHECK: call func
define internal void @fcmpGt(float %a, float %b, double %c, double %d) {
entry:
%cmp = fcmp ogt float %a, %b
br i1 %cmp, label %if.then, label %if.end
if.then: ; preds = %entry
call void @func()
br label %if.end
if.end: ; preds = %if.then, %entry
%cmp1 = fcmp ogt double %c, %d
br i1 %cmp1, label %if.then2, label %if.end3
if.then2: ; preds = %if.end
call void @func()
br label %if.end3
if.end3: ; preds = %if.then2, %if.end
ret void
}
; CHECK: fcmpGt:
; CHECK: ucomiss
; CHECK: ja .
; CHECK: call func
; CHECK: ucomisd
; CHECK: ja .
; CHECK: call func
define internal void @fcmpGe(float %a, float %b, double %c, double %d) {
entry:
%cmp = fcmp ult float %a, %b
br i1 %cmp, label %if.end, label %if.then
if.then: ; preds = %entry
call void @func()
br label %if.end
if.end: ; preds = %entry, %if.then
%cmp1 = fcmp ult double %c, %d
br i1 %cmp1, label %if.end3, label %if.then2
if.then2: ; preds = %if.end
call void @func()
br label %if.end3
if.end3: ; preds = %if.end, %if.then2
ret void
}
; CHECK: fcmpGe:
; CHECK: ucomiss
; CHECK: jb .
; CHECK: call func
; CHECK: ucomisd
; CHECK: jb .
; CHECK: call func
define internal void @fcmpLt(float %a, float %b, double %c, double %d) {
entry:
%cmp = fcmp olt float %a, %b
br i1 %cmp, label %if.then, label %if.end
if.then: ; preds = %entry
call void @func()
br label %if.end
if.end: ; preds = %if.then, %entry
%cmp1 = fcmp olt double %c, %d
br i1 %cmp1, label %if.then2, label %if.end3
if.then2: ; preds = %if.end
call void @func()
br label %if.end3
if.end3: ; preds = %if.then2, %if.end
ret void
}
; CHECK: fcmpLt:
; CHECK: ucomiss
; CHECK: ja .
; CHECK: call func
; CHECK: ucomisd
; CHECK: ja .
; CHECK: call func
define internal void @fcmpLe(float %a, float %b, double %c, double %d) {
entry:
%cmp = fcmp ugt float %a, %b
br i1 %cmp, label %if.end, label %if.then
if.then: ; preds = %entry
call void @func()
br label %if.end
if.end: ; preds = %entry, %if.then
%cmp1 = fcmp ugt double %c, %d
br i1 %cmp1, label %if.end3, label %if.then2
if.then2: ; preds = %if.end
call void @func()
br label %if.end3
if.end3: ; preds = %if.end, %if.then2
ret void
}
; CHECK: fcmpLe:
; CHECK: ucomiss
; CHECK: jb .
; CHECK: call func
; CHECK: ucomisd
; CHECK: jb .
; CHECK: call func
define internal i32 @fcmpFalseFloat(float %a, float %b) {
entry:
%cmp = fcmp false float %a, %b
%cmp.ret_ext = zext i1 %cmp to i32
ret i32 %cmp.ret_ext
}
; CHECK: fcmpFalseFloat:
; CHECK: mov {{.*}}, 0
define internal i32 @fcmpFalseDouble(double %a, double %b) {
entry:
%cmp = fcmp false double %a, %b
%cmp.ret_ext = zext i1 %cmp to i32
ret i32 %cmp.ret_ext
}
; CHECK: fcmpFalseDouble:
; CHECK: mov {{.*}}, 0
define internal i32 @fcmpOeqFloat(float %a, float %b) {
entry:
%cmp = fcmp oeq float %a, %b
%cmp.ret_ext = zext i1 %cmp to i32
ret i32 %cmp.ret_ext
}
; CHECK: fcmpOeqFloat:
; CHECK: ucomiss
; CHECK: jne .
; CHECK: jp .
define internal i32 @fcmpOeqDouble(double %a, double %b) {
entry:
%cmp = fcmp oeq double %a, %b
%cmp.ret_ext = zext i1 %cmp to i32
ret i32 %cmp.ret_ext
}
; CHECK: fcmpOeqDouble:
; CHECK: ucomisd
; CHECK: jne .
; CHECK: jp .
define internal i32 @fcmpOgtFloat(float %a, float %b) {
entry:
%cmp = fcmp ogt float %a, %b
%cmp.ret_ext = zext i1 %cmp to i32
ret i32 %cmp.ret_ext
}
; CHECK: fcmpOgtFloat:
; CHECK: ucomiss
; CHECK: ja .
define internal i32 @fcmpOgtDouble(double %a, double %b) {
entry:
%cmp = fcmp ogt double %a, %b
%cmp.ret_ext = zext i1 %cmp to i32
ret i32 %cmp.ret_ext
}
; CHECK: fcmpOgtDouble:
; CHECK: ucomisd
; CHECK: ja .
define internal i32 @fcmpOgeFloat(float %a, float %b) {
entry:
%cmp = fcmp oge float %a, %b
%cmp.ret_ext = zext i1 %cmp to i32
ret i32 %cmp.ret_ext
}
; CHECK: fcmpOgeFloat:
; CHECK: ucomiss
; CHECK: jae .
define internal i32 @fcmpOgeDouble(double %a, double %b) {
entry:
%cmp = fcmp oge double %a, %b
%cmp.ret_ext = zext i1 %cmp to i32
ret i32 %cmp.ret_ext
}
; CHECK: fcmpOgeDouble:
; CHECK: ucomisd
; CHECK: jae .
define internal i32 @fcmpOltFloat(float %a, float %b) {
entry:
%cmp = fcmp olt float %a, %b
%cmp.ret_ext = zext i1 %cmp to i32
ret i32 %cmp.ret_ext
}
; CHECK: fcmpOltFloat:
; CHECK: ucomiss
; CHECK: ja .
define internal i32 @fcmpOltDouble(double %a, double %b) {
entry:
%cmp = fcmp olt double %a, %b
%cmp.ret_ext = zext i1 %cmp to i32
ret i32 %cmp.ret_ext
}
; CHECK: fcmpOltDouble:
; CHECK: ucomisd
; CHECK: ja .
define internal i32 @fcmpOleFloat(float %a, float %b) {
entry:
%cmp = fcmp ole float %a, %b
%cmp.ret_ext = zext i1 %cmp to i32
ret i32 %cmp.ret_ext
}
; CHECK: fcmpOleFloat:
; CHECK: ucomiss
; CHECK: jae .
define internal i32 @fcmpOleDouble(double %a, double %b) {
entry:
%cmp = fcmp ole double %a, %b
%cmp.ret_ext = zext i1 %cmp to i32
ret i32 %cmp.ret_ext
}
; CHECK: fcmpOleDouble:
; CHECK: ucomisd
; CHECK: jae .
define internal i32 @fcmpOneFloat(float %a, float %b) {
entry:
%cmp = fcmp one float %a, %b
%cmp.ret_ext = zext i1 %cmp to i32
ret i32 %cmp.ret_ext
}
; CHECK: fcmpOneFloat:
; CHECK: ucomiss
; CHECK: jne .
define internal i32 @fcmpOneDouble(double %a, double %b) {
entry:
%cmp = fcmp one double %a, %b
%cmp.ret_ext = zext i1 %cmp to i32
ret i32 %cmp.ret_ext
}
; CHECK: fcmpOneDouble:
; CHECK: ucomisd
; CHECK: jne .
define internal i32 @fcmpOrdFloat(float %a, float %b) {
entry:
%cmp = fcmp ord float %a, %b
%cmp.ret_ext = zext i1 %cmp to i32
ret i32 %cmp.ret_ext
}
; CHECK: fcmpOrdFloat:
; CHECK: ucomiss
; CHECK: jnp .
define internal i32 @fcmpOrdDouble(double %a, double %b) {
entry:
%cmp = fcmp ord double %a, %b
%cmp.ret_ext = zext i1 %cmp to i32
ret i32 %cmp.ret_ext
}
; CHECK: fcmpOrdDouble:
; CHECK: ucomisd
; CHECK: jnp .
define internal i32 @fcmpUeqFloat(float %a, float %b) {
entry:
%cmp = fcmp ueq float %a, %b
%cmp.ret_ext = zext i1 %cmp to i32
ret i32 %cmp.ret_ext
}
; CHECK: fcmpUeqFloat:
; CHECK: ucomiss
; CHECK: je .
define internal i32 @fcmpUeqDouble(double %a, double %b) {
entry:
%cmp = fcmp ueq double %a, %b
%cmp.ret_ext = zext i1 %cmp to i32
ret i32 %cmp.ret_ext
}
; CHECK: fcmpUeqDouble:
; CHECK: ucomisd
; CHECK: je .
define internal i32 @fcmpUgtFloat(float %a, float %b) {
entry:
%cmp = fcmp ugt float %a, %b
%cmp.ret_ext = zext i1 %cmp to i32
ret i32 %cmp.ret_ext
}
; CHECK: fcmpUgtFloat:
; CHECK: ucomiss
; CHECK: jb .
define internal i32 @fcmpUgtDouble(double %a, double %b) {
entry:
%cmp = fcmp ugt double %a, %b
%cmp.ret_ext = zext i1 %cmp to i32
ret i32 %cmp.ret_ext
}
; CHECK: fcmpUgtDouble:
; CHECK: ucomisd
; CHECK: jb .
define internal i32 @fcmpUgeFloat(float %a, float %b) {
entry:
%cmp = fcmp uge float %a, %b
%cmp.ret_ext = zext i1 %cmp to i32
ret i32 %cmp.ret_ext
}
; CHECK: fcmpUgeFloat:
; CHECK: ucomiss
; CHECK: jbe .
define internal i32 @fcmpUgeDouble(double %a, double %b) {
entry:
%cmp = fcmp uge double %a, %b
%cmp.ret_ext = zext i1 %cmp to i32
ret i32 %cmp.ret_ext
}
; CHECK: fcmpUgeDouble:
; CHECK: ucomisd
; CHECK: jbe .
define internal i32 @fcmpUltFloat(float %a, float %b) {
entry:
%cmp = fcmp ult float %a, %b
%cmp.ret_ext = zext i1 %cmp to i32
ret i32 %cmp.ret_ext
}
; CHECK: fcmpUltFloat:
; CHECK: ucomiss
; CHECK: jb .
define internal i32 @fcmpUltDouble(double %a, double %b) {
entry:
%cmp = fcmp ult double %a, %b
%cmp.ret_ext = zext i1 %cmp to i32
ret i32 %cmp.ret_ext
}
; CHECK: fcmpUltDouble:
; CHECK: ucomisd
; CHECK: jb .
define internal i32 @fcmpUleFloat(float %a, float %b) {
entry:
%cmp = fcmp ule float %a, %b
%cmp.ret_ext = zext i1 %cmp to i32
ret i32 %cmp.ret_ext
}
; CHECK: fcmpUleFloat:
; CHECK: ucomiss
; CHECK: jbe .
define internal i32 @fcmpUleDouble(double %a, double %b) {
entry:
%cmp = fcmp ule double %a, %b
%cmp.ret_ext = zext i1 %cmp to i32
ret i32 %cmp.ret_ext
}
; CHECK: fcmpUleDouble:
; CHECK: ucomisd
; CHECK: jbe .
define internal i32 @fcmpUneFloat(float %a, float %b) {
entry:
%cmp = fcmp une float %a, %b
%cmp.ret_ext = zext i1 %cmp to i32
ret i32 %cmp.ret_ext
}
; CHECK: fcmpUneFloat:
; CHECK: ucomiss
; CHECK: jne .
; CHECK: jp .
define internal i32 @fcmpUneDouble(double %a, double %b) {
entry:
%cmp = fcmp une double %a, %b
%cmp.ret_ext = zext i1 %cmp to i32
ret i32 %cmp.ret_ext
}
; CHECK: fcmpUneDouble:
; CHECK: ucomisd
; CHECK: jne .
; CHECK: jp .
define internal i32 @fcmpUnoFloat(float %a, float %b) {
entry:
%cmp = fcmp uno float %a, %b
%cmp.ret_ext = zext i1 %cmp to i32
ret i32 %cmp.ret_ext
}
; CHECK: fcmpUnoFloat:
; CHECK: ucomiss
; CHECK: jp .
define internal i32 @fcmpUnoDouble(double %a, double %b) {
entry:
%cmp = fcmp uno double %a, %b
%cmp.ret_ext = zext i1 %cmp to i32
ret i32 %cmp.ret_ext
}
; CHECK: fcmpUnoDouble:
; CHECK: ucomisd
; CHECK: jp .
define internal i32 @fcmpTrueFloat(float %a, float %b) {
entry:
%cmp = fcmp true float %a, %b
%cmp.ret_ext = zext i1 %cmp to i32
ret i32 %cmp.ret_ext
}
; CHECK: fcmpTrueFloat:
; CHECK: mov {{.*}}, 1
define internal i32 @fcmpTrueDouble(double %a, double %b) {
entry:
%cmp = fcmp true double %a, %b
%cmp.ret_ext = zext i1 %cmp to i32
ret i32 %cmp.ret_ext
}
; CHECK: fcmpTrueDouble:
; CHECK: mov {{.*}}, 1
define internal float @loadFloat(i32 %a) {
entry:
%a.asptr = inttoptr i32 %a to float*
%v0 = load float* %a.asptr, align 4
ret float %v0
}
; CHECK: loadFloat:
; CHECK: movss
; CHECK: fld
define internal double @loadDouble(i32 %a) {
entry:
%a.asptr = inttoptr i32 %a to double*
%v0 = load double* %a.asptr, align 8
ret double %v0
}
; CHECK: loadDouble:
; CHECK: movsd
; CHECK: fld
define internal void @storeFloat(i32 %a, float %value) {
entry:
%a.asptr = inttoptr i32 %a to float*
store float %value, float* %a.asptr, align 4
ret void
}
; CHECK: storeFloat:
; CHECK: movss
define internal void @storeDouble(i32 %a, double %value) {
entry:
%a.asptr = inttoptr i32 %a to double*
store double %value, double* %a.asptr, align 8
ret void
}
; CHECK: storeDouble:
; CHECK: movsd
define internal void @storeFloatConst(i32 %a) {
entry:
%a.asptr = inttoptr i32 %a to float*
store float 0x3FF3AE1480000000, float* %a.asptr, align 4
ret void
}
; CHECK: storeFloatConst:
; CHECK: mov
; CHECK: mov
define internal void @storeDoubleConst(i32 %a) {
entry:
%a.asptr = inttoptr i32 %a to double*
store double 1.230000e+00, double* %a.asptr, align 8
ret void
}
; CHECK: storeDoubleConst:
; CHECK: mov
; CHECK: mov
define internal float @selectFloatVarVar(float %a, float %b) {
entry:
%cmp = fcmp olt float %a, %b
%cond = select i1 %cmp, float %a, float %b
ret float %cond
}
; CHECK: selectFloatVarVar:
; CHECK: ucomiss
; CHECK: ja .
; CHECK: fld
define internal double @selectDoubleVarVar(double %a, double %b) {
entry:
%cmp = fcmp olt double %a, %b
%cond = select i1 %cmp, double %a, double %b
ret double %cond
}
; CHECK: selectDoubleVarVar:
; CHECK: ucomisd
; CHECK: ja .
; CHECK: fld
; ERRORS-NOT: ICE translation error
; DUMP-NOT: SZ
; RUN: %szdiff --llvm2ice=%llvm2ice %s | FileCheck --check-prefix=DUMP %s
; This is a smoke test for floating-point constant pooling. It tests
; pooling of various float and double constants (including positive
; and negative NaN) within functions and across functions. Note that
; in LLVM bitcode, hex constants are used for an FP constant whenever
; the constant "cannot be represented as a decimal floating point
; number in a reasonable number of digits". See
; http://llvm.org/docs/LangRef.html#simple-constants .
@__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 float @FpLookup1(i32 %Arg) {
entry:
switch i32 %Arg, label %return [
i32 0, label %sw.bb
i32 1, label %sw.bb1
i32 2, label %sw.bb4
i32 3, label %sw.bb7
i32 -1, label %sw.bb10
i32 -2, label %sw.bb14
i32 -3, label %sw.bb19
i32 10, label %sw.bb24
i32 -10, label %sw.bb27
i32 100, label %sw.bb30
i32 101, label %sw.bb33
i32 102, label %sw.bb36
i32 103, label %sw.bb39
i32 -101, label %sw.bb42
i32 -102, label %sw.bb47
i32 -103, label %sw.bb52
i32 110, label %sw.bb57
i32 -110, label %sw.bb60
]
sw.bb: ; preds = %entry
%call = call float @Dummy(i32 0)
%add = fadd float %call, 1.000000e+00
br label %return
sw.bb1: ; preds = %entry
%call2 = call float @Dummy(i32 1)
%add3 = fadd float %call2, 2.000000e+00
br label %return
sw.bb4: ; preds = %entry
%call5 = call float @Dummy(i32 2)
%add6 = fadd float %call5, 4.000000e+00
br label %return
sw.bb7: ; preds = %entry
%call8 = call float @Dummy(i32 3)
%add9 = fadd float %call8, 8.000000e+00
br label %return
sw.bb10: ; preds = %entry
%call11 = call float @Dummy(i32 -1)
%conv13 = fadd float %call11, 5.000000e-01
br label %return
sw.bb14: ; preds = %entry
%call15 = call float @Dummy(i32 -2)
%conv16 = fpext float %call15 to double
%add17 = fadd double %conv16, 0x3FD5555555555555
%conv18 = fptrunc double %add17 to float
br label %return
sw.bb19: ; preds = %entry
%call20 = call float @Dummy(i32 -3)
%conv23 = fadd float %call20, 2.500000e-01
br label %return
sw.bb24: ; preds = %entry
%call25 = call float @Dummy(i32 10)
%add26 = fadd float %call25, 0x7FF8000000000000
br label %return
sw.bb27: ; preds = %entry
%call28 = call float @Dummy(i32 -10)
%add29 = fadd float %call28, 0xFFF8000000000000
br label %return
sw.bb30: ; preds = %entry
%call31 = call float @Dummy(i32 100)
%add32 = fadd float %call31, 1.000000e+00
br label %return
sw.bb33: ; preds = %entry
%call34 = call float @Dummy(i32 101)
%add35 = fadd float %call34, 2.000000e+00
br label %return
sw.bb36: ; preds = %entry
%call37 = call float @Dummy(i32 102)
%add38 = fadd float %call37, 4.000000e+00
br label %return
sw.bb39: ; preds = %entry
%call40 = call float @Dummy(i32 103)
%add41 = fadd float %call40, 8.000000e+00
br label %return
sw.bb42: ; preds = %entry
%call43 = call float @Dummy(i32 -101)
%conv46 = fadd float %call43, 5.000000e-01
br label %return
sw.bb47: ; preds = %entry
%call48 = call float @Dummy(i32 -102)
%conv49 = fpext float %call48 to double
%add50 = fadd double %conv49, 0x3FD5555555555555
%conv51 = fptrunc double %add50 to float
br label %return
sw.bb52: ; preds = %entry
%call53 = call float @Dummy(i32 -103)
%conv56 = fadd float %call53, 2.500000e-01
br label %return
sw.bb57: ; preds = %entry
%call58 = call float @Dummy(i32 110)
%add59 = fadd float %call58, 0x7FF8000000000000
br label %return
sw.bb60: ; preds = %entry
%call61 = call float @Dummy(i32 -110)
%add62 = fadd float %call61, 0xFFF8000000000000
br label %return
return: ; preds = %entry, %sw.bb60, %sw.bb57, %sw.bb52, %sw.bb47, %sw.bb42, %sw.bb39, %sw.bb36, %sw.bb33, %sw.bb30, %sw.bb27, %sw.bb24, %sw.bb19, %sw.bb14, %sw.bb10, %sw.bb7, %sw.bb4, %sw.bb1, %sw.bb
%retval.0 = phi float [ %add62, %sw.bb60 ], [ %add59, %sw.bb57 ], [ %conv56, %sw.bb52 ], [ %conv51, %sw.bb47 ], [ %conv46, %sw.bb42 ], [ %add41, %sw.bb39 ], [ %add38, %sw.bb36 ], [ %add35, %sw.bb33 ], [ %add32, %sw.bb30 ], [ %add29, %sw.bb27 ], [ %add26, %sw.bb24 ], [ %conv23, %sw.bb19 ], [ %conv18, %sw.bb14 ], [ %conv13, %sw.bb10 ], [ %add9, %sw.bb7 ], [ %add6, %sw.bb4 ], [ %add3, %sw.bb1 ], [ %add, %sw.bb ], [ 0.000000e+00, %entry ]
ret float %retval.0
}
declare float @Dummy(i32)
define internal float @FpLookup2(i32 %Arg) {
entry:
switch i32 %Arg, label %return [
i32 0, label %sw.bb
i32 1, label %sw.bb1
i32 2, label %sw.bb4
i32 3, label %sw.bb7
i32 -1, label %sw.bb10
i32 -2, label %sw.bb14
i32 -3, label %sw.bb19
i32 10, label %sw.bb24
i32 -10, label %sw.bb27
i32 100, label %sw.bb30
i32 101, label %sw.bb33
i32 102, label %sw.bb36
i32 103, label %sw.bb39
i32 -101, label %sw.bb42
i32 -102, label %sw.bb47
i32 -103, label %sw.bb52
i32 110, label %sw.bb57
i32 -110, label %sw.bb60
]
sw.bb: ; preds = %entry
%call = call float @Dummy(i32 0)
%add = fadd float %call, 1.000000e+00
br label %return
sw.bb1: ; preds = %entry
%call2 = call float @Dummy(i32 1)
%add3 = fadd float %call2, 2.000000e+00
br label %return
sw.bb4: ; preds = %entry
%call5 = call float @Dummy(i32 2)
%add6 = fadd float %call5, 4.000000e+00
br label %return
sw.bb7: ; preds = %entry
%call8 = call float @Dummy(i32 3)
%add9 = fadd float %call8, 8.000000e+00
br label %return
sw.bb10: ; preds = %entry
%call11 = call float @Dummy(i32 -1)
%conv13 = fadd float %call11, 5.000000e-01
br label %return
sw.bb14: ; preds = %entry
%call15 = call float @Dummy(i32 -2)
%conv16 = fpext float %call15 to double
%add17 = fadd double %conv16, 0x3FD5555555555555
%conv18 = fptrunc double %add17 to float
br label %return
sw.bb19: ; preds = %entry
%call20 = call float @Dummy(i32 -3)
%conv23 = fadd float %call20, 2.500000e-01
br label %return
sw.bb24: ; preds = %entry
%call25 = call float @Dummy(i32 10)
%add26 = fadd float %call25, 0x7FF8000000000000
br label %return
sw.bb27: ; preds = %entry
%call28 = call float @Dummy(i32 -10)
%add29 = fadd float %call28, 0xFFF8000000000000
br label %return
sw.bb30: ; preds = %entry
%call31 = call float @Dummy(i32 100)
%add32 = fadd float %call31, 1.000000e+00
br label %return
sw.bb33: ; preds = %entry
%call34 = call float @Dummy(i32 101)
%add35 = fadd float %call34, 2.000000e+00
br label %return
sw.bb36: ; preds = %entry
%call37 = call float @Dummy(i32 102)
%add38 = fadd float %call37, 4.000000e+00
br label %return
sw.bb39: ; preds = %entry
%call40 = call float @Dummy(i32 103)
%add41 = fadd float %call40, 8.000000e+00
br label %return
sw.bb42: ; preds = %entry
%call43 = call float @Dummy(i32 -101)
%conv46 = fadd float %call43, 5.000000e-01
br label %return
sw.bb47: ; preds = %entry
%call48 = call float @Dummy(i32 -102)
%conv49 = fpext float %call48 to double
%add50 = fadd double %conv49, 0x3FD5555555555555
%conv51 = fptrunc double %add50 to float
br label %return
sw.bb52: ; preds = %entry
%call53 = call float @Dummy(i32 -103)
%conv56 = fadd float %call53, 2.500000e-01
br label %return
sw.bb57: ; preds = %entry
%call58 = call float @Dummy(i32 110)
%add59 = fadd float %call58, 0x7FF8000000000000
br label %return
sw.bb60: ; preds = %entry
%call61 = call float @Dummy(i32 -110)
%add62 = fadd float %call61, 0xFFF8000000000000
br label %return
return: ; preds = %entry, %sw.bb60, %sw.bb57, %sw.bb52, %sw.bb47, %sw.bb42, %sw.bb39, %sw.bb36, %sw.bb33, %sw.bb30, %sw.bb27, %sw.bb24, %sw.bb19, %sw.bb14, %sw.bb10, %sw.bb7, %sw.bb4, %sw.bb1, %sw.bb
%retval.0 = phi float [ %add62, %sw.bb60 ], [ %add59, %sw.bb57 ], [ %conv56, %sw.bb52 ], [ %conv51, %sw.bb47 ], [ %conv46, %sw.bb42 ], [ %add41, %sw.bb39 ], [ %add38, %sw.bb36 ], [ %add35, %sw.bb33 ], [ %add32, %sw.bb30 ], [ %add29, %sw.bb27 ], [ %add26, %sw.bb24 ], [ %conv23, %sw.bb19 ], [ %conv18, %sw.bb14 ], [ %conv13, %sw.bb10 ], [ %add9, %sw.bb7 ], [ %add6, %sw.bb4 ], [ %add3, %sw.bb1 ], [ %add, %sw.bb ], [ 0.000000e+00, %entry ]
ret float %retval.0
}
define internal double @FpLookup3(i32 %Arg) {
entry:
switch i32 %Arg, label %return [
i32 0, label %sw.bb
i32 1, label %sw.bb1
i32 2, label %sw.bb5
i32 3, label %sw.bb9
i32 -1, label %sw.bb13
i32 -2, label %sw.bb17
i32 -3, label %sw.bb21
i32 10, label %sw.bb25
i32 -10, label %sw.bb29
i32 100, label %sw.bb33
i32 101, label %sw.bb37
i32 102, label %sw.bb41
i32 103, label %sw.bb45
i32 -101, label %sw.bb49
i32 -102, label %sw.bb53
i32 -103, label %sw.bb57
i32 110, label %sw.bb61
i32 -110, label %sw.bb65
]
sw.bb: ; preds = %entry
%call = call float @Dummy(i32 0)
%add = fadd float %call, 1.000000e+00
%conv = fpext float %add to double
br label %return
sw.bb1: ; preds = %entry
%call2 = call float @Dummy(i32 1)
%add3 = fadd float %call2, 2.000000e+00
%conv4 = fpext float %add3 to double
br label %return
sw.bb5: ; preds = %entry
%call6 = call float @Dummy(i32 2)
%add7 = fadd float %call6, 4.000000e+00
%conv8 = fpext float %add7 to double
br label %return
sw.bb9: ; preds = %entry
%call10 = call float @Dummy(i32 3)
%add11 = fadd float %call10, 8.000000e+00
%conv12 = fpext float %add11 to double
br label %return
sw.bb13: ; preds = %entry
%call14 = call float @Dummy(i32 -1)
%conv15 = fpext float %call14 to double
%add16 = fadd double %conv15, 5.000000e-01
br label %return
sw.bb17: ; preds = %entry
%call18 = call float @Dummy(i32 -2)
%conv19 = fpext float %call18 to double
%add20 = fadd double %conv19, 0x3FD5555555555555
br label %return
sw.bb21: ; preds = %entry
%call22 = call float @Dummy(i32 -3)
%conv23 = fpext float %call22 to double
%add24 = fadd double %conv23, 2.500000e-01
br label %return
sw.bb25: ; preds = %entry
%call26 = call float @Dummy(i32 10)
%conv27 = fpext float %call26 to double
%add28 = fadd double %conv27, 0x7FF8000000000000
br label %return
sw.bb29: ; preds = %entry
%call30 = call float @Dummy(i32 -10)
%conv31 = fpext float %call30 to double
%add32 = fadd double %conv31, 0xFFF8000000000000
br label %return
sw.bb33: ; preds = %entry
%call34 = call float @Dummy(i32 100)
%add35 = fadd float %call34, 1.000000e+00
%conv36 = fpext float %add35 to double
br label %return
sw.bb37: ; preds = %entry
%call38 = call float @Dummy(i32 101)
%add39 = fadd float %call38, 2.000000e+00
%conv40 = fpext float %add39 to double
br label %return
sw.bb41: ; preds = %entry
%call42 = call float @Dummy(i32 102)
%add43 = fadd float %call42, 4.000000e+00
%conv44 = fpext float %add43 to double
br label %return
sw.bb45: ; preds = %entry
%call46 = call float @Dummy(i32 103)
%add47 = fadd float %call46, 8.000000e+00
%conv48 = fpext float %add47 to double
br label %return
sw.bb49: ; preds = %entry
%call50 = call float @Dummy(i32 -101)
%conv51 = fpext float %call50 to double
%add52 = fadd double %conv51, 5.000000e-01
br label %return
sw.bb53: ; preds = %entry
%call54 = call float @Dummy(i32 -102)
%conv55 = fpext float %call54 to double
%add56 = fadd double %conv55, 0x3FD5555555555555
br label %return
sw.bb57: ; preds = %entry
%call58 = call float @Dummy(i32 -103)
%conv59 = fpext float %call58 to double
%add60 = fadd double %conv59, 2.500000e-01
br label %return
sw.bb61: ; preds = %entry
%call62 = call float @Dummy(i32 110)
%conv63 = fpext float %call62 to double
%add64 = fadd double %conv63, 0x7FF8000000000000
br label %return
sw.bb65: ; preds = %entry
%call66 = call float @Dummy(i32 -110)
%conv67 = fpext float %call66 to double
%add68 = fadd double %conv67, 0xFFF8000000000000
br label %return
return: ; preds = %entry, %sw.bb65, %sw.bb61, %sw.bb57, %sw.bb53, %sw.bb49, %sw.bb45, %sw.bb41, %sw.bb37, %sw.bb33, %sw.bb29, %sw.bb25, %sw.bb21, %sw.bb17, %sw.bb13, %sw.bb9, %sw.bb5, %sw.bb1, %sw.bb
%retval.0 = phi double [ %add68, %sw.bb65 ], [ %add64, %sw.bb61 ], [ %add60, %sw.bb57 ], [ %add56, %sw.bb53 ], [ %add52, %sw.bb49 ], [ %conv48, %sw.bb45 ], [ %conv44, %sw.bb41 ], [ %conv40, %sw.bb37 ], [ %conv36, %sw.bb33 ], [ %add32, %sw.bb29 ], [ %add28, %sw.bb25 ], [ %add24, %sw.bb21 ], [ %add20, %sw.bb17 ], [ %add16, %sw.bb13 ], [ %conv12, %sw.bb9 ], [ %conv8, %sw.bb5 ], [ %conv4, %sw.bb1 ], [ %conv, %sw.bb ], [ 0.000000e+00, %entry ]
ret double %retval.0
}
define internal double @FpLookup4(i32 %Arg) {
entry:
switch i32 %Arg, label %return [
i32 0, label %sw.bb
i32 1, label %sw.bb1
i32 2, label %sw.bb5
i32 3, label %sw.bb9
i32 -1, label %sw.bb13
i32 -2, label %sw.bb17
i32 -3, label %sw.bb21
i32 10, label %sw.bb25
i32 -10, label %sw.bb29
i32 100, label %sw.bb33
i32 101, label %sw.bb37
i32 102, label %sw.bb41
i32 103, label %sw.bb45
i32 -101, label %sw.bb49
i32 -102, label %sw.bb53
i32 -103, label %sw.bb57
i32 110, label %sw.bb61
i32 -110, label %sw.bb65
]
sw.bb: ; preds = %entry
%call = call float @Dummy(i32 0)
%add = fadd float %call, 1.000000e+00
%conv = fpext float %add to double
br label %return
sw.bb1: ; preds = %entry
%call2 = call float @Dummy(i32 1)
%add3 = fadd float %call2, 2.000000e+00
%conv4 = fpext float %add3 to double
br label %return
sw.bb5: ; preds = %entry
%call6 = call float @Dummy(i32 2)
%add7 = fadd float %call6, 4.000000e+00
%conv8 = fpext float %add7 to double
br label %return
sw.bb9: ; preds = %entry
%call10 = call float @Dummy(i32 3)
%add11 = fadd float %call10, 8.000000e+00
%conv12 = fpext float %add11 to double
br label %return
sw.bb13: ; preds = %entry
%call14 = call float @Dummy(i32 -1)
%conv15 = fpext float %call14 to double
%add16 = fadd double %conv15, 5.000000e-01
br label %return
sw.bb17: ; preds = %entry
%call18 = call float @Dummy(i32 -2)
%conv19 = fpext float %call18 to double
%add20 = fadd double %conv19, 0x3FD5555555555555
br label %return
sw.bb21: ; preds = %entry
%call22 = call float @Dummy(i32 -3)
%conv23 = fpext float %call22 to double
%add24 = fadd double %conv23, 2.500000e-01
br label %return
sw.bb25: ; preds = %entry
%call26 = call float @Dummy(i32 10)
%conv27 = fpext float %call26 to double
%add28 = fadd double %conv27, 0x7FF8000000000000
br label %return
sw.bb29: ; preds = %entry
%call30 = call float @Dummy(i32 -10)
%conv31 = fpext float %call30 to double
%add32 = fadd double %conv31, 0xFFF8000000000000
br label %return
sw.bb33: ; preds = %entry
%call34 = call float @Dummy(i32 100)
%add35 = fadd float %call34, 1.000000e+00
%conv36 = fpext float %add35 to double
br label %return
sw.bb37: ; preds = %entry
%call38 = call float @Dummy(i32 101)
%add39 = fadd float %call38, 2.000000e+00
%conv40 = fpext float %add39 to double
br label %return
sw.bb41: ; preds = %entry
%call42 = call float @Dummy(i32 102)
%add43 = fadd float %call42, 4.000000e+00
%conv44 = fpext float %add43 to double
br label %return
sw.bb45: ; preds = %entry
%call46 = call float @Dummy(i32 103)
%add47 = fadd float %call46, 8.000000e+00
%conv48 = fpext float %add47 to double
br label %return
sw.bb49: ; preds = %entry
%call50 = call float @Dummy(i32 -101)
%conv51 = fpext float %call50 to double
%add52 = fadd double %conv51, 5.000000e-01
br label %return
sw.bb53: ; preds = %entry
%call54 = call float @Dummy(i32 -102)
%conv55 = fpext float %call54 to double
%add56 = fadd double %conv55, 0x3FD5555555555555
br label %return
sw.bb57: ; preds = %entry
%call58 = call float @Dummy(i32 -103)
%conv59 = fpext float %call58 to double
%add60 = fadd double %conv59, 2.500000e-01
br label %return
sw.bb61: ; preds = %entry
%call62 = call float @Dummy(i32 110)
%conv63 = fpext float %call62 to double
%add64 = fadd double %conv63, 0x7FF8000000000000
br label %return
sw.bb65: ; preds = %entry
%call66 = call float @Dummy(i32 -110)
%conv67 = fpext float %call66 to double
%add68 = fadd double %conv67, 0xFFF8000000000000
br label %return
return: ; preds = %entry, %sw.bb65, %sw.bb61, %sw.bb57, %sw.bb53, %sw.bb49, %sw.bb45, %sw.bb41, %sw.bb37, %sw.bb33, %sw.bb29, %sw.bb25, %sw.bb21, %sw.bb17, %sw.bb13, %sw.bb9, %sw.bb5, %sw.bb1, %sw.bb
%retval.0 = phi double [ %add68, %sw.bb65 ], [ %add64, %sw.bb61 ], [ %add60, %sw.bb57 ], [ %add56, %sw.bb53 ], [ %add52, %sw.bb49 ], [ %conv48, %sw.bb45 ], [ %conv44, %sw.bb41 ], [ %conv40, %sw.bb37 ], [ %conv36, %sw.bb33 ], [ %add32, %sw.bb29 ], [ %add28, %sw.bb25 ], [ %add24, %sw.bb21 ], [ %add20, %sw.bb17 ], [ %add16, %sw.bb13 ], [ %conv12, %sw.bb9 ], [ %conv8, %sw.bb5 ], [ %conv4, %sw.bb1 ], [ %conv, %sw.bb ], [ 0.000000e+00, %entry ]
ret double %retval.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
@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