Add Om1 lowering with no optimizations.

This adds infrastructure for low-level x86-32 instructions, and the target lowering patterns.

Practically no optimizations are performed.  Optimizations to be introduced later include liveness analysis, dead-code elimination, global linear-scan register allocation, linear-scan based stack slot coalescing, and compare/branch fusing.  One optimization that is present is simple coalescing of stack slots for variables that are only live within a single basic block.

There are also some fairly comprehensive cross tests.  This testing infrastructure translates bitcode using both Subzero and llc, and a testing harness calls both versions with a variety of "interesting" inputs and compares the results.  Specifically, Arithmetic, Icmp, Fcmp, and Cast instructions are tested this way, across all PNaCl primitive types.

BUG=
R=jvoung@chromium.org

Review URL: https://codereview.chromium.org/265703002
diff --git a/crosstest/crosstest.py b/crosstest/crosstest.py
new file mode 100755
index 0000000..e835193
--- /dev/null
+++ b/crosstest/crosstest.py
@@ -0,0 +1,127 @@
+#!/usr/bin/env python2
+
+import argparse
+import os
+import re
+import subprocess
+import sys
+import tempfile
+
+sys.path.insert(0, '../pydir')
+from utils import shellcmd
+
+if __name__ == '__main__':
+    """Builds a cross-test binary that allows functions translated by
+    Subzero and llc to be compared.
+
+    Each --test argument is compiled once by llc and once by Subzero.
+    C/C++ tests are first compiled down to PNaCl bitcode by the
+    build-pnacl-ir.py script.  The --prefix argument ensures that
+    symbol names are different between the two object files, to avoid
+    linking errors.
+
+    There is also a --driver argument that specifies the C/C++ file
+    that calls the test functions with a variety of interesting inputs
+    and compares their results.
+    """
+    # arch_map maps a Subzero target string to an llvm-mc -arch string.
+    arch_map = { 'x8632':'x86', 'x8664':'x86-64', 'arm':'arm' }
+    desc = 'Build a cross-test that compares Subzero and llc translation.'
+    argparser = argparse.ArgumentParser(description=desc)
+    argparser.add_argument('--test', required=True, action='append',
+                           metavar='TESTFILE_LIST',
+                           help='List of C/C++/.ll files with test functions')
+    argparser.add_argument('--driver', required=True,
+                           metavar='DRIVER',
+                           help='Driver program')
+    argparser.add_argument('--target', required=False, default='x8632',
+                           choices=arch_map.keys(),
+                           metavar='TARGET',
+                           help='Translation target architecture')
+    argparser.add_argument('-O', required=False, default='2', dest='optlevel',
+                           choices=['m1', '-1', '0', '1', '2'],
+                           metavar='OPTLEVEL',
+                           help='Optimization level ' +
+                                '(m1 and -1 are equivalent)')
+    argparser.add_argument('--prefix', required=True,
+                           metavar='SZ_PREFIX',
+                           help='String prepended to Subzero symbol names')
+    argparser.add_argument('--output', '-o', required=True,
+                           metavar='EXECUTABLE',
+                           help='Executable to produce')
+    argparser.add_argument('--dir', required=False, default='.',
+                           metavar='OUTPUT_DIR',
+                           help='Output directory for all files')
+    argparser.add_argument('--llvm-bin-path', required=False,
+                           default=os.environ.get('LLVM_BIN_PATH'),
+                           metavar='PATH',
+                           help='Path to LLVM executables like llc ' +
+                                '(defaults to $LLVM_BIN_PATH)')
+    args = argparser.parse_args()
+
+    objs = []
+    remove_internal = re.compile('^define internal ')
+    fix_target = re.compile('le32-unknown-nacl')
+    llvm_bin_path = args.llvm_bin_path
+    for arg in args.test:
+        base, ext = os.path.splitext(arg)
+        if ext == '.ll':
+            bitcode = arg
+        else:
+            bitcode = os.path.join(args.dir, base + '.pnacl.ll')
+            shellcmd(['../pydir/build-pnacl-ir.py', '--disable-verify',
+                      '--dir', args.dir, arg])
+            # Read in the bitcode file, fix it up, and rewrite the file.
+            f = open(bitcode)
+            ll_lines = f.readlines()
+            f.close()
+            f = open(bitcode, 'w')
+            for line in ll_lines:
+                line = remove_internal.sub('define ', line)
+                line = fix_target.sub('i686-pc-linux-gnu', line)
+                f.write(line)
+            f.close()
+
+        asm_sz = os.path.join(args.dir, base + '.sz.s')
+        obj_sz = os.path.join(args.dir, base + '.sz.o')
+        obj_llc = os.path.join(args.dir, base + '.llc.o')
+        shellcmd(['../llvm2ice',
+                  '-O' + args.optlevel,
+                  '--target=' + args.target,
+                  '--prefix=' + args.prefix,
+                  '-o=' + asm_sz,
+                  bitcode])
+        shellcmd([os.path.join(llvm_bin_path, 'llvm-mc'),
+                  '-arch=' + arch_map[args.target],
+                  '-x86-asm-syntax=intel',
+                  '-filetype=obj',
+                  '-o=' + obj_sz,
+                  asm_sz])
+        objs.append(obj_sz)
+        # Each original bitcode file needs to be translated by the
+        # LLVM toolchain and have its object file linked in.  There
+        # are two ways to do this: explicitly use llc, or include the
+        # .ll file in the link command.  It turns out that these two
+        # approaches can produce different semantics on some undefined
+        # bitcode behavior.  Specifically, LLVM produces different
+        # results for overflowing fptoui instructions for i32 and i64
+        # on x86-32.  As it turns out, Subzero lowering was based on
+        # inspecting the object code produced by the direct llc
+        # command, so we need to directly run llc on the bitcode, even
+        # though it makes this script longer, to avoid spurious
+        # failures.  This behavior can be inspected by switching
+        # use_llc between True and False.
+        use_llc = False
+        if use_llc:
+            shellcmd([os.path.join(llvm_bin_path, 'llc'),
+                      '-filetype=obj',
+                      '-o=' + obj_llc,
+                      bitcode])
+            objs.append(obj_llc)
+        else:
+            objs.append(bitcode)
+
+    linker = 'clang' if os.path.splitext(args.driver)[1] == '.c' else 'clang++'
+    shellcmd([os.path.join(llvm_bin_path, linker), '-g', '-m32', args.driver] +
+             objs +
+             ['-lm', '-o', os.path.join(args.dir, args.output)])
diff --git a/crosstest/runtests.sh b/crosstest/runtests.sh
new file mode 100755
index 0000000..400e7a4
--- /dev/null
+++ b/crosstest/runtests.sh
@@ -0,0 +1,59 @@
+#!/bin/sh
+
+# TODO: Retire this script and move the individual tests into the lit
+# framework, to leverage parallel testing and other lit goodness.
+
+set -eux
+
+OPTLEVELS="m1"
+OUTDIR=Output
+# Clean the output directory to avoid reusing stale results.
+rm -rf "${OUTDIR}"
+mkdir -p "${OUTDIR}"
+
+for optlevel in ${OPTLEVELS} ; do
+
+    ./crosstest.py -O${optlevel} --prefix=Subzero_ --target=x8632 \
+        --dir="${OUTDIR}" \
+        --llvm-bin-path="${LLVM_BIN_PATH}" \
+        --test=simple_loop.c \
+        --driver=simple_loop_main.c \
+        --output=simple_loop_O${optlevel}
+
+    ./crosstest.py -O${optlevel} --prefix=Subzero_ --target=x8632 \
+        --dir="${OUTDIR}" \
+        --llvm-bin-path="${LLVM_BIN_PATH}" \
+        --test=test_cast.cpp --test=test_cast_to_u1.ll \
+        --driver=test_cast_main.cpp \
+        --output=test_cast_O${optlevel}
+
+    ./crosstest.py -O${optlevel} --prefix=Subzero_ --target=x8632 \
+        --dir="${OUTDIR}" \
+        --llvm-bin-path="${LLVM_BIN_PATH}" \
+        --test=test_fcmp.pnacl.ll \
+        --driver=test_fcmp_main.cpp \
+        --output=test_fcmp_O${optlevel}
+
+    ./crosstest.py -O${optlevel} --prefix=Subzero_ --target=x8632 \
+        --dir="${OUTDIR}" \
+        --llvm-bin-path="${LLVM_BIN_PATH}" \
+        --test=test_icmp.cpp \
+        --driver=test_icmp_main.cpp \
+        --output=test_icmp_O${optlevel}
+
+    ./crosstest.py -O${optlevel} --prefix=Subzero_ --target=x8632 \
+        --dir="${OUTDIR}" \
+        --llvm-bin-path="${LLVM_BIN_PATH}" \
+        --test=test_arith.cpp --test=test_arith_frem.ll \
+        --driver=test_arith_main.cpp \
+        --output=test_arith_O${optlevel}
+
+done
+
+for optlevel in ${OPTLEVELS} ; do
+    "${OUTDIR}"/simple_loop_O${optlevel}
+    "${OUTDIR}"/test_cast_O${optlevel}
+    "${OUTDIR}"/test_fcmp_O${optlevel}
+    "${OUTDIR}"/test_icmp_O${optlevel}
+    "${OUTDIR}"/test_arith_O${optlevel}
+done
diff --git a/crosstest/simple_loop.c b/crosstest/simple_loop.c
new file mode 100644
index 0000000..6568380
--- /dev/null
+++ b/crosstest/simple_loop.c
@@ -0,0 +1,10 @@
+// This is a simple loop that sums elements of an input array and
+// returns the result.  It's here mainly because it's one of the
+// simple examples guiding the early Subzero design.
+
+int simple_loop(int *a, int n) {
+  int sum = 0;
+  for (int i = 0; i < n; ++i)
+    sum += a[i];
+  return sum;
+}
diff --git a/crosstest/simple_loop_main.c b/crosstest/simple_loop_main.c
new file mode 100644
index 0000000..5ff36b8
--- /dev/null
+++ b/crosstest/simple_loop_main.c
@@ -0,0 +1,29 @@
+/* crosstest.py --test=simple_loop.c --driver=simple_loop_main.c \
+   --prefix=Subzero_ --output=simple_loop */
+
+#include <stdio.h>
+
+int simple_loop(int *a, int n);
+int Subzero_simple_loop(int *a, int n);
+
+int main(int argc, char **argv) {
+  unsigned TotalTests = 0;
+  unsigned Passes = 0;
+  unsigned Failures = 0;
+  int a[100];
+  for (int i = 0; i < 100; ++i)
+    a[i] = i * 2 - 100;
+  for (int i = -2; i < 100; ++i) {
+    ++TotalTests;
+    int llc_result = simple_loop(a, i);
+    int sz_result = Subzero_simple_loop(a, i);
+    if (llc_result == sz_result) {
+      ++Passes;
+    } else {
+      ++Failures;
+      printf("Failure: i=%d, llc=%d, sz=%d\n", i, llc_result, sz_result);
+    }
+  }
+  printf("TotalTests=%u Passes=%u Failures=%u\n", TotalTests, Passes, Failures);
+  return Failures;
+}
diff --git a/crosstest/test_arith.cpp b/crosstest/test_arith.cpp
new file mode 100644
index 0000000..18b4b57
--- /dev/null
+++ b/crosstest/test_arith.cpp
@@ -0,0 +1,30 @@
+// This aims to test all the arithmetic bitcode instructions across
+// all PNaCl primitive data types.
+
+#include <stdint.h>
+
+#include "test_arith.h"
+
+#define X(inst, op, isdiv)                                                     \
+  bool test##inst(bool a, bool b) { return a op b; }                           \
+  uint8_t test##inst(uint8_t a, uint8_t b) { return a op b; }                  \
+  uint16_t test##inst(uint16_t a, uint16_t b) { return a op b; }               \
+  uint32_t test##inst(uint32_t a, uint32_t b) { return a op b; }               \
+  uint64_t test##inst(uint64_t a, uint64_t b) { return a op b; }
+UINTOP_TABLE
+#undef X
+
+#define X(inst, op, isdiv)                                                     \
+  bool test##inst(bool a, bool b) { return a op b; }                           \
+  int8_t test##inst(int8_t a, int8_t b) { return a op b; }                     \
+  int16_t test##inst(int16_t a, int16_t b) { return a op b; }                  \
+  int32_t test##inst(int32_t a, int32_t b) { return a op b; }                  \
+  int64_t test##inst(int64_t a, int64_t b) { return a op b; }
+SINTOP_TABLE
+#undef X
+
+#define X(inst, op, func)                                                      \
+  float test##inst(float a, float b) { return func(a op b); }                  \
+  double test##inst(double a, double b) { return func(a op b); }
+FPOP_TABLE
+#undef X
diff --git a/crosstest/test_arith.def b/crosstest/test_arith.def
new file mode 100644
index 0000000..4cf4596
--- /dev/null
+++ b/crosstest/test_arith.def
@@ -0,0 +1,45 @@
+#ifndef TEST_ARITH_DEF
+#define TEST_ARITH_DEF
+
+#define XSTR(s) STR(s)
+#define STR(s) #s
+
+#define UINTOP_TABLE \
+  /* inst, operator, div */ \
+  X(Add,   +,        0 )    \
+  X(Sub,   -,        0 )    \
+  X(Mul,   *,        0 )    \
+  X(Udiv,  /,        1 )    \
+  X(Urem,  %,        1 )    \
+  X(Shl,   <<,       0)     \
+  X(Lshr,  >>,       0)     \
+  X(And,   &,        0 )    \
+  X(Or,    |,        0 )    \
+  X(Xor,   ^,        0 )    \
+//#define X(inst, op, isdiv)
+
+#define SINTOP_TABLE \
+  /* inst, operator, div */ \
+  X(Sdiv,  /,        1)     \
+  X(Srem,  %,        1)     \
+  X(Ashr,  >>,       0)     \
+//#define X(inst, op, isdiv)
+
+#define COMMA ,
+#define FPOP_TABLE \
+  /* inst, infix_op, func */ \
+  X(Fadd,  +,              ) \
+  X(Fsub,  -,              ) \
+  X(Fmul,  *,              ) \
+  X(Fdiv,  /,              ) \
+  X(Frem,  COMMA,    myFrem) \
+//#define X(inst, op, func)
+
+// Note: The above definition of COMMA, plus the "func" argument to
+// the X macro, are because C++ does not allow the % operator on
+// floating-point primitive types.  To work around this, the expansion
+// is "func(a infix_op b)", which becomes "myFrem(a , b)" for the Frem
+// instruction and "(a + b)" for the Fadd instruction.  The two
+// versions of myFrem() are defined in a separate bitcode file.
+
+#endif // TEST_ARITH_DEF
diff --git a/crosstest/test_arith.h b/crosstest/test_arith.h
new file mode 100644
index 0000000..996d962
--- /dev/null
+++ b/crosstest/test_arith.h
@@ -0,0 +1,29 @@
+#include <stdint.h>
+#include "test_arith.def"
+
+#define X(inst, op, isdiv)                                                     \
+  bool test##inst(bool a, bool b);                                             \
+  uint8_t test##inst(uint8_t a, uint8_t b);                                    \
+  uint16_t test##inst(uint16_t a, uint16_t b);                                 \
+  uint32_t test##inst(uint32_t a, uint32_t b);                                 \
+  uint64_t test##inst(uint64_t a, uint64_t b);
+UINTOP_TABLE
+#undef X
+
+#define X(inst, op, isdiv)                                                     \
+  bool test##inst(bool a, bool b);                                             \
+  int8_t test##inst(int8_t a, int8_t b);                                       \
+  int16_t test##inst(int16_t a, int16_t b);                                    \
+  int32_t test##inst(int32_t a, int32_t b);                                    \
+  int64_t test##inst(int64_t a, int64_t b);
+SINTOP_TABLE
+#undef X
+
+float myFrem(float a, float b);
+double myFrem(double a, double b);
+
+#define X(inst, op, func)                                                      \
+  float test##inst(float a, float b);                                          \
+  double test##inst(double a, double b);
+FPOP_TABLE
+#undef X
diff --git a/crosstest/test_arith_frem.ll b/crosstest/test_arith_frem.ll
new file mode 100644
index 0000000..34b7156
--- /dev/null
+++ b/crosstest/test_arith_frem.ll
@@ -0,0 +1,11 @@
+target triple = "i686-pc-linux-gnu"
+
+define float @_Z6myFremff(float %a, float %b) {
+  %rem = frem float %a, %b
+  ret float %rem
+}
+
+define double @_Z6myFremdd(double %a, double %b) {
+  %rem = frem double %a, %b
+  ret double %rem
+}
diff --git a/crosstest/test_arith_main.cpp b/crosstest/test_arith_main.cpp
new file mode 100644
index 0000000..745da61
--- /dev/null
+++ b/crosstest/test_arith_main.cpp
@@ -0,0 +1,193 @@
+/* crosstest.py --test=test_arith.cpp --test=test_arith_frem.ll \
+   --driver=test_arith_main.cpp --prefix=Subzero_ --output=test_arith */
+
+#include <stdint.h>
+
+#include <cfloat>
+#include <cstring> // memcmp
+#include <iostream>
+
+// Include test_arith.h twice - once normally, and once within the
+// Subzero_ namespace, corresponding to the llc and Subzero translated
+// object files, respectively.
+#include "test_arith.h"
+namespace Subzero_ {
+#include "test_arith.h"
+}
+
+volatile unsigned Values[] = { 0x0,        0x1,        0x7ffffffe, 0x7fffffff,
+                               0x80000000, 0x80000001, 0xfffffffe, 0xffffffff,
+                               0x7e,       0x7f,       0x80,       0x81,
+                               0xfe,       0xff,       0x100,      0x101,
+                               0x7ffe,     0x7fff,     0x8000,     0x8001,
+                               0xfffe,     0xffff,     0x10000,    0x10001, };
+const static size_t NumValues = sizeof(Values) / sizeof(*Values);
+
+template <typename TypeUnsigned, typename TypeSigned>
+void testsInt(size_t &TotalTests, size_t &Passes, size_t &Failures) {
+  typedef TypeUnsigned (*FuncTypeUnsigned)(TypeUnsigned, TypeUnsigned);
+  typedef TypeSigned (*FuncTypeSigned)(TypeSigned, TypeSigned);
+  static struct {
+    const char *Name;
+    FuncTypeUnsigned FuncLlc;
+    FuncTypeUnsigned FuncSz;
+    bool ExcludeDivExceptions; // for divide related tests
+  } Funcs[] = {
+#define X(inst, op, isdiv)                                                     \
+  {                                                                            \
+    STR(inst), (FuncTypeUnsigned)test##inst,                                   \
+        (FuncTypeUnsigned)Subzero_::test##inst, isdiv                          \
+  }                                                                            \
+  ,
+      UINTOP_TABLE
+#undef X
+#define X(inst, op, isdiv)                                                     \
+  {                                                                            \
+    STR(inst), (FuncTypeUnsigned)(FuncTypeSigned)test##inst,                   \
+        (FuncTypeUnsigned)(FuncTypeSigned)Subzero_::test##inst, isdiv          \
+  }                                                                            \
+  ,
+          SINTOP_TABLE
+#undef X
+    };
+  const static size_t NumFuncs = sizeof(Funcs) / sizeof(*Funcs);
+
+  if (sizeof(TypeUnsigned) <= sizeof(uint32_t)) {
+    // This is the "normal" version of the loop nest, for 32-bit or
+    // narrower types.
+    for (size_t f = 0; f < NumFuncs; ++f) {
+      for (size_t i = 0; i < NumValues; ++i) {
+        for (size_t j = 0; j < NumValues; ++j) {
+          TypeUnsigned Value1 = Values[i];
+          TypeUnsigned Value2 = Values[j];
+          // Avoid HW divide-by-zero exception.
+          if (Funcs[f].ExcludeDivExceptions && Value2 == 0)
+            continue;
+          // Avoid HW overflow exception (on x86-32).  TODO: adjust
+          // for other architectures.
+          if (Funcs[f].ExcludeDivExceptions && Value1 == 0x80000000 &&
+              Value2 == 0xffffffff)
+            continue;
+          ++TotalTests;
+          TypeUnsigned ResultSz = Funcs[f].FuncSz(Value1, Value2);
+          TypeUnsigned ResultLlc = Funcs[f].FuncLlc(Value1, Value2);
+          if (ResultSz == ResultLlc) {
+            ++Passes;
+          } else {
+            ++Failures;
+            std::cout << "test" << Funcs[f].Name << (8 * sizeof(TypeUnsigned))
+                      << "(" << Value1 << ", " << Value2
+                      << "): sz=" << (unsigned)ResultSz
+                      << " llc=" << (unsigned)ResultLlc << std::endl;
+          }
+        }
+      }
+    }
+  } else {
+    // This is the 64-bit version.  Test values are synthesized from
+    // the 32-bit values in Values[].
+    for (size_t f = 0; f < NumFuncs; ++f) {
+      for (size_t iLo = 0; iLo < NumValues; ++iLo) {
+        for (size_t iHi = 0; iHi < NumValues; ++iHi) {
+          for (size_t jLo = 0; jLo < NumValues; ++jLo) {
+            for (size_t jHi = 0; jHi < NumValues; ++jHi) {
+              TypeUnsigned Value1 =
+                  (((TypeUnsigned)Values[iHi]) << 32) + Values[iLo];
+              TypeUnsigned Value2 =
+                  (((TypeUnsigned)Values[jHi]) << 32) + Values[jLo];
+              // Avoid HW divide-by-zero exception.
+              if (Funcs[f].ExcludeDivExceptions && Value2 == 0)
+                continue;
+              ++TotalTests;
+              TypeUnsigned ResultSz = Funcs[f].FuncSz(Value1, Value2);
+              TypeUnsigned ResultLlc = Funcs[f].FuncLlc(Value1, Value2);
+              if (ResultSz == ResultLlc) {
+                ++Passes;
+              } else {
+                ++Failures;
+                std::cout << "test" << Funcs[f].Name
+                          << (8 * sizeof(TypeUnsigned)) << "(" << Value1 << ", "
+                          << Value2 << "): sz=" << (unsigned)ResultSz
+                          << " llc=" << (unsigned)ResultLlc << std::endl;
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
+template <typename Type>
+void testsFp(size_t &TotalTests, size_t &Passes, size_t &Failures) {
+  static const Type NegInf = -1.0 / 0.0;
+  static const Type PosInf = 1.0 / 0.0;
+  static const Type Nan = 0.0 / 0.0;
+  volatile Type Values[] = {
+    0,                    1,                    0x7e,
+    0x7f,                 0x80,                 0x81,
+    0xfe,                 0xff,                 0x7ffe,
+    0x7fff,               0x8000,               0x8001,
+    0xfffe,               0xffff,               0x7ffffffe,
+    0x7fffffff,           0x80000000,           0x80000001,
+    0xfffffffe,           0xffffffff,           0x100000000ll,
+    0x100000001ll,        0x7ffffffffffffffell, 0x7fffffffffffffffll,
+    0x8000000000000000ll, 0x8000000000000001ll, 0xfffffffffffffffell,
+    0xffffffffffffffffll, NegInf,               PosInf,
+    Nan,                  FLT_MIN,              FLT_MAX,
+    DBL_MIN,              DBL_MAX
+  };
+  const static size_t NumValues = sizeof(Values) / sizeof(*Values);
+  typedef Type (*FuncType)(Type, Type);
+  static struct {
+    const char *Name;
+    FuncType FuncLlc;
+    FuncType FuncSz;
+  } Funcs[] = {
+#define X(inst, op, func)                                                      \
+  { STR(inst), (FuncType)test##inst, (FuncType)Subzero_::test##inst }          \
+  ,
+      FPOP_TABLE
+#undef X
+    };
+  const static size_t NumFuncs = sizeof(Funcs) / sizeof(*Funcs);
+
+  for (size_t f = 0; f < NumFuncs; ++f) {
+    for (size_t i = 0; i < NumValues; ++i) {
+      for (size_t j = 0; j < NumValues; ++j) {
+        Type Value1 = Values[i];
+        Type Value2 = Values[j];
+        ++TotalTests;
+        Type ResultSz = Funcs[f].FuncSz(Value1, Value2);
+        Type ResultLlc = Funcs[f].FuncLlc(Value1, Value2);
+        // Compare results using memcmp() in case they are both NaN.
+        if (!memcmp(&ResultSz, &ResultLlc, sizeof(Type))) {
+          ++Passes;
+        } else {
+          ++Failures;
+          std::cout << std::fixed << "test" << Funcs[f].Name
+                    << (8 * sizeof(Type)) << "(" << Value1 << ", " << Value2
+                    << "): sz=" << ResultSz << " llc=" << ResultLlc
+                    << std::endl;
+        }
+      }
+    }
+  }
+}
+
+int main(int argc, char **argv) {
+  size_t TotalTests = 0;
+  size_t Passes = 0;
+  size_t Failures = 0;
+
+  testsInt<uint8_t, int8_t>(TotalTests, Passes, Failures);
+  testsInt<uint16_t, int16_t>(TotalTests, Passes, Failures);
+  testsInt<uint32_t, int32_t>(TotalTests, Passes, Failures);
+  testsInt<uint64_t, int64_t>(TotalTests, Passes, Failures);
+  testsFp<float>(TotalTests, Passes, Failures);
+  testsFp<double>(TotalTests, Passes, Failures);
+
+  std::cout << "TotalTests=" << TotalTests << " Passes=" << Passes
+            << " Failures=" << Failures << "\n";
+  return Failures;
+}
diff --git a/crosstest/test_cast.cpp b/crosstest/test_cast.cpp
new file mode 100644
index 0000000..1035109
--- /dev/null
+++ b/crosstest/test_cast.cpp
@@ -0,0 +1,61 @@
+// This aims to test all the conversion bitcode instructions across
+// all PNaCl primitive data types.
+
+#include <stdint.h>
+#include "test_cast.h"
+
+template <typename FromType, typename ToType>
+ToType __attribute__((noinline)) cast(FromType a) {
+  return (ToType)a;
+}
+
+template <typename FromType, typename ToType>
+ToType __attribute__((noinline)) castBits(FromType a) {
+  return *(ToType *)&a;
+}
+
+// The purpose of the following sets of templates is to force
+// cast<A,B>() to be instantiated in the resulting bitcode file for
+// all <A,B>, so that they can be called from the driver.
+template <typename ToType> class Caster {
+  static ToType f(bool a) { return cast<bool, ToType>(a); }
+  static ToType f(int8_t a) { return cast<int8_t, ToType>(a); }
+  static ToType f(uint8_t a) { return cast<uint8_t, ToType>(a); }
+  static ToType f(int16_t a) { return cast<int16_t, ToType>(a); }
+  static ToType f(uint16_t a) { return cast<uint16_t, ToType>(a); }
+  static ToType f(int32_t a) { return cast<int32_t, ToType>(a); }
+  static ToType f(uint32_t a) { return cast<uint32_t, ToType>(a); }
+  static ToType f(int64_t a) { return cast<int64_t, ToType>(a); }
+  static ToType f(uint64_t a) { return cast<uint64_t, ToType>(a); }
+  static ToType f(float a) { return cast<float, ToType>(a); }
+  static ToType f(double a) { return cast<double, ToType>(a); }
+};
+
+// Comment out the definition of Caster<bool> because clang compiles
+// casts to bool using icmp instead of the desired cast instruction.
+// The corrected definitions are in test_cast_to_u1.ll.
+
+// template class Caster<bool>;
+
+template class Caster<int8_t>;
+template class Caster<uint8_t>;
+template class Caster<int16_t>;
+template class Caster<uint16_t>;
+template class Caster<int32_t>;
+template class Caster<uint32_t>;
+template class Caster<int64_t>;
+template class Caster<uint64_t>;
+template class Caster<float>;
+template class Caster<double>;
+
+// This function definition forces castBits<A,B>() to be instantiated
+// in the resulting bitcode file for the 4 relevant <A,B>
+// combinations, so that they can be called from the driver.
+double makeBitCasters() {
+  double Result = 0;
+  Result += castBits<uint32_t, float>(0);
+  Result += castBits<uint64_t, double>(0);
+  Result += castBits<float, uint32_t>(0);
+  Result += castBits<double, uint64_t>(0);
+  return Result;
+}
diff --git a/crosstest/test_cast.h b/crosstest/test_cast.h
new file mode 100644
index 0000000..bf59cd9
--- /dev/null
+++ b/crosstest/test_cast.h
@@ -0,0 +1,2 @@
+template <typename FromType, typename ToType> ToType cast(FromType a);
+template <typename FromType, typename ToType> ToType castBits(FromType a);
diff --git a/crosstest/test_cast_main.cpp b/crosstest/test_cast_main.cpp
new file mode 100644
index 0000000..330f984
--- /dev/null
+++ b/crosstest/test_cast_main.cpp
@@ -0,0 +1,225 @@
+/* crosstest.py --test=test_cast.cpp --test=test_cast_to_u1.ll \
+   --driver=test_cast_main.cpp --prefix=Subzero_ --output=test_cast */
+
+#include <cstring>
+#include <iostream>
+#include <stdint.h>
+
+// Include test_cast.h twice - once normally, and once within the
+// Subzero_ namespace, corresponding to the llc and Subzero translated
+// object files, respectively.
+#include "test_cast.h"
+namespace Subzero_ {
+#include "test_cast.h"
+}
+
+#define XSTR(s) STR(s)
+#define STR(s) #s
+#define COMPARE(Func, FromCName, ToCName, Input)                               \
+  do {                                                                         \
+    ToCName ResultSz, ResultLlc;                                               \
+    ResultLlc = Func<FromCName, ToCName>(Input);                               \
+    ResultSz = Subzero_::Func<FromCName, ToCName>(Input);                      \
+    ++TotalTests;                                                              \
+    if (!memcmp(&ResultLlc, &ResultSz, sizeof(ToCName))) {                     \
+      ++Passes;                                                                \
+    } else {                                                                   \
+      ++Failures;                                                              \
+      std::cout << std::fixed << XSTR(Func)                                    \
+                << "<" XSTR(FromCName) ", " XSTR(ToCName) ">(" << Input        \
+                << "): sz=" << ResultSz << " llc=" << ResultLlc << "\n";       \
+    }                                                                          \
+  } while (0)
+
+template <typename FromType>
+void testValue(FromType Val, size_t &TotalTests, size_t &Passes,
+               size_t &Failures) {
+  COMPARE(cast, FromType, bool, Val);
+  COMPARE(cast, FromType, uint8_t, Val);
+  COMPARE(cast, FromType, int8_t, Val);
+  COMPARE(cast, FromType, uint16_t, Val);
+  COMPARE(cast, FromType, int16_t, Val);
+  COMPARE(cast, FromType, uint32_t, Val);
+  COMPARE(cast, FromType, int32_t, Val);
+  COMPARE(cast, FromType, uint64_t, Val);
+  COMPARE(cast, FromType, int64_t, Val);
+  COMPARE(cast, FromType, float, Val);
+  COMPARE(cast, FromType, double, Val);
+}
+
+int main(int argc, char **argv) {
+  size_t TotalTests = 0;
+  size_t Passes = 0;
+  size_t Failures = 0;
+
+  volatile bool ValsUi1[] = { false, true };
+  static const size_t NumValsUi1 = sizeof(ValsUi1) / sizeof(*ValsUi1);
+  volatile uint8_t ValsUi8[] = { 0, 1, 0x7e, 0x7f, 0x80, 0x81, 0xfe, 0xff };
+  static const size_t NumValsUi8 = sizeof(ValsUi8) / sizeof(*ValsUi8);
+
+  volatile int8_t ValsSi8[] = { 0, 1, 0x7e, 0x7f, 0x80, 0x81, 0xfe, 0xff };
+  static const size_t NumValsSi8 = sizeof(ValsSi8) / sizeof(*ValsSi8);
+
+  volatile uint16_t ValsUi16[] = { 0,      1,      0x7e,   0x7f,   0x80,
+                                   0x81,   0xfe,   0xff,   0x7ffe, 0x7fff,
+                                   0x8000, 0x8001, 0xfffe, 0xffff };
+  static const size_t NumValsUi16 = sizeof(ValsUi16) / sizeof(*ValsUi16);
+
+  volatile int16_t ValsSi16[] = { 0,      1,      0x7e,   0x7f,   0x80,
+                                  0x81,   0xfe,   0xff,   0x7ffe, 0x7fff,
+                                  0x8000, 0x8001, 0xfffe, 0xffff };
+  static const size_t NumValsSi16 = sizeof(ValsSi16) / sizeof(*ValsSi16);
+
+  volatile size_t ValsUi32[] = {
+    0,          1,          0x7e,       0x7f,       0x80,
+    0x81,       0xfe,       0xff,       0x7ffe,     0x7fff,
+    0x8000,     0x8001,     0xfffe,     0xffff,     0x7ffffffe,
+    0x7fffffff, 0x80000000, 0x80000001, 0xfffffffe, 0xffffffff
+  };
+  static const size_t NumValsUi32 = sizeof(ValsUi32) / sizeof(*ValsUi32);
+
+  volatile size_t ValsSi32[] = {
+    0,          1,          0x7e,       0x7f,       0x80,
+    0x81,       0xfe,       0xff,       0x7ffe,     0x7fff,
+    0x8000,     0x8001,     0xfffe,     0xffff,     0x7ffffffe,
+    0x7fffffff, 0x80000000, 0x80000001, 0xfffffffe, 0xffffffff
+  };
+  static const size_t NumValsSi32 = sizeof(ValsSi32) / sizeof(*ValsSi32);
+
+  volatile uint64_t ValsUi64[] = {
+    0,                     1,                     0x7e,
+    0x7f,                  0x80,                  0x81,
+    0xfe,                  0xff,                  0x7ffe,
+    0x7fff,                0x8000,                0x8001,
+    0xfffe,                0xffff,                0x7ffffffe,
+    0x7fffffff,            0x80000000,            0x80000001,
+    0xfffffffe,            0xffffffff,            0x100000000ull,
+    0x100000001ull,        0x7ffffffffffffffeull, 0x7fffffffffffffffull,
+    0x8000000000000000ull, 0x8000000000000001ull, 0xfffffffffffffffeull,
+    0xffffffffffffffffull
+  };
+  static const size_t NumValsUi64 = sizeof(ValsUi64) / sizeof(*ValsUi64);
+
+  volatile int64_t ValsSi64[] = {
+    0,                    1,                    0x7e,
+    0x7f,                 0x80,                 0x81,
+    0xfe,                 0xff,                 0x7ffe,
+    0x7fff,               0x8000,               0x8001,
+    0xfffe,               0xffff,               0x7ffffffe,
+    0x7fffffff,           0x80000000,           0x80000001,
+    0xfffffffe,           0xffffffff,           0x100000000ll,
+    0x100000001ll,        0x7ffffffffffffffell, 0x7fffffffffffffffll,
+    0x8000000000000000ll, 0x8000000000000001ll, 0xfffffffffffffffell,
+    0xffffffffffffffffll
+  };
+  static const size_t NumValsSi64 = sizeof(ValsSi64) / sizeof(*ValsSi64);
+
+  volatile float ValsF32[] = {
+    0,                    1,                    0x7e,
+    0x7f,                 0x80,                 0x81,
+    0xfe,                 0xff,                 0x7ffe,
+    0x7fff,               0x8000,               0x8001,
+    0xfffe,               0xffff,               0x7ffffffe,
+    0x7fffffff,           0x80000000,           0x80000001,
+    0xfffffffe,           0xffffffff,           0x100000000ll,
+    0x100000001ll,        0x7ffffffffffffffell, 0x7fffffffffffffffll,
+    0x8000000000000000ll, 0x8000000000000001ll, 0xfffffffffffffffell,
+    0xffffffffffffffffll
+  };
+  static const size_t NumValsF32 = sizeof(ValsF32) / sizeof(*ValsF32);
+
+  volatile double ValsF64[] = {
+    0,                    1,                    0x7e,
+    0x7f,                 0x80,                 0x81,
+    0xfe,                 0xff,                 0x7ffe,
+    0x7fff,               0x8000,               0x8001,
+    0xfffe,               0xffff,               0x7ffffffe,
+    0x7fffffff,           0x80000000,           0x80000001,
+    0xfffffffe,           0xffffffff,           0x100000000ll,
+    0x100000001ll,        0x7ffffffffffffffell, 0x7fffffffffffffffll,
+    0x8000000000000000ll, 0x8000000000000001ll, 0xfffffffffffffffell,
+    0xffffffffffffffffll
+  };
+  static const size_t NumValsF64 = sizeof(ValsF64) / sizeof(*ValsF64);
+
+  for (size_t i = 0; i < NumValsUi1; ++i) {
+    bool Val = ValsUi1[i];
+    testValue<bool>(Val, TotalTests, Passes, Failures);
+  }
+  for (size_t i = 0; i < NumValsUi8; ++i) {
+    uint8_t Val = ValsUi8[i];
+    testValue<uint8_t>(Val, TotalTests, Passes, Failures);
+  }
+  for (size_t i = 0; i < NumValsSi8; ++i) {
+    int8_t Val = ValsSi8[i];
+    testValue<int8_t>(Val, TotalTests, Passes, Failures);
+  }
+  for (size_t i = 0; i < NumValsUi16; ++i) {
+    uint16_t Val = ValsUi16[i];
+    testValue<uint16_t>(Val, TotalTests, Passes, Failures);
+  }
+  for (size_t i = 0; i < NumValsSi16; ++i) {
+    int16_t Val = ValsSi16[i];
+    testValue<int16_t>(Val, TotalTests, Passes, Failures);
+  }
+  for (size_t i = 0; i < NumValsUi32; ++i) {
+    uint32_t Val = ValsUi32[i];
+    testValue<uint32_t>(Val, TotalTests, Passes, Failures);
+    COMPARE(castBits, uint32_t, float, Val);
+  }
+  for (size_t i = 0; i < NumValsSi32; ++i) {
+    int32_t Val = ValsSi32[i];
+    testValue<int32_t>(Val, TotalTests, Passes, Failures);
+  }
+  for (size_t i = 0; i < NumValsUi64; ++i) {
+    uint64_t Val = ValsUi64[i];
+    testValue<uint64_t>(Val, TotalTests, Passes, Failures);
+    COMPARE(castBits, uint64_t, double, Val);
+  }
+  for (size_t i = 0; i < NumValsSi64; ++i) {
+    int64_t Val = ValsSi64[i];
+    testValue<int64_t>(Val, TotalTests, Passes, Failures);
+  }
+  for (size_t i = 0; i < NumValsF32; ++i) {
+    for (unsigned j = 0; j < 2; ++j) {
+      float Val = ValsF32[i];
+      if (j > 0)
+        Val = -Val;
+      testValue<float>(Val, TotalTests, Passes, Failures);
+      COMPARE(castBits, float, uint32_t, Val);
+    }
+  }
+  for (size_t i = 0; i < NumValsF64; ++i) {
+    for (unsigned j = 0; j < 2; ++j) {
+      double Val = ValsF64[i];
+      if (j > 0)
+        Val = -Val;
+      testValue<double>(Val, TotalTests, Passes, Failures);
+      COMPARE(castBits, double, uint64_t, Val);
+    }
+  }
+
+  std::cout << "TotalTests=" << TotalTests << " Passes=" << Passes
+            << " Failures=" << Failures << "\n";
+  return Failures;
+}
+
+////////////////////////////////////////////////////////////////
+
+// The following are helper definitions that should be part of the
+// Subzero runtime.
+
+extern "C" {
+uint32_t cvtdtoui32(double a) { return (uint32_t)a; }
+uint32_t cvtftoui32(float a) { return (uint32_t)a; }
+int64_t cvtdtosi64(double a) { return (int64_t)a; }
+int64_t cvtftosi64(float a) { return (int64_t)a; }
+uint64_t cvtdtoui64(double a) { return (uint64_t)a; }
+uint64_t cvtftoui64(float a) { return (uint64_t)a; }
+float cvtui64tof(uint64_t a) { return (float)a; }
+double cvtui64tod(uint64_t a) { return (double)a; }
+float cvtsi64tof(int64_t a) { return (float)a; }
+float cvtui32tof(uint32_t a) { return (float)a; }
+double cvtui32tod(uint32_t a) { return (double)a; }
+double cvtsi64tod(int64_t a) { return (double)a; }
+}
diff --git a/crosstest/test_cast_to_u1.ll b/crosstest/test_cast_to_u1.ll
new file mode 100644
index 0000000..f8a9ec6
--- /dev/null
+++ b/crosstest/test_cast_to_u1.ll
@@ -0,0 +1,92 @@
+target triple = "i686-pc-linux-gnu"
+
+define i32 @_Z4castIxbET0_T_(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
+}
+
+define i32 @_Z4castIybET0_T_(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
+}
+
+define i32 @_Z4castIibET0_T_(i32 %a) {
+entry:
+;  %tobool = icmp ne i32 %a, 0
+  %tobool = trunc i32 %a to i1
+  %tobool.ret_ext = zext i1 %tobool to i32
+  ret i32 %tobool.ret_ext
+}
+
+define i32 @_Z4castIjbET0_T_(i32 %a) {
+entry:
+;  %tobool = icmp ne i32 %a, 0
+  %tobool = trunc i32 %a to i1
+  %tobool.ret_ext = zext i1 %tobool to i32
+  ret i32 %tobool.ret_ext
+}
+
+define i32 @_Z4castIsbET0_T_(i32 %a) {
+entry:
+  %a.arg_trunc = trunc i32 %a to i16
+;  %tobool = icmp ne i16 %a.arg_trunc, 0
+  %tobool = trunc i16 %a.arg_trunc to i1
+  %tobool.ret_ext = zext i1 %tobool to i32
+  ret i32 %tobool.ret_ext
+}
+
+define i32 @_Z4castItbET0_T_(i32 %a) {
+entry:
+  %a.arg_trunc = trunc i32 %a to i16
+;  %tobool = icmp ne i16 %a.arg_trunc, 0
+  %tobool = trunc i16 %a.arg_trunc to i1
+  %tobool.ret_ext = zext i1 %tobool to i32
+  ret i32 %tobool.ret_ext
+}
+
+define i32 @_Z4castIabET0_T_(i32 %a) {
+entry:
+  %a.arg_trunc = trunc i32 %a to i8
+;  %tobool = icmp ne i8 %a.arg_trunc, 0
+  %tobool = trunc i8 %a.arg_trunc to i1
+  %tobool.ret_ext = zext i1 %tobool to i32
+  ret i32 %tobool.ret_ext
+}
+
+define i32 @_Z4castIhbET0_T_(i32 %a) {
+entry:
+  %a.arg_trunc = trunc i32 %a to i8
+;  %tobool = icmp ne i8 %a.arg_trunc, 0
+  %tobool = trunc i8 %a.arg_trunc to i1
+  %tobool.ret_ext = zext i1 %tobool to i32
+  ret i32 %tobool.ret_ext
+}
+
+define i32 @_Z4castIbbET0_T_(i32 %a) {
+entry:
+  %a.arg_trunc = trunc i32 %a to i1
+  %a.arg_trunc.ret_ext = zext i1 %a.arg_trunc to i32
+  ret i32 %a.arg_trunc.ret_ext
+}
+
+define i32 @_Z4castIdbET0_T_(double %a) {
+entry:
+;  %tobool = fcmp une double %a, 0.000000e+00
+  %tobool = fptoui double %a to i1
+  %tobool.ret_ext = zext i1 %tobool to i32
+  ret i32 %tobool.ret_ext
+}
+
+define i32 @_Z4castIfbET0_T_(float %a) {
+entry:
+;  %tobool = fcmp une float %a, 0.000000e+00
+  %tobool = fptoui float %a to i1
+  %tobool.ret_ext = zext i1 %tobool to i32
+  ret i32 %tobool.ret_ext
+}
diff --git a/crosstest/test_fcmp.def b/crosstest/test_fcmp.def
new file mode 100644
index 0000000..9f498b4
--- /dev/null
+++ b/crosstest/test_fcmp.def
@@ -0,0 +1,27 @@
+#ifndef TEST_FCMP_DEF
+#define TEST_FCMP_DEF
+
+#define XSTR(s) STR(s)
+#define STR(s) #s
+
+#define FCMP_TABLE \
+  /* cmp */ \
+  X(False) \
+  X(Oeq) \
+  X(Ogt) \
+  X(Oge) \
+  X(Olt) \
+  X(Ole) \
+  X(One) \
+  X(Ord) \
+  X(Ueq) \
+  X(Ugt) \
+  X(Uge) \
+  X(Ult) \
+  X(Ule) \
+  X(Une) \
+  X(Uno) \
+  X(True) \
+//#define X(cmp)
+
+#endif // TEST_FCMP_DEF
diff --git a/crosstest/test_fcmp.pnacl.ll b/crosstest/test_fcmp.pnacl.ll
new file mode 100644
index 0000000..7c4d42e
--- /dev/null
+++ b/crosstest/test_fcmp.pnacl.ll
@@ -0,0 +1,324 @@
+target triple = "i686-pc-linux-gnu"
+
+; This file is extracted from fp.pnacl.ll in the lit tests, with
+; the "internal" attribute removed from the functions.
+
+define 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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: je .
+; CHECK: jnp .
+
+define 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: je .
+; CHECK: jnp .
+
+define 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 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 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 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
diff --git a/crosstest/test_fcmp_main.cpp b/crosstest/test_fcmp_main.cpp
new file mode 100644
index 0000000..8677c48
--- /dev/null
+++ b/crosstest/test_fcmp_main.cpp
@@ -0,0 +1,98 @@
+/* crosstest.py --test=test_fcmp.pnacl.ll --driver=test_fcmp_main.cpp \
+   --prefix=Subzero_ --output=test_fcmp */
+
+#include <cassert>
+#include <cfloat>
+#include <cmath>
+#include <iostream>
+
+#include "test_fcmp.def"
+
+#define X(cmp)                                                                 \
+  extern "C" bool fcmp##cmp##Float(float a, float b);                          \
+  extern "C" bool fcmp##cmp##Double(double a, double b);                       \
+  extern "C" bool Subzero_fcmp##cmp##Float(float a, float b);                  \
+  extern "C" bool Subzero_fcmp##cmp##Double(double a, double b);
+FCMP_TABLE;
+#undef X
+
+int main(int argc, char **argv) {
+  static const double NegInf = -1.0 / 0.0;
+  static const double Zero = 0.0;
+  static const double Ten = 10.0;
+  static const double PosInf = 1.0 / 0.0;
+  static const double Nan = 0.0 / 0.0;
+  assert(std::fpclassify(NegInf) == FP_INFINITE);
+  assert(std::fpclassify(PosInf) == FP_INFINITE);
+  assert(std::fpclassify(Nan) == FP_NAN);
+  assert(NegInf < Zero);
+  assert(NegInf < PosInf);
+  assert(Zero < PosInf);
+
+  volatile double Values[] = { NegInf,  Zero,    DBL_MIN, FLT_MIN, Ten,
+                               FLT_MAX, DBL_MAX, PosInf,  Nan, };
+  const static size_t NumValues = sizeof(Values) / sizeof(*Values);
+
+  typedef bool (*FuncTypeFloat)(float, float);
+  typedef bool (*FuncTypeDouble)(double, double);
+  static struct {
+    const char *Name;
+    FuncTypeFloat FuncFloatSz;
+    FuncTypeFloat FuncFloatLlc;
+    FuncTypeDouble FuncDoubleSz;
+    FuncTypeDouble FuncDoubleLlc;
+  } Funcs[] = {
+#define X(cmp)                                                                 \
+  {                                                                            \
+    "fcmp" STR(cmp), Subzero_fcmp##cmp##Float, fcmp##cmp##Float,               \
+        Subzero_fcmp##cmp##Double, fcmp##cmp##Double                           \
+  }                                                                            \
+  ,
+      FCMP_TABLE
+#undef X
+    };
+  const static size_t NumFuncs = sizeof(Funcs) / sizeof(*Funcs);
+
+  bool ResultSz, ResultLlc;
+
+  size_t TotalTests = 0;
+  size_t Passes = 0;
+  size_t Failures = 0;
+
+  for (size_t f = 0; f < NumFuncs; ++f) {
+    for (size_t i = 0; i < NumValues; ++i) {
+      for (size_t j = 0; j < NumValues; ++j) {
+        ++TotalTests;
+        float Value1Float = Values[i];
+        float Value2Float = Values[j];
+        ResultSz = Funcs[f].FuncFloatSz(Value1Float, Value2Float);
+        ResultLlc = Funcs[f].FuncFloatLlc(Value1Float, Value2Float);
+        if (ResultSz == ResultLlc) {
+          ++Passes;
+        } else {
+          ++Failures;
+          std::cout << Funcs[f].Name << "Float(" << Value1Float << ", "
+                    << Value2Float << "): sz=" << ResultSz
+                    << " llc=" << ResultLlc << std::endl;
+        }
+        ++TotalTests;
+        double Value1Double = Values[i];
+        double Value2Double = Values[j];
+        ResultSz = Funcs[f].FuncDoubleSz(Value1Double, Value2Double);
+        ResultLlc = Funcs[f].FuncDoubleLlc(Value1Double, Value2Double);
+        if (ResultSz == ResultLlc) {
+          ++Passes;
+        } else {
+          ++Failures;
+          std::cout << Funcs[f].Name << "Double(" << Value1Double << ", "
+                    << Value2Double << "): sz=" << ResultSz
+                    << " llc=" << ResultLlc << std::endl;
+        }
+      }
+    }
+  }
+
+  std::cout << "TotalTests=" << TotalTests << " Passes=" << Passes
+            << " Failures=" << Failures << "\n";
+  return Failures;
+}
diff --git a/crosstest/test_icmp.cpp b/crosstest/test_icmp.cpp
new file mode 100644
index 0000000..f1b144d
--- /dev/null
+++ b/crosstest/test_icmp.cpp
@@ -0,0 +1,21 @@
+// This aims to test the icmp bitcode instruction across all PNaCl
+// primitive integer types.
+
+#include <stdint.h>
+
+#include "test_icmp.h"
+
+#define X(cmp, op)                                                             \
+  bool icmp##cmp(uint8_t a, uint8_t b) { return a op b; }                      \
+  bool icmp##cmp(uint16_t a, uint16_t b) { return a op b; }                    \
+  bool icmp##cmp(uint32_t a, uint32_t b) { return a op b; }                    \
+  bool icmp##cmp(uint64_t a, uint64_t b) { return a op b; }
+ICMP_U_TABLE
+#undef X
+#define X(cmp, op)                                                             \
+  bool icmp##cmp(int8_t a, int8_t b) { return a op b; }                        \
+  bool icmp##cmp(int16_t a, int16_t b) { return a op b; }                      \
+  bool icmp##cmp(int32_t a, int32_t b) { return a op b; }                      \
+  bool icmp##cmp(int64_t a, int64_t b) { return a op b; }
+ICMP_S_TABLE
+#undef X
diff --git a/crosstest/test_icmp.def b/crosstest/test_icmp.def
new file mode 100644
index 0000000..c7cfc96
--- /dev/null
+++ b/crosstest/test_icmp.def
@@ -0,0 +1,25 @@
+#ifndef TEST_ICMP_DEF
+#define TEST_ICMP_DEF
+
+#define XSTR(s) STR(s)
+#define STR(s) #s
+
+#define ICMP_U_TABLE \
+  /* cmp, operator */ \
+  X(Eq,  ==) \
+  X(Ne,  !=) \
+  X(Ugt, >) \
+  X(Uge, >=) \
+  X(Ult, <) \
+  X(Ule, <=) \
+//#define X(cmp, op)
+
+#define ICMP_S_TABLE \
+  /* cmp, operator */ \
+  X(Sgt, >) \
+  X(Sge, >=) \
+  X(Slt, <) \
+  X(Sle, <=) \
+//#define X(cmp, op)
+
+#endif // TEST_ICMP_DEF
diff --git a/crosstest/test_icmp.h b/crosstest/test_icmp.h
new file mode 100644
index 0000000..d4ce9f1
--- /dev/null
+++ b/crosstest/test_icmp.h
@@ -0,0 +1,17 @@
+#include "test_icmp.def"
+
+#define X(cmp, op)                                                             \
+  bool icmp##cmp(uint8_t a, uint8_t b);                                        \
+  bool icmp##cmp(uint16_t a, uint16_t b);                                      \
+  bool icmp##cmp(uint32_t a, uint32_t b);                                      \
+  bool icmp##cmp(uint64_t a, uint64_t b);
+ICMP_U_TABLE
+#undef X
+
+#define X(cmp, op)                                                             \
+  bool icmp##cmp(int8_t a, int8_t b);                                          \
+  bool icmp##cmp(int16_t a, int16_t b);                                        \
+  bool icmp##cmp(int32_t a, int32_t b);                                        \
+  bool icmp##cmp(int64_t a, int64_t b);
+ICMP_S_TABLE
+#undef X
diff --git a/crosstest/test_icmp_main.cpp b/crosstest/test_icmp_main.cpp
new file mode 100644
index 0000000..3981fcf
--- /dev/null
+++ b/crosstest/test_icmp_main.cpp
@@ -0,0 +1,118 @@
+/* crosstest.py --test=test_icmp.cpp --driver=test_icmp_main.cpp \
+   --prefix=Subzero_ --output=test_icmp */
+
+#include <stdint.h>
+#include <iostream>
+
+// Include test_icmp.h twice - once normally, and once within the
+// Subzero_ namespace, corresponding to the llc and Subzero translated
+// object files, respectively.
+#include "test_icmp.h"
+namespace Subzero_ {
+#include "test_icmp.h"
+}
+
+volatile unsigned Values[] = { 0x0,        0x1,        0x7ffffffe, 0x7fffffff,
+                               0x80000000, 0x80000001, 0xfffffffe, 0xffffffff,
+                               0x7e,       0x7f,       0x80,       0x81,
+                               0xfe,       0xff,       0x100,      0x101,
+                               0x7ffe,     0x7fff,     0x8000,     0x8001,
+                               0xfffe,     0xffff,     0x10000,    0x10001, };
+const static size_t NumValues = sizeof(Values) / sizeof(*Values);
+
+template <typename TypeUnsigned, typename TypeSigned>
+void testsInt(size_t &TotalTests, size_t &Passes, size_t &Failures) {
+  typedef bool (*FuncTypeUnsigned)(TypeUnsigned, TypeUnsigned);
+  typedef bool (*FuncTypeSigned)(TypeSigned, TypeSigned);
+  static struct {
+    const char *Name;
+    FuncTypeUnsigned FuncLlc;
+    FuncTypeUnsigned FuncSz;
+  } Funcs[] = {
+#define X(cmp, op)                                                             \
+  {                                                                            \
+    STR(inst), (FuncTypeUnsigned)icmp##cmp,                                    \
+        (FuncTypeUnsigned)Subzero_::icmp##cmp                                  \
+  }                                                                            \
+  ,
+      ICMP_U_TABLE
+#undef X
+#define X(cmp, op)                                                             \
+  {                                                                            \
+    STR(inst), (FuncTypeUnsigned)(FuncTypeSigned)icmp##cmp,                    \
+        (FuncTypeUnsigned)(FuncTypeSigned)Subzero_::icmp##cmp                  \
+  }                                                                            \
+  ,
+          ICMP_S_TABLE
+#undef X
+    };
+  const static size_t NumFuncs = sizeof(Funcs) / sizeof(*Funcs);
+
+  if (sizeof(TypeUnsigned) <= sizeof(uint32_t)) {
+    // This is the "normal" version of the loop nest, for 32-bit or
+    // narrower types.
+    for (size_t f = 0; f < NumFuncs; ++f) {
+      for (size_t i = 0; i < NumValues; ++i) {
+        for (size_t j = 0; j < NumValues; ++j) {
+          TypeUnsigned Value1 = Values[i];
+          TypeUnsigned Value2 = Values[j];
+          ++TotalTests;
+          bool ResultSz = Funcs[f].FuncSz(Value1, Value2);
+          bool ResultLlc = Funcs[f].FuncLlc(Value1, Value2);
+          if (ResultSz == ResultLlc) {
+            ++Passes;
+          } else {
+            ++Failures;
+            std::cout << "icmp" << Funcs[f].Name << (8 * sizeof(TypeUnsigned))
+                      << "(" << Value1 << ", " << Value2 << "): sz=" << ResultSz
+                      << " llc=" << ResultLlc << std::endl;
+          }
+        }
+      }
+    }
+  } else {
+    // This is the 64-bit version.  Test values are synthesized from
+    // the 32-bit values in Values[].
+    for (size_t f = 0; f < NumFuncs; ++f) {
+      for (size_t iLo = 0; iLo < NumValues; ++iLo) {
+        for (size_t iHi = 0; iHi < NumValues; ++iHi) {
+          for (size_t jLo = 0; jLo < NumValues; ++jLo) {
+            for (size_t jHi = 0; jHi < NumValues; ++jHi) {
+              TypeUnsigned Value1 =
+                  (((TypeUnsigned)Values[iHi]) << 32) + Values[iLo];
+              TypeUnsigned Value2 =
+                  (((TypeUnsigned)Values[jHi]) << 32) + Values[jLo];
+              ++TotalTests;
+              bool ResultSz = Funcs[f].FuncSz(Value1, Value2);
+              bool ResultLlc = Funcs[f].FuncLlc(Value1, Value2);
+              if (ResultSz == ResultLlc) {
+                ++Passes;
+              } else {
+                ++Failures;
+                std::cout << "icmp" << Funcs[f].Name
+                          << (8 * sizeof(TypeUnsigned)) << "(" << Value1 << ", "
+                          << Value2 << "): sz=" << ResultSz
+                          << " llc=" << ResultLlc << std::endl;
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
+int main(int argc, char **argv) {
+  size_t TotalTests = 0;
+  size_t Passes = 0;
+  size_t Failures = 0;
+
+  testsInt<uint8_t, int8_t>(TotalTests, Passes, Failures);
+  testsInt<uint16_t, int16_t>(TotalTests, Passes, Failures);
+  testsInt<uint32_t, int32_t>(TotalTests, Passes, Failures);
+  testsInt<uint64_t, int64_t>(TotalTests, Passes, Failures);
+
+  std::cout << "TotalTests=" << TotalTests << " Passes=" << Passes
+            << " Failures=" << Failures << "\n";
+  return Failures;
+}