Refactor Subzero initialization and add a browser callback handler.

Handlers are represented as a "compile server" even though
right now it can really only handle a single
compile request.

Then there can be a commandline-based server and a
browser-based server. This server takes over the main
thread. In the browser-based case the server can block,
waiting on bytes to be pushed. This becomes a producer of
bitcode bytes.

The original main thread which did bitcode reading is now
shifted to yet another worker thread, which is then the
consumer of bitcode bytes.

This uses an IRT interface for listening to messages
from the browser:
https://codereview.chromium.org/984713003/

TEST=Build the IRT core nexe w/ the above patch and compile w/ something like:

echo """
readwrite_file objfile /tmp/temp.nexe---gcc.opt.stripped.pexe---.o
rpc StreamInitWithSplit i(4) h(objfile) h(invalid) h(invalid) h(invalid) h(invalid) h(invalid) h(invalid) h(invalid) h(invalid) h(invalid) h(invalid) h(invalid) h(invalid) h(invalid) h(invalid) h(invalid) C(4,-O2\x00) * s()
stream_file /usr/local/google/home/jvoung/pexe_tests/gcc.opt.stripped.pexe 65536 1000000000
rpc StreamEnd * i() s() s() s()
echo "pnacl-sz complete"
""" | scons-out/opt-linux-x86-32/staging/sel_universal \
    -a -B scons-out/nacl_irt-x86-32/staging/irt_core.nexe \
    --abort_on_error \
    -- toolchain/linux_x86/pnacl_translator/translator/x86-32/bin/pnacl-sz.nexe

echo """
readwrite_file nexefile /tmp/temp.nexe.tmp
readonly_file objfile0 /tmp/temp.nexe---gcc.opt.stripped.pexe---.o
rpc RunWithSplit i(1) h(objfile0) h(invalid) h(invalid) h(invalid) h(invalid) h(invalid) h(invalid) h(invalid) h(invalid) h(invalid) h(invalid) h(invalid) h(invalid) h(invalid) h(invalid) h(invalid) h(nexefile) *
echo "ld complete"
""" | /usr/local/google/home/nacl3/native_client/scons-out/opt-linux-x86-32/staging/sel_universal \
    --abort_on_error \
    -a -B \
    scons-out/nacl_irt-x86-32/staging/irt_core.nexe \
    -E NACL_IRT_OPEN_RESOURCE_BASE=toolchain/linux_x86/pnacl_translator/translator/x86-32/lib/ \
    -E NACL_IRT_OPEN_RESOURCE_REMAP=libpnacl_irt_shim.a:libpnacl_irt_shim_dummy.a \
    -- toolchain/linux_x86/pnacl_translator/translator/x86-32/bin/ld.nexe

BUG= https://code.google.com/p/nativeclient/issues/detail?id=4091
R=kschimpf@google.com, stichnot@chromium.org

Review URL: https://codereview.chromium.org/997773002
diff --git a/Makefile b/Makefile
index 7e1bed9..be05c15 100644
--- a/Makefile
+++ b/Makefile
@@ -22,9 +22,9 @@
 ifeq ($(PNACL_BROWSER_TRANSLATOR),1)
   CPP.Defines += -DALLOW_DUMP=0 -DALLOW_LLVM_CL=0 -DALLOW_LLVM_IR=0 \
     -DALLOW_LLVM_IR_AS_INPUT=0 -DALLOW_DISABLE_IR_GEN=0 \
-    -DALLOW_MINIMAL_BUILD=1
+    -DALLOW_MINIMAL_BUILD=1 -DPNACL_BROWSER_TRANSLATOR=1
 else
   CPP.Defines += -DALLOW_DUMP=1 -DALLOW_LLVM_CL=1 -DALLOW_LLVM_IR=1 \
     -DALLOW_LLVM_IR_AS_INPUT=1 -DALLOW_DISABLE_IR_GEN=1 \
-    -DALLOW_MINIMAL_BUILD=0
+    -DALLOW_MINIMAL_BUILD=0 -DPNACL_BROWSER_TRANSLATOR=0
 endif
diff --git a/Makefile.standalone b/Makefile.standalone
index c0d2cbb..2d3f52f 100644
--- a/Makefile.standalone
+++ b/Makefile.standalone
@@ -4,7 +4,7 @@
 #
 #   make LLVM_SRC_PATH=<path> LLVM_BIN_PATH=<path> \
 #        LIBCXX_INSTALL_PATH=<path> CLANG_PATH=<path> \
-#        BINUTILS_BIN_PATH=<path> ...
+#        PNACL_BIN_PATH=<path> ...
 #
 
 # LLVM_SRC_PATH is the path to the root of the checked out source code. This
@@ -22,24 +22,33 @@
 LLVM_BIN_PATH ?= $(shell readlink -e \
 	../../out/llvm_x86_64_linux_work/Release+Asserts/bin)
 
+# The x86-32-specific sandboxed translator directory.
+# It holds sandboxed versions of libraries and binaries.
+SB_LLVM_PATH ?= $(shell readlink -e \
+	../../out/sandboxed_translators_work/translator-i686/llvm-sb/Release)
+
+# NACL_ROOT is the root of the native client repository.
+NACL_ROOT ?= $(shell python -c "import sys; sys.path.insert(0, 'pydir'); \
+	import utils; print utils.FindBaseNaCl()")
+
 # PNACL_TOOLCHAIN_ROOT is the location of the PNaCl toolchain.
 # This is used as the default root for finding binutils, libcxx, etc.
-PNACL_TOOLCHAIN_ROOT = $(shell readlink -e \
-	../../../toolchain/linux_x86/pnacl_newlib)
+PNACL_TOOLCHAIN_ROOT ?= $(shell readlink -e \
+	$(NACL_ROOT)/toolchain/linux_x86/pnacl_newlib)
+
+# The location of PNaCl tools (e.g., binutils objdump, pnacl-clang++, etc.).
+PNACL_BIN_PATH ?= $(shell readlink -e $(PNACL_TOOLCHAIN_ROOT)/bin)
 
 # CLANG_PATH is the location of the clang compiler to use for building
 # the host binaries.
 CLANG_PATH ?= $(shell readlink -e \
-	../../../../third_party/llvm-build/Release+Asserts/bin)
+	$(NACL_ROOT)/../third_party/llvm-build/Release+Asserts/bin)
 
 # LIBCXX_INSTALL_PATH is the directory where libc++ is located. It should
 # contain header files and corresponding libraries. This is used for
 # building the host binaries in conjuction with clang.
 LIBCXX_INSTALL_PATH ?= $(PNACL_TOOLCHAIN_ROOT)
 
-# The location of binutils tools (e.g., objdump) for testing.
-BINUTILS_BIN_PATH ?= $(shell readlink -e $(PNACL_TOOLCHAIN_ROOT)/bin)
-
 HOST_ARCH ?= x86_64
 ifeq ($(HOST_ARCH),x86_64)
   HOST_FLAGS = -m64 -stdlib=libc++
@@ -52,28 +61,33 @@
 ifdef DEBUG
   OBJDIR = build/Debug
   OPTLEVEL = -O0
+  LINKOPTLEVEL = -O0
 else
   OBJDIR = build/Release
   OPTLEVEL = -O2 -ffunction-sections -fdata-sections
+  LINKOPTLEVEL = -O2
 endif
 
 # The list of CXX defines that are dependent on build parameters.
-CXX_DEFINES =
+BASE_CXX_DEFINES =
 CXX_EXTRA =
 LD_EXTRA =
 
 ifdef MINIMAL
   NOASSERT = 1
   OBJDIR := $(OBJDIR)+Min
-  CXX_DEFINES += -DALLOW_DUMP=0 -DALLOW_LLVM_CL=0 -DALLOW_LLVM_IR=0 \
+  BASE_CXX_DEFINES += -DALLOW_DUMP=0 -DALLOW_LLVM_CL=0 -DALLOW_LLVM_IR=0 \
 	-DALLOW_LLVM_IR_AS_INPUT=0 -DALLOW_DISABLE_IR_GEN=0 \
-        -DALLOW_MINIMAL_BUILD=1
+    -DALLOW_MINIMAL_BUILD=1
 else
-  CXX_DEFINES += -DALLOW_DUMP=1 -DALLOW_LLVM_CL=1 -DALLOW_LLVM_IR=1 \
+  BASE_CXX_DEFINES += -DALLOW_DUMP=1 -DALLOW_LLVM_CL=1 -DALLOW_LLVM_IR=1 \
 	-DALLOW_LLVM_IR_AS_INPUT=1 -DALLOW_DISABLE_IR_GEN=1 \
-        -DALLOW_MINIMAL_BUILD=0
+    -DALLOW_MINIMAL_BUILD=0
 endif
 
+SB_CXX_DEFINES := $(BASE_CXX_DEFINES) -DPNACL_BROWSER_TRANSLATOR=1
+CXX_DEFINES := $(BASE_CXX_DEFINES) -DPNACL_BROWSER_TRANSLATOR=0
+
 ifdef NOASSERT
   ASSERTIONS = -DNDEBUG
 else
@@ -87,37 +101,63 @@
   LD_EXTRA += -fsanitize=thread
 endif
 
+SB_OBJDIR := $(OBJDIR)+Sandboxed
+
 $(info -----------------------------------------------)
 $(info Using LLVM_SRC_PATH = $(LLVM_SRC_PATH))
 $(info Using LLVM_BIN_PATH = $(LLVM_BIN_PATH))
+$(info Using SB_LLVM_PATH = $(SB_LLVM_PATH))
+$(info Using NACL_ROOT = $(NACL_ROOT))
 $(info Using PNACL_TOOLCHAIN_ROOT = $(PNACL_TOOLCHAIN_ROOT))
+$(info Using PNACL_BIN_PATH = $(PNACL_BIN_PATH))
 $(info Using CLANG_PATH = $(CLANG_PATH))
 $(info Using LIBCXX_INSTALL_PATH = $(LIBCXX_INSTALL_PATH))
-$(info Using BINUTILS_BIN_PATH = $(BINUTILS_BIN_PATH))
 $(info Using HOST_ARCH     = $(HOST_ARCH))
 $(info -----------------------------------------------)
 
 LLVM_CXXFLAGS := `$(LLVM_BIN_PATH)/llvm-config --cxxflags`
-LLVM_LDFLAGS := `$(LLVM_BIN_PATH)/llvm-config --libs` \
+SB_LLVM_CXXFLAGS := $(LLVM_CXXFLAGS)
+
+# Listing specific libraries that are needed for pnacl-sz
+# and the unittests, since we build "tools-only" for the
+# sandboxed_translators (which doesn't include every library
+# listed by llvm-config).
+LLVM_LIBS := -lLLVMIRReader -lLLVMBitReader -lLLVMNaClBitTestUtils \
+	-lLLVMNaClBitReader -lLLVMNaClBitAnalysis -lLLVMNaClBitWriter \
+	-lLLVMAsmParser -lLLVMNaClAnalysis -lLLVMCore -lLLVMSupport
+LLVM_LDFLAGS := $(LLVM_LIBS) \
                 `$(LLVM_BIN_PATH)/llvm-config --ldflags` \
                 `$(LLVM_BIN_PATH)/llvm-config --system-libs`
+SB_LLVM_LDFLAGS := $(LLVM_LIBS) \
+                   -L$(SB_LLVM_PATH)/lib
 
-# It's recommended that CXX matches the compiler you used to build LLVM itself.
 CCACHE := `command -v ccache`
 CXX := CCACHE_CPP2=yes $(CCACHE) $(CLANG_PATH)/clang++
+SB_CXX := CCACHE_CPP2=yes $(CCACHE) $(PNACL_BIN_PATH)/pnacl-clang++
+SB_TRANSLATE := $(PNACL_BIN_PATH)/pnacl-translate
 
-CXXFLAGS := $(LLVM_CXXFLAGS) -std=gnu++11 -Wall -Wextra -Werror -fno-rtti \
-	-fno-exceptions $(OPTLEVEL) $(ASSERTIONS) $(CXX_DEFINES) -g \
-	$(HOST_FLAGS) -pedantic -Wno-error=unused-parameter \
-	-I$(LIBCXX_INSTALL_PATH)/include/c++/v1 $(CXX_EXTRA)
+BASE_CXXFLAGS := -std=gnu++11 -Wall -Wextra -Werror -fno-rtti \
+	-fno-exceptions $(OPTLEVEL) $(ASSERTIONS) -g -pedantic \
+	-Wno-error=unused-parameter $(CXX_EXTRA)
+
+CXXFLAGS := $(LLVM_CXXFLAGS) $(BASE_CXXFLAGS) $(CXX_DEFINES) $(HOST_FLAGS) \
+	-I$(LIBCXX_INSTALL_PATH)/include/c++/v1
+SB_CXXFLAGS := $(SB_LLVM_CXXFLAGS) $(BASE_CXXFLAGS) $(SB_CXX_DEFINES)
+
 LDFLAGS := $(HOST_FLAGS) -L$(LIBCXX_INSTALL_PATH)/lib -Wl,--gc-sections \
 	$(LD_EXTRA)
+# Not specifying -Wl,--gc-sections but instead doing bitcode linking GC w/ LTO.
+SB_LDFLAGS := $(LINKOPTLEVEL) $(LD_EXTRA)
 
 SRCS = \
 	assembler.cpp \
 	assembler_ia32.cpp \
+	IceBrowserCompileServer.cpp \
 	IceCfg.cpp \
 	IceCfgNode.cpp \
+	IceClFlags.cpp \
+	IceCompiler.cpp \
+	IceCompileServer.cpp \
 	IceELFObjectWriter.cpp \
 	IceELFSection.cpp \
 	IceFixups.cpp \
@@ -145,6 +185,7 @@
 endif
 
 OBJS=$(patsubst %.cpp, $(OBJDIR)/%.o, $(SRCS))
+SB_OBJS=$(patsubst %.cpp, $(SB_OBJDIR)/%.o, $(SRCS))
 
 UNITTEST_SRCS = \
 	BitcodeMunge.cpp \
@@ -157,6 +198,13 @@
 # Keep all the first target so it's the default.
 all: $(OBJDIR)/pnacl-sz make_symlink runtime
 
+ifdef TSAN
+sb:
+	@echo "Skipping pnacl-sz.*.nexe: TSAN isn't supported under NaCl."
+else
+sb: $(SB_OBJDIR)/pnacl-sz.x86-32.nexe
+endif
+
 # Creates symbolic link so that testing is easier. Also runs
 # pnacl-sz to verify that the defines flags have valid values,
 # as well as describe the corresponding build attributes.
@@ -166,16 +214,26 @@
 	@echo "Build Attributes:"
 	@$(OBJDIR)/pnacl-sz --build-atts
 
-.PHONY: all make_symlink runtime bloat
+.PHONY: all make_symlink runtime bloat sb
 
 $(OBJDIR)/pnacl-sz: $(OBJS)
 	$(CXX) $(LDFLAGS) -o $@ $^ $(LLVM_LDFLAGS) \
                -Wl,-rpath=$(abspath $(LIBCXX_INSTALL_PATH)/lib)
 
+$(SB_OBJDIR)/pnacl-sz.x86-32.nexe: $(SB_OBJS)
+	$(eval PNACL_SZ_BASE := $(patsubst %.nexe, %, $@))
+	$(SB_CXX) $(SB_LDFLAGS) -o $(PNACL_SZ_BASE).nonfinal.pexe $^ \
+		$(SB_LLVM_LDFLAGS)
+	$(SB_TRANSLATE) -arch x86-32 $(PNACL_SZ_BASE).nonfinal.pexe -o $@ \
+		--allow-llvm-bitcode-input
+
 # TODO(stichnot): Be more precise than "*.h" here and elsewhere.
 $(OBJS): $(OBJDIR)/%.o: src/%.cpp src/*.h src/*.def
 	$(CXX) -c $(CXXFLAGS) $< -o $@
 
+$(SB_OBJS): $(SB_OBJDIR)/%.o: src/%.cpp src/*.h src/*.def
+	$(SB_CXX) -c $(SB_CXXFLAGS) $< -o $@
+
 $(OBJDIR)/run_unittests: $(UNITTEST_OBJS) $(UNITTEST_LIB_OBJS)
 	$(CXX) $(LDFLAGS) -o $@ $^ $(LLVM_LDFLAGS) -lgtest -lgtest_main -ldl \
                -Wl,-rpath=$(abspath $(LIBCXX_INSTALL_PATH)/lib)
@@ -189,11 +247,14 @@
 		$< -o $@
 
 $(OBJS): | $(OBJDIR)
+$(SB_OBJS): | $(SB_OBJDIR)
 
 $(UNITTEST_OBJS): | $(OBJDIR)/unittest
 
 $(OBJDIR):
 	@mkdir -p $@
+$(SB_OBJDIR):
+	@mkdir -p $@
 
 $(OBJDIR)/unittest: $(OBJDIR)
 	@mkdir -p $@
@@ -213,7 +274,7 @@
 
 check-lit: $(OBJDIR)/pnacl-sz make_symlink
 	LLVM_BIN_PATH=$(LLVM_BIN_PATH) \
-	BINUTILS_BIN_PATH=$(BINUTILS_BIN_PATH) \
+	BINUTILS_BIN_PATH=$(PNACL_BIN_PATH) \
 	$(LLVM_SRC_PATH)/utils/lit/lit.py -sv tests_lit
 
 ifdef MINIMAL
@@ -254,7 +315,7 @@
 	@echo See Subzero size breakdown in bloat/pnacl-sz.bloat.html
 
 clean:
-	rm -rf pnacl-sz *.o $(OBJDIR) build/pnacl-sz.bloat.json
+	rm -rf pnacl-sz *.o $(OBJDIR) $(SB_OBJDIR) build/pnacl-sz.bloat.json
 
 clean-all: clean
 	rm -rf build/
diff --git a/src/IceBrowserCompileServer.cpp b/src/IceBrowserCompileServer.cpp
new file mode 100644
index 0000000..6a9ee65
--- /dev/null
+++ b/src/IceBrowserCompileServer.cpp
@@ -0,0 +1,150 @@
+//===- subzero/src/IceBrowserCompileServer.cpp - Browser compile server ---===//
+//
+//                        The Subzero Code Generator
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the browser-based compile server.
+//
+//===----------------------------------------------------------------------===//
+
+// Can only compile this with the NaCl compiler (needs irt.h, and the
+// unsandboxed LLVM build using the trusted compiler does not have irt.h).
+#if PNACL_BROWSER_TRANSLATOR
+
+#include <cstring>
+#include <irt.h>
+#include <irt_dev.h>
+#include <thread>
+
+#include "llvm/Support/QueueStreamer.h"
+
+#include "IceBrowserCompileServer.h"
+
+namespace Ice {
+
+// Create C wrappers around callback handlers for the IRT interface.
+namespace {
+
+BrowserCompileServer *gCompileServer;
+struct nacl_irt_private_pnacl_translator_compile gIRTFuncs;
+
+void getIRTInterfaces() {
+  size_t QueryResult =
+      nacl_interface_query(NACL_IRT_PRIVATE_PNACL_TRANSLATOR_COMPILE_v0_1,
+                           &gIRTFuncs, sizeof(gIRTFuncs));
+  if (QueryResult != sizeof(gIRTFuncs))
+    llvm::report_fatal_error("Failed to get translator compile IRT interface");
+}
+
+char *onInitCallback(uint32_t NumThreads, int *ObjFileFDs,
+                     size_t ObjFileFDCount, char **argv, size_t argc) {
+  if (ObjFileFDCount < 1) {
+    std::string Buffer;
+    llvm::raw_string_ostream StrBuf(Buffer);
+    StrBuf << "Invalid number of FDs for onInitCallback " << ObjFileFDCount
+           << "\n";
+    return strdup(StrBuf.str().c_str());
+  }
+  int ObjFileFD = ObjFileFDs[0];
+  if (ObjFileFD < 0) {
+    std::string Buffer;
+    llvm::raw_string_ostream StrBuf(Buffer);
+    StrBuf << "Invalid FD given for onInitCallback " << ObjFileFD << "\n";
+    return strdup(StrBuf.str().c_str());
+  }
+  // NOTE: argv is owned by the caller, but we parse here before returning.
+  gCompileServer->getParsedFlags(NumThreads, argc, argv);
+  gCompileServer->startCompileThread(ObjFileFD);
+  return nullptr;
+}
+
+int onDataCallback(const void *Data, size_t NumBytes) {
+  return gCompileServer->pushInputBytes(Data, NumBytes) ? 1 : 0;
+}
+
+char *onEndCallback() {
+  gCompileServer->endInputStream();
+  gCompileServer->waitForCompileThread();
+  // TODO(jvoung): Also return an error string, and UMA data.
+  // Set up a report_fatal_error handler to grab that string.
+  if (gCompileServer->getErrorCode().value()) {
+    return strdup("Some error occurred");
+  }
+  return nullptr;
+}
+
+struct nacl_irt_pnacl_compile_funcs SubzeroCallbacks {
+  &onInitCallback, &onDataCallback, &onEndCallback
+};
+
+std::unique_ptr<llvm::raw_fd_ostream> getOutputStream(int FD) {
+  if (FD <= 0)
+    llvm::report_fatal_error("Invalid output FD");
+  const bool CloseOnDtor = true;
+  const bool Unbuffered = false;
+  return std::unique_ptr<llvm::raw_fd_ostream>(
+      new llvm::raw_fd_ostream(FD, CloseOnDtor, Unbuffered));
+}
+
+} // end of anonymous namespace
+
+BrowserCompileServer::~BrowserCompileServer() {}
+
+void BrowserCompileServer::run() {
+  gCompileServer = this;
+  // TODO(jvoung): make a useful fatal error handler that can
+  // exit all *worker* threads, keep the server thread alive,
+  // and be able to handle a server request for the last error string.
+  getIRTInterfaces();
+  gIRTFuncs.serve_translate_request(&SubzeroCallbacks);
+}
+
+void BrowserCompileServer::getParsedFlags(uint32_t NumThreads, int argc,
+                                          char **argv) {
+  ClFlags::parseFlags(argc, argv);
+  ClFlags::getParsedClFlags(Flags);
+  ClFlags::getParsedClFlagsExtra(ExtraFlags);
+  // Set some defaults which aren't specified via the argv string.
+  Flags.setNumTranslationThreads(NumThreads);
+  Flags.setUseSandboxing(true);
+  Flags.setOutFileType(FT_Elf);
+  // TODO(jvoung): allow setting different target arches.
+  Flags.setTargetArch(Target_X8632);
+  ExtraFlags.setBuildOnRead(true);
+  ExtraFlags.setInputFileFormat(llvm::PNaClFormat);
+}
+
+bool BrowserCompileServer::pushInputBytes(const void *Data, size_t NumBytes) {
+  return InputStream->PutBytes(
+             const_cast<unsigned char *>(
+                 reinterpret_cast<const unsigned char *>(Data)),
+             NumBytes) != NumBytes;
+}
+
+void BrowserCompileServer::endInputStream() { InputStream->SetDone(); }
+
+void BrowserCompileServer::startCompileThread(int ObjFD) {
+  InputStream = new llvm::QueueStreamer();
+  LogStream = getOutputStream(STDOUT_FILENO);
+  LogStream->SetUnbuffered();
+  EmitStream = getOutputStream(ObjFD);
+  EmitStream->SetBufferSize(1 << 14);
+  ELFStream.reset(new ELFStreamer(*EmitStream.get()));
+  Ctx.reset(new GlobalContext(LogStream.get(), EmitStream.get(),
+                              ELFStream.get(), Flags));
+  CompileThread = std::thread([this]() {
+    Ctx->initParserThread();
+    this->getCompiler().run(ExtraFlags, *Ctx.get(),
+                            // Retain original reference, but the compiler
+                            // (LLVM's MemoryObject) wants to handle deletion.
+                            std::unique_ptr<llvm::DataStreamer>(InputStream));
+  });
+}
+
+} // end of namespace Ice
+
+#endif // PNACL_BROWSER_TRANSLATOR
diff --git a/src/IceBrowserCompileServer.h b/src/IceBrowserCompileServer.h
new file mode 100644
index 0000000..034a1af
--- /dev/null
+++ b/src/IceBrowserCompileServer.h
@@ -0,0 +1,95 @@
+//===- subzero/src/IceBrowserCompileServer.h - Browser server ---*- 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 browser-specific compile server.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SUBZERO_SRC_ICEBROWSERCOMPILESERVER_H
+#define SUBZERO_SRC_ICEBROWSERCOMPILESERVER_H
+
+#include <thread>
+
+#include "IceClFlags.h"
+#include "IceClFlagsExtra.h"
+#include "IceCompileServer.h"
+#include "IceDefs.h"
+#include "IceELFStreamer.h"
+
+namespace llvm {
+class QueueStreamer;
+class raw_fd_ostream;
+};
+
+namespace Ice {
+
+// The browser variant of the compile server.
+// Compared to the commandline version, this version gets compile
+// requests over IPC. Each compile request will have a slimmed down
+// version of argc, argv while other flags are set to defaults that
+// make sense in the browser case. The output file is specified via
+// a posix FD, and input bytes are pushed to the server.
+class BrowserCompileServer : public CompileServer {
+  BrowserCompileServer() = delete;
+  BrowserCompileServer(const BrowserCompileServer &) = delete;
+  BrowserCompileServer &operator=(const BrowserCompileServer &) = delete;
+
+public:
+  explicit BrowserCompileServer(Compiler &Comp)
+      : CompileServer(Comp), InputStream(nullptr) {}
+
+  ~BrowserCompileServer() final;
+
+  void run() final;
+
+  // Parse and set up the flags for compile jobs.
+  void getParsedFlags(uint32_t NumThreads, int argc, char **argv);
+
+  // Creates the streams + context and starts the compile thread,
+  // handing off the streams + context.
+  void startCompileThread(int OutFD);
+
+  // Call to push more bytes to the current input stream.
+  // Returns false on success and true on error.
+  bool pushInputBytes(const void *Data, size_t NumBytes);
+
+  // Notify the input stream of EOF.
+  void endInputStream();
+
+  // Wait for the compile thread to complete then reset the state.
+  void waitForCompileThread() {
+    CompileThread.join();
+    LastError.assign(Ctx->getErrorStatus()->value());
+    // Reset some state. The InputStream is deleted by the compiler
+    // so only reset this to nullptr. Free and flush the rest
+    // of the streams.
+    InputStream = nullptr;
+    EmitStream.reset(nullptr);
+    ELFStream.reset(nullptr);
+  }
+
+private:
+  // This currently only handles a single compile request, hence one copy
+  // of the state.
+  std::unique_ptr<GlobalContext> Ctx;
+  // A borrowed reference to the current InputStream. The compiler owns
+  // the actual reference so the server must be careful not to access
+  // after the compiler is done.
+  llvm::QueueStreamer *InputStream;
+  std::unique_ptr<Ostream> LogStream;
+  std::unique_ptr<llvm::raw_fd_ostream> EmitStream;
+  std::unique_ptr<ELFStreamer> ELFStream;
+  ClFlags Flags;
+  ClFlagsExtra ExtraFlags;
+  std::thread CompileThread;
+};
+
+} // end of namespace Ice
+
+#endif // SUBZERO_SRC_ICEBROWSERCOMPILESERVER_H
diff --git a/src/IceClFlags.cpp b/src/IceClFlags.cpp
new file mode 100644
index 0000000..9e24665
--- /dev/null
+++ b/src/IceClFlags.cpp
@@ -0,0 +1,304 @@
+//===- subzero/src/IceClFlags.cpp - Command line flags and parsing --------===//
+//
+//                        The Subzero Code Generator
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines commandline flags parsing.
+// This currently relies on llvm::cl to parse.  In the future, the minimal
+// build can have a simpler parser.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Support/CommandLine.h"
+
+#include "IceClFlags.h"
+#include "IceClFlagsExtra.h"
+
+namespace cl = llvm::cl;
+
+// Options which are captured in Ice::ClFlags and propagated.
+
+namespace {
+
+cl::opt<bool> AllowErrorRecovery(
+    "allow-pnacl-reader-error-recovery",
+    cl::desc("Allow error recovery when reading PNaCl bitcode."),
+    cl::init(false));
+
+// This is currently needed by crosstest.py.
+cl::opt<bool> AllowUninitializedGlobals(
+    "allow-uninitialized-globals",
+    cl::desc("Allow global variables to be uninitialized"));
+
+cl::opt<bool>
+    DataSections("fdata-sections",
+                 cl::desc("Emit (global) data into separate sections"));
+
+cl::opt<bool> DecorateAsm(
+    "asm-verbose",
+    cl::desc("Decorate textual asm output with register liveness info"));
+
+cl::opt<std::string>
+    DefaultFunctionPrefix("default-function-prefix",
+                          cl::desc("Define default function prefix for naming "
+                                   "unnamed functions"),
+                          cl::init("Function"));
+
+cl::opt<std::string>
+    DefaultGlobalPrefix("default-global-prefix",
+                        cl::desc("Define default global prefix for naming "
+                                 "unnamed globals"),
+                        cl::init("Global"));
+cl::opt<bool> DisableInternal("externalize",
+                              cl::desc("Externalize all symbols"));
+// Note: Modifiable only if ALLOW_DISABLE_IR_GEN.
+cl::opt<bool> DisableIRGeneration("no-ir-gen",
+                                  cl::desc("Disable generating Subzero IR."));
+cl::opt<bool> DisableTranslation("notranslate",
+                                 cl::desc("Disable Subzero translation"));
+
+cl::opt<bool>
+    DumpStats("szstats",
+              cl::desc("Print statistics after translating each function"));
+
+cl::opt<bool>
+    FunctionSections("ffunction-sections",
+                     cl::desc("Emit functions into separate sections"));
+
+// Number of translation threads (in addition to the parser thread and
+// the emitter thread).  The special case of 0 means purely
+// sequential, i.e. parser, translator, and emitter all within the
+// same single thread.  (This may need a slight rework if we expand to
+// multiple parser or emitter threads.)
+cl::opt<uint32_t> NumThreads(
+    "threads",
+    cl::desc("Number of translation threads (0 for purely sequential)"),
+    // TODO(stichnot): Settle on a good default.  Consider
+    // something related to std::thread::hardware_concurrency().
+    cl::init(2));
+
+cl::opt<Ice::OptLevel> OLevel(cl::desc("Optimization level"),
+                              cl::init(Ice::Opt_m1), cl::value_desc("level"),
+                              cl::values(clEnumValN(Ice::Opt_m1, "Om1", "-1"),
+                                         clEnumValN(Ice::Opt_m1, "O-1", "-1"),
+                                         clEnumValN(Ice::Opt_0, "O0", "0"),
+                                         clEnumValN(Ice::Opt_1, "O1", "1"),
+                                         clEnumValN(Ice::Opt_2, "O2", "2"),
+                                         clEnumValEnd));
+
+cl::opt<bool>
+    EnablePhiEdgeSplit("phi-edge-split",
+                       cl::desc("Enable edge splitting for Phi lowering"),
+                       cl::init(true));
+
+// TODO(stichnot): See if we can easily use LLVM's -rng-seed option
+// and implementation.  I expect the implementation is different and
+// therefore the tests would need to be changed.
+cl::opt<unsigned long long>
+    RandomSeed("sz-seed", cl::desc("Seed the random number generator"),
+               cl::init(time(0)));
+
+cl::opt<bool> ShouldDoNopInsertion("nop-insertion",
+                                   cl::desc("Randomly insert NOPs"),
+                                   cl::init(false));
+
+cl::opt<bool>
+    RandomizeRegisterAllocation("randomize-regalloc",
+                                cl::desc("Randomize register allocation"),
+                                cl::init(false));
+
+cl::opt<bool> SubzeroTimingEnabled(
+    "timing", cl::desc("Enable breakdown timing of Subzero translation"));
+
+cl::opt<Ice::TargetArch> TargetArch(
+    "target", cl::desc("Target architecture:"), cl::init(Ice::Target_X8632),
+    cl::values(
+        clEnumValN(Ice::Target_X8632, "x8632", "x86-32"),
+        clEnumValN(Ice::Target_X8632, "x86-32", "x86-32 (same as x8632)"),
+        clEnumValN(Ice::Target_X8632, "x86_32", "x86-32 (same as x8632)"),
+        clEnumValN(Ice::Target_X8664, "x8664", "x86-64"),
+        clEnumValN(Ice::Target_X8664, "x86-64", "x86-64 (same as x8664)"),
+        clEnumValN(Ice::Target_X8664, "x86_64", "x86-64 (same as x8664)"),
+        clEnumValN(Ice::Target_ARM32, "arm", "arm32"),
+        clEnumValN(Ice::Target_ARM32, "arm32", "arm32 (same as arm)"),
+        clEnumValN(Ice::Target_ARM64, "arm64", "arm64"), clEnumValEnd));
+cl::opt<Ice::TargetInstructionSet> TargetInstructionSet(
+    "mattr", cl::desc("Target architecture attributes"),
+    cl::init(Ice::X86InstructionSet_SSE2),
+    cl::values(clEnumValN(Ice::X86InstructionSet_SSE2, "sse2",
+                          "Enable SSE2 instructions (default)"),
+               clEnumValN(Ice::X86InstructionSet_SSE4_1, "sse4.1",
+                          "Enable SSE 4.1 instructions"),
+               clEnumValEnd));
+cl::opt<std::string>
+    TestPrefix("prefix",
+               cl::desc("Prepend a prefix to symbol names for testing"),
+               cl::init(""), cl::value_desc("prefix"));
+
+cl::opt<bool> TimeEachFunction(
+    "timing-funcs", cl::desc("Print total translation time for each function"));
+
+cl::opt<std::string> TimingFocusOn(
+    "timing-focus",
+    cl::desc("Break down timing for a specific function (use '*' for all)"),
+    cl::init(""));
+
+cl::opt<std::string>
+    TranslateOnly("translate-only",
+                  cl::desc("Translate only the given function"), cl::init(""));
+
+cl::opt<bool> UseSandboxing("sandbox", cl::desc("Use sandboxing"));
+
+cl::opt<std::string> VerboseFocusOn(
+    "verbose-focus",
+    cl::desc("Temporarily enable full verbosity for a specific function"),
+    cl::init(""));
+
+cl::opt<Ice::FileType> OutFileType(
+    "filetype", cl::desc("Output file type"), cl::init(Ice::FT_Iasm),
+    cl::values(clEnumValN(Ice::FT_Elf, "obj", "Native ELF object ('.o') file"),
+               clEnumValN(Ice::FT_Asm, "asm", "Assembly ('.s') file"),
+               clEnumValN(Ice::FT_Iasm, "iasm",
+                          "Low-level integrated assembly ('.s') file"),
+               clEnumValEnd));
+
+cl::opt<int> MaxNopsPerInstruction(
+    "max-nops-per-instruction",
+    cl::desc("Max number of nops to insert per instruction"), cl::init(1));
+
+cl::opt<int> NopProbabilityAsPercentage(
+    "nop-insertion-percentage",
+    cl::desc("Nop insertion probability as percentage"), cl::init(10));
+
+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_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_AddrOpt, "addropt", "Address mode optimization"),
+        clEnumValN(Ice::IceV_Random, "random", "Randomization details"),
+        clEnumValN(Ice::IceV_All, "all", "Use all verbose options"),
+        clEnumValN(Ice::IceV_Most, "most",
+                   "Use all verbose options except 'regalloc'"),
+        clEnumValN(Ice::IceV_None, "none", "No verbosity"), clEnumValEnd));
+
+// Options not captured in Ice::ClFlags and propagated.
+
+cl::opt<bool> AlwaysExitSuccess(
+    "exit-success", cl::desc("Exit with success status, even if errors found"),
+    cl::init(false));
+
+// Note: While this flag isn't used in the minimal build, we keep this
+// flag so that tests can set this command-line flag without concern
+// to the type of build. We double check that this flag at runtime
+// to make sure the consistency is maintained.
+cl::opt<bool>
+    BuildOnRead("build-on-read",
+                cl::desc("Build ICE instructions when reading bitcode"),
+                cl::init(true));
+
+cl::opt<llvm::NaClFileFormat> InputFileFormat(
+    "bitcode-format", cl::desc("Define format of input file:"),
+    cl::values(clEnumValN(llvm::LLVMFormat, "llvm", "LLVM file (default)"),
+               clEnumValN(llvm::PNaClFormat, "pnacl", "PNaCl bitcode file"),
+               clEnumValEnd),
+    cl::init(llvm::LLVMFormat));
+
+cl::opt<bool> GenerateBuildAtts(
+    "build-atts", cl::desc("Generate list of build attributes associated with "
+                           "this executable."),
+    cl::init(false));
+
+cl::opt<std::string> IRFilename(cl::Positional, cl::desc("<IR file>"),
+                                cl::init("-"));
+cl::opt<std::string> LogFilename("log", cl::desc("Set log filename"),
+                                 cl::init("-"), cl::value_desc("filename"));
+cl::opt<bool> LLVMVerboseErrors(
+    "verbose-llvm-parse-errors",
+    cl::desc("Print out more descriptive PNaCl bitcode parse errors when "
+             "building LLVM IR first"),
+    cl::init(false));
+cl::opt<std::string> OutputFilename("o", cl::desc("Override output filename"),
+                                    cl::init("-"), cl::value_desc("filename"));
+
+Ice::IceString AppName;
+
+} // end of anonymous namespace
+
+namespace Ice {
+
+void ClFlags::parseFlags(int argc, char **argv) {
+  cl::ParseCommandLineOptions(argc, argv);
+  AppName = IceString(argv[0]);
+}
+
+void ClFlags::getParsedClFlags(ClFlags &OutFlags) {
+  if (::DisableIRGeneration)
+    ::DisableTranslation = true;
+
+  Ice::VerboseMask VMask = Ice::IceV_None;
+  // Don't generate verbose messages if routines
+  // to dump messages are not available.
+  if (ALLOW_DUMP) {
+    for (unsigned i = 0; i != VerboseList.size(); ++i)
+      VMask |= VerboseList[i];
+  }
+
+  OutFlags.setAllowErrorRecovery(::AllowErrorRecovery);
+  OutFlags.setAllowUninitializedGlobals(::AllowUninitializedGlobals);
+  OutFlags.setDataSections(::DataSections);
+  OutFlags.setDecorateAsm(::DecorateAsm);
+  OutFlags.setDefaultFunctionPrefix(::DefaultFunctionPrefix);
+  OutFlags.setDefaultGlobalPrefix(::DefaultGlobalPrefix);
+  OutFlags.setDisableInternal(::DisableInternal);
+  OutFlags.setDisableIRGeneration(::DisableIRGeneration);
+  OutFlags.setDisableTranslation(::DisableTranslation);
+  OutFlags.setDumpStats(::DumpStats);
+  OutFlags.setFunctionSections(::FunctionSections);
+  OutFlags.setNumTranslationThreads(::NumThreads);
+  OutFlags.setOptLevel(::OLevel);
+  OutFlags.setPhiEdgeSplit(::EnablePhiEdgeSplit);
+  OutFlags.setRandomSeed(::RandomSeed);
+  OutFlags.setShouldDoNopInsertion(::ShouldDoNopInsertion);
+  OutFlags.setShouldRandomizeRegAlloc(::RandomizeRegisterAllocation);
+  OutFlags.setSubzeroTimingEnabled(::SubzeroTimingEnabled);
+  OutFlags.setTargetArch(::TargetArch);
+  OutFlags.setTargetInstructionSet(::TargetInstructionSet);
+  OutFlags.setTestPrefix(::TestPrefix);
+  OutFlags.setTimeEachFunction(::TimeEachFunction);
+  OutFlags.setTimingFocusOn(::TimingFocusOn);
+  OutFlags.setTranslateOnly(::TranslateOnly);
+  OutFlags.setUseSandboxing(::UseSandboxing);
+  OutFlags.setVerboseFocusOn(::VerboseFocusOn);
+  OutFlags.setOutFileType(::OutFileType);
+  OutFlags.setMaxNopsPerInstruction(::MaxNopsPerInstruction);
+  OutFlags.setNopProbabilityAsPercentage(::NopProbabilityAsPercentage);
+  OutFlags.setVerbose(VMask);
+}
+
+void ClFlags::getParsedClFlagsExtra(ClFlagsExtra &OutFlagsExtra) {
+  OutFlagsExtra.setAlwaysExitSuccess(AlwaysExitSuccess);
+  OutFlagsExtra.setBuildOnRead(BuildOnRead);
+  OutFlagsExtra.setGenerateBuildAtts(GenerateBuildAtts);
+  OutFlagsExtra.setLLVMVerboseErrors(LLVMVerboseErrors);
+  OutFlagsExtra.setAppName(AppName);
+  OutFlagsExtra.setInputFileFormat(InputFileFormat);
+  OutFlagsExtra.setIRFilename(IRFilename);
+  OutFlagsExtra.setLogFilename(LogFilename);
+  OutFlagsExtra.setOutputFilename(OutputFilename);
+}
+
+} // end of namespace Ice
diff --git a/src/IceClFlags.h b/src/IceClFlags.h
index 1133f8c..b581a24 100644
--- a/src/IceClFlags.h
+++ b/src/IceClFlags.h
@@ -14,10 +14,13 @@
 #ifndef SUBZERO_SRC_ICECLFLAGS_H
 #define SUBZERO_SRC_ICECLFLAGS_H
 
+#include "IceDefs.h"
 #include "IceTypes.h"
 
 namespace Ice {
 
+class ClFlagsExtra;
+
 class ClFlags {
   ClFlags(const ClFlags &) = delete;
   ClFlags &operator=(const ClFlags &) = delete;
@@ -33,7 +36,7 @@
         RandomNopInsertion(false), RandomRegAlloc(false),
         SubzeroTimingEnabled(false), TimeEachFunction(false),
         UseSandboxing(false),
-        // Enum and integer fields
+        // Enum and integer fields.
         Opt(Opt_m1), OutFileType(FT_Iasm), RandomMaxNopsPerInstruction(0),
         RandomNopProbabilityAsPercentage(0), TArch(TargetArch_NUM),
         VMask(IceV_None),
@@ -43,6 +46,10 @@
         // size_t and 64-bit fields.
         NumTranslationThreads(0), RandomSeed(0) {}
 
+  static void parseFlags(int argc, char *argv[]);
+  static void getParsedClFlags(ClFlags &OutFlags);
+  static void getParsedClFlagsExtra(ClFlagsExtra &OutFlagsExtra);
+
   // bool accessors.
 
   bool getAllowErrorRecovery() const { return AllowErrorRecovery; }
diff --git a/src/IceClFlagsExtra.h b/src/IceClFlagsExtra.h
new file mode 100644
index 0000000..9346d6e
--- /dev/null
+++ b/src/IceClFlagsExtra.h
@@ -0,0 +1,79 @@
+//===- subzero/src/IceClFlagsExtra.h - Extra Cl Flags -----------*- 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 command line flags primarily used for non-minimal builds.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SUBZERO_SRC_ICECLFLAGSEXTRA_H
+#define SUBZERO_SRC_ICECLFLAGSEXTRA_H
+
+#include "llvm/IRReader/IRReader.h"
+
+#include "IceDefs.h"
+
+namespace Ice {
+
+class ClFlagsExtra {
+  ClFlagsExtra(const ClFlagsExtra &) = delete;
+  ClFlagsExtra &operator=(const ClFlagsExtra &) = delete;
+
+public:
+  ClFlagsExtra()
+      : AlwaysExitSuccess(false), BuildOnRead(false), GenerateBuildAtts(false),
+        LLVMVerboseErrors(false), InputFileFormat(llvm::LLVMFormat) {}
+
+  bool getAlwaysExitSuccess() const { return AlwaysExitSuccess; }
+  void setAlwaysExitSuccess(bool NewValue) { AlwaysExitSuccess = NewValue; }
+
+  bool getBuildOnRead() const { return BuildOnRead; }
+  void setBuildOnRead(bool NewValue) { BuildOnRead = NewValue; }
+
+  bool getGenerateBuildAtts() const { return GenerateBuildAtts; }
+  void setGenerateBuildAtts(bool NewValue) { GenerateBuildAtts = NewValue; }
+
+  bool getLLVMVerboseErrors() const { return LLVMVerboseErrors; }
+  void setLLVMVerboseErrors(bool NewValue) { LLVMVerboseErrors = NewValue; }
+
+  llvm::NaClFileFormat getInputFileFormat() const { return InputFileFormat; }
+  void setInputFileFormat(llvm::NaClFileFormat NewValue) {
+    InputFileFormat = NewValue;
+  }
+
+  const IceString &getAppName() const { return AppName; }
+  void setAppName(const IceString &NewValue) { AppName = NewValue; }
+
+  const IceString &getIRFilename() const { return IRFilename; }
+  void setIRFilename(const IceString &NewValue) { IRFilename = NewValue; }
+
+  const IceString &getLogFilename() const { return LogFilename; }
+  void setLogFilename(const IceString &NewValue) { LogFilename = NewValue; }
+
+  const IceString &getOutputFilename() const { return OutputFilename; }
+  void setOutputFilename(const IceString &NewValue) {
+    OutputFilename = NewValue;
+  }
+
+private:
+  bool AlwaysExitSuccess;
+  bool BuildOnRead;
+  bool GenerateBuildAtts;
+  bool LLVMVerboseErrors;
+
+  llvm::NaClFileFormat InputFileFormat;
+
+  IceString AppName;
+  IceString IRFilename;
+  IceString LogFilename;
+  IceString OutputFilename;
+};
+
+} // end of namespace Ice
+
+#endif // SUBZERO_SRC_ICECLFLAGSEXTRA_H
diff --git a/src/IceCompileServer.cpp b/src/IceCompileServer.cpp
new file mode 100644
index 0000000..86ebb0b
--- /dev/null
+++ b/src/IceCompileServer.cpp
@@ -0,0 +1,113 @@
+//===- subzero/src/IceCompileServer.cpp - Compile server ------------------===//
+//
+//                        The Subzero Code Generator
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the basic commandline-based compile server.
+//
+//===----------------------------------------------------------------------===//
+
+#include <fstream>
+#include <iostream>
+#include <thread>
+
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/raw_os_ostream.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/StreamingMemoryObject.h"
+
+#include "IceClFlags.h"
+#include "IceClFlagsExtra.h"
+#include "IceCompileServer.h"
+#include "IceELFStreamer.h"
+#include "IceGlobalContext.h"
+
+namespace Ice {
+
+namespace {
+
+std::unique_ptr<Ostream> getStream(const IceString &Filename) {
+  std::ofstream Ofs;
+  if (Filename != "-") {
+    Ofs.open(Filename.c_str(), std::ofstream::out);
+    return std::unique_ptr<Ostream>(new llvm::raw_os_ostream(Ofs));
+  } else {
+    return std::unique_ptr<Ostream>(new llvm::raw_os_ostream(std::cout));
+  }
+}
+
+ErrorCodes getReturnValue(const Ice::ClFlagsExtra &Flags, ErrorCodes Val) {
+  if (Flags.getAlwaysExitSuccess())
+    return EC_None;
+  return Val;
+}
+
+} // end of anonymous namespace
+
+void CLCompileServer::run() {
+  ClFlags::parseFlags(argc, argv);
+  ClFlags Flags;
+  ClFlagsExtra ExtraFlags;
+  ClFlags::getParsedClFlags(Flags);
+  ClFlags::getParsedClFlagsExtra(ExtraFlags);
+
+  std::unique_ptr<Ostream> Ls = getStream(ExtraFlags.getLogFilename());
+  Ls->SetUnbuffered();
+  std::unique_ptr<Ostream> Os;
+  std::unique_ptr<ELFStreamer> ELFStr;
+  switch (Flags.getOutFileType()) {
+  case FT_Elf: {
+    if (ExtraFlags.getOutputFilename() == "-") {
+      *Ls << "Error: writing binary ELF to stdout is unsupported\n";
+      return transferErrorCode(getReturnValue(ExtraFlags, Ice::EC_Args));
+    }
+    std::error_code EC;
+    std::unique_ptr<llvm::raw_fd_ostream> FdOs(new llvm::raw_fd_ostream(
+        ExtraFlags.getOutputFilename(), EC, llvm::sys::fs::F_None));
+    if (EC) {
+      *Ls << "Failed to open output file: " << ExtraFlags.getOutputFilename()
+          << ":\n" << EC.message() << "\n";
+      return transferErrorCode(getReturnValue(ExtraFlags, Ice::EC_Args));
+    }
+    ELFStr.reset(new ELFStreamer(*FdOs.get()));
+    Os.reset(FdOs.release());
+    // NaCl sets st_blksize to 0, and LLVM uses that to pick the
+    // default preferred buffer size. Set to something non-zero.
+    Os->SetBufferSize(1 << 14);
+  } break;
+  case FT_Asm:
+  case FT_Iasm: {
+    Os = getStream(ExtraFlags.getOutputFilename());
+    Os->SetUnbuffered();
+  } break;
+  }
+
+  IceString StrError;
+  std::unique_ptr<llvm::DataStreamer> InputStream(
+      llvm::getDataFileStreamer(ExtraFlags.getIRFilename(), &StrError));
+  if (!StrError.empty() || !InputStream) {
+    llvm::SMDiagnostic Err(ExtraFlags.getIRFilename(),
+                           llvm::SourceMgr::DK_Error, StrError);
+    Err.print(ExtraFlags.getAppName().c_str(), *Ls);
+    return transferErrorCode(getReturnValue(ExtraFlags, Ice::EC_Bitcode));
+  }
+
+  Ctx.reset(new GlobalContext(Ls.get(), Os.get(), ELFStr.get(), Flags));
+  if (Ctx->getFlags().getNumTranslationThreads() != 0) {
+    std::thread CompileThread([this, &ExtraFlags, &InputStream]() {
+      Ctx->initParserThread();
+      getCompiler().run(ExtraFlags, *Ctx.get(), std::move(InputStream));
+    });
+    CompileThread.join();
+  } else {
+    getCompiler().run(ExtraFlags, *Ctx.get(), std::move(InputStream));
+  }
+  transferErrorCode(getReturnValue(
+      ExtraFlags, static_cast<ErrorCodes>(Ctx->getErrorStatus()->value())));
+}
+
+} // end of namespace Ice
diff --git a/src/IceCompileServer.h b/src/IceCompileServer.h
new file mode 100644
index 0000000..a0a0e7a
--- /dev/null
+++ b/src/IceCompileServer.h
@@ -0,0 +1,84 @@
+//===- subzero/src/IceCompileServer.h - Compile server ----------*- 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 compile server. Given a compiler implementation,
+// it dispatches compile requests to the implementation.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SUBZERO_SRC_ICECOMPILESERVER_H
+#define SUBZERO_SRC_ICECOMPILESERVER_H
+
+#include "IceCompiler.h"
+#include "IceDefs.h"
+#include "IceGlobalContext.h"
+
+namespace llvm {
+class DataStreamer;
+class raw_fd_ostream;
+};
+
+namespace Ice {
+
+// A CompileServer awaits compile requests, and dispatches the requests
+// to a given Compiler. Each request is paired with an input stream,
+// a context (which has the output stream), and a set of arguments.
+// The CompileServer takes over the current thread to listen to requests,
+// and compile requests are handled on separate threads.
+//
+// Currently, this only handles a single request.
+//
+// When run on the commandline, it receives and therefore dispatches
+// the request immediately.  When run in the browser, it blocks waiting
+// for a request.
+class CompileServer {
+  CompileServer() = delete;
+  CompileServer(const CompileServer &) = delete;
+  CompileServer &operator=(const CompileServer &) = delete;
+
+public:
+  explicit CompileServer(Compiler &Comp) : Comp(Comp) {}
+
+  virtual ~CompileServer() {}
+
+  virtual void run() = 0;
+
+  ErrorCode &getErrorCode() { return LastError; }
+  void transferErrorCode(ErrorCodes Code) { LastError.assign(Code); }
+
+protected:
+  Compiler &getCompiler() const { return Comp; }
+
+  Compiler &Comp;
+  ErrorCode LastError;
+};
+
+// Commandline variant of the compile server.
+class CLCompileServer : public CompileServer {
+  CLCompileServer() = delete;
+  CLCompileServer(const CLCompileServer &) = delete;
+  CLCompileServer &operator=(const CLCompileServer &) = delete;
+
+public:
+  CLCompileServer(Compiler &Comp, int argc, char **argv)
+      : CompileServer(Comp), argc(argc), argv(argv) {}
+
+  ~CLCompileServer() final {}
+
+  void run() final;
+
+private:
+  int argc;
+  char **argv;
+  std::unique_ptr<GlobalContext> Ctx;
+};
+
+} // end of namespace Ice
+
+#endif // SUBZERO_SRC_ICECOMPILESERVER_H
diff --git a/src/IceCompiler.cpp b/src/IceCompiler.cpp
new file mode 100644
index 0000000..47f1ba4
--- /dev/null
+++ b/src/IceCompiler.cpp
@@ -0,0 +1,164 @@
+//===- subzero/src/IceCompiler.cpp - Driver for bitcode translation -------===//
+//
+//                        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 for translating PNaCl bitcode into native code.
+// It can either directly parse the binary bitcode file, or use LLVM routines to
+// parse a textual bitcode file into LLVM IR and then convert LLVM IR into ICE.
+// In either case, the high-level ICE is then compiled down to native code, as
+// either an ELF object file or a textual asm file.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/IR/LLVMContext.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IRReader/IRReader.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/StreamingMemoryObject.h"
+
+#include "IceCfg.h"
+#include "IceClFlags.h"
+#include "IceClFlagsExtra.h"
+#include "IceCompiler.h"
+#include "IceConverter.h"
+#include "IceELFObjectWriter.h"
+#include "PNaClTranslator.h"
+namespace Ice {
+
+namespace {
+
+struct {
+  const char *FlagName;
+  int FlagValue;
+} ConditionalBuildAttributes[] = {{"dump", ALLOW_DUMP},
+                                  {"disable_ir_gen", ALLOW_DISABLE_IR_GEN},
+                                  {"llvm_cl", ALLOW_LLVM_CL},
+                                  {"llvm_ir", ALLOW_LLVM_IR},
+                                  {"llvm_ir_as_input", ALLOW_LLVM_IR_AS_INPUT},
+                                  {"minimal_build", ALLOW_MINIMAL_BUILD},
+                                  {"browser_mode", PNACL_BROWSER_TRANSLATOR}};
+
+// Validates values of build attributes. Prints them to Stream if
+// Stream is non-null.
+void ValidateAndGenerateBuildAttributes(const ClFlags &Flags, Ostream *Stream) {
+  if (Stream)
+    *Stream << Flags.getTargetArch() << "\n";
+
+  for (size_t i = 0; i < llvm::array_lengthof(ConditionalBuildAttributes);
+       ++i) {
+    switch (ConditionalBuildAttributes[i].FlagValue) {
+    case 0:
+      if (Stream)
+        *Stream << "no_" << ConditionalBuildAttributes[i].FlagName << "\n";
+      break;
+    case 1:
+      if (Stream)
+        *Stream << "allow_" << ConditionalBuildAttributes[i].FlagName << "\n";
+      break;
+    default: {
+      std::string Buffer;
+      llvm::raw_string_ostream StrBuf(Buffer);
+      StrBuf << "Flag " << ConditionalBuildAttributes[i].FlagName
+             << " must be defined as 0/1. Found: "
+             << ConditionalBuildAttributes[i].FlagValue;
+      llvm::report_fatal_error(StrBuf.str());
+    }
+    }
+  }
+}
+
+} // end of anonymous namespace
+
+void Compiler::run(const Ice::ClFlagsExtra &ExtraFlags, GlobalContext &Ctx,
+                   std::unique_ptr<llvm::DataStreamer> &&InputStream) {
+  ValidateAndGenerateBuildAttributes(
+      Ctx.getFlags(),
+      ExtraFlags.getGenerateBuildAtts() ? &Ctx.getStrDump() : nullptr);
+  if (ExtraFlags.getGenerateBuildAtts())
+    return Ctx.getErrorStatus()->assign(EC_None);
+
+  if (!ALLOW_DISABLE_IR_GEN && Ctx.getFlags().getDisableIRGeneration()) {
+    Ctx.getStrDump() << "Error: Build doesn't allow --no-ir-gen when not "
+                     << "ALLOW_DISABLE_IR_GEN!\n";
+    return Ctx.getErrorStatus()->assign(EC_Args);
+  }
+
+  // Force -build-on-read=0 for .ll files.
+  const std::string LLSuffix = ".ll";
+  const IceString &IRFilename = ExtraFlags.getIRFilename();
+  bool BuildOnRead = ExtraFlags.getBuildOnRead();
+  if (ALLOW_LLVM_IR_AS_INPUT && IRFilename.length() >= LLSuffix.length() &&
+      IRFilename.compare(IRFilename.length() - LLSuffix.length(),
+                         LLSuffix.length(), LLSuffix) == 0)
+    BuildOnRead = false;
+
+  TimerMarker T(Ice::TimerStack::TT_szmain, &Ctx);
+
+  if (Ctx.getFlags().getOutFileType() == FT_Elf) {
+    TimerMarker T1(Ice::TimerStack::TT_emit, &Ctx);
+    Ctx.getObjectWriter()->writeInitialELFHeader();
+  }
+
+  Ctx.startWorkerThreads();
+
+  std::unique_ptr<Translator> Translator;
+  if (BuildOnRead) {
+    std::unique_ptr<PNaClTranslator> PTranslator(new PNaClTranslator(&Ctx));
+    std::unique_ptr<llvm::StreamingMemoryObject> MemObj(
+        new llvm::StreamingMemoryObjectImpl(InputStream.release()));
+    PTranslator->translate(IRFilename, std::move(MemObj));
+    Translator.reset(PTranslator.release());
+  } else if (ALLOW_LLVM_IR) {
+    if (PNACL_BROWSER_TRANSLATOR) {
+      Ctx.getStrDump()
+          << "non BuildOnRead is not supported w/ PNACL_BROWSER_TRANSLATOR\n";
+      return Ctx.getErrorStatus()->assign(EC_Args);
+    }
+    // Parse the input LLVM IR file into a module.
+    llvm::SMDiagnostic Err;
+    TimerMarker T1(Ice::TimerStack::TT_parse, &Ctx);
+    llvm::raw_ostream *Verbose =
+        ExtraFlags.getLLVMVerboseErrors() ? &llvm::errs() : nullptr;
+    std::unique_ptr<llvm::Module> Mod =
+        NaClParseIRFile(IRFilename, ExtraFlags.getInputFileFormat(), Err,
+                        Verbose, llvm::getGlobalContext());
+    if (!Mod) {
+      Err.print(ExtraFlags.getAppName().c_str(), llvm::errs());
+      return Ctx.getErrorStatus()->assign(EC_Bitcode);
+    }
+
+    std::unique_ptr<Converter> Converter(new class Converter(Mod.get(), &Ctx));
+    Converter->convertToIce();
+    Translator.reset(Converter.release());
+  } else {
+    Ctx.getStrDump() << "Error: Build doesn't allow LLVM IR, "
+                     << "--build-on-read=0 not allowed\n";
+    return Ctx.getErrorStatus()->assign(EC_Args);
+  }
+
+  Ctx.waitForWorkerThreads();
+  Translator->transferErrorCode();
+  Translator->emitConstants();
+
+  if (Ctx.getFlags().getOutFileType() == FT_Elf) {
+    TimerMarker T1(Ice::TimerStack::TT_emit, &Ctx);
+    Ctx.getObjectWriter()->setUndefinedSyms(Ctx.getConstantExternSyms());
+    Ctx.getObjectWriter()->writeNonUserSections();
+  }
+  if (Ctx.getFlags().getSubzeroTimingEnabled())
+    Ctx.dumpTimers();
+  if (Ctx.getFlags().getTimeEachFunction()) {
+    const bool DumpCumulative = false;
+    Ctx.dumpTimers(GlobalContext::TSK_Funcs, DumpCumulative);
+  }
+  const bool FinalStats = true;
+  Ctx.dumpStats("_FINAL_", FinalStats);
+}
+
+} // end of namespace Ice
diff --git a/src/IceCompiler.h b/src/IceCompiler.h
new file mode 100644
index 0000000..87915a6
--- /dev/null
+++ b/src/IceCompiler.h
@@ -0,0 +1,43 @@
+//===- subzero/src/IceCompiler.h - Compiler driver --------------*- 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 driver for translating bitcode to native code.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef SUBZERO_SRC_ICECOMPILER_H
+#define SUBZERO_SRC_ICECOMPILER_H
+
+#include "IceDefs.h"
+
+namespace llvm {
+class DataStreamer;
+}
+
+namespace Ice {
+
+class ClFlagsExtra;
+
+// A compiler driver. It may be called to handle a single compile request.
+class Compiler {
+  Compiler(const Compiler &) = delete;
+  Compiler &operator=(const Compiler &) = delete;
+
+public:
+  Compiler() {}
+
+  // Run the compiler with the given GlobalContext for compilation
+  // state.  Upon error, the Context's error status will be set.
+  void run(const ClFlagsExtra &ExtraFlags, GlobalContext &Ctx,
+           std::unique_ptr<llvm::DataStreamer> &&InputStream);
+};
+
+} // end of namespace Ice
+
+#endif // SUBZERO_SRC_ICECOMPILER_H
diff --git a/src/IceGlobalContext.h b/src/IceGlobalContext.h
index d81697d..9264d43 100644
--- a/src/IceGlobalContext.h
+++ b/src/IceGlobalContext.h
@@ -300,6 +300,14 @@
   EmitterWorkItem *emitQueueBlockingPop();
   void emitQueueNotifyEnd() { EmitQ.notifyEnd(); }
 
+  void initParserThread() {
+    ThreadContext *Tls = new ThreadContext();
+    auto Timers = getTimers();
+    Timers->initInto(Tls->Timers);
+    AllThreadContexts.push_back(Tls);
+    ICE_TLS_SET_FIELD(TLS, Tls);
+  }
+
   void startWorkerThreads() {
     size_t NumWorkers = getFlags().getNumTranslationThreads();
     auto Timers = getTimers();
diff --git a/src/IceInstX8632.h b/src/IceInstX8632.h
index cebcc55..de60349 100644
--- a/src/IceInstX8632.h
+++ b/src/IceInstX8632.h
@@ -290,6 +290,7 @@
       validateVectorAddrModeOpnd(getSrc(i));
     }
   }
+
 private:
   static void validateVectorAddrModeOpnd(const Operand *Opnd) {
     if (llvm::isa<OperandX8632Mem>(Opnd) && isVectorType(Opnd->getType())) {
diff --git a/src/IceTranslator.h b/src/IceTranslator.h
index d039148..adddc32 100644
--- a/src/IceTranslator.h
+++ b/src/IceTranslator.h
@@ -74,7 +74,7 @@
 protected:
   GlobalContext *Ctx;
   uint32_t NextSequenceNumber;
-  // Exit status of the translation. False is successful. True otherwise.
+  // ErrorCode of the translation.
   ErrorCode ErrorStatus;
 };
 
diff --git a/src/Makefile b/src/Makefile
index ebccd54..7b57f44 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -2,7 +2,7 @@
 SUBZERO_LEVEL := ..
 
 TOOLNAME := pnacl-sz
-LINK_COMPONENTS := bitreader naclbitreader irreader naclanalysis nacltransforms support
+LINK_COMPONENTS := irreader bitreader naclbitreader naclanalysis core support
 
 # This tool has no plugins, optimize startup time.
 TOOL_NO_EXPORTS := 1
diff --git a/src/main.cpp b/src/main.cpp
index 9858fa2..50556f6 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,4 +1,4 @@
-//===- subzero/src/main.cpp - Driver for bitcode translation --------------===//
+//===- subzero/src/main.cpp - Entry point for bitcode translation ---------===//
 //
 //                        The Subzero Code Generator
 //
@@ -7,465 +7,30 @@
 //
 //===----------------------------------------------------------------------===//
 //
-// This file defines a driver for translating PNaCl bitcode into native code.
-// It can either directly parse the binary bitcode file, or use LLVM routines to
-// parse a textual bitcode file into LLVM IR and then convert LLVM IR into ICE.
-// In either case, the high-level ICE is then compiled down to native code, as
-// either an ELF object file or a textual asm file.
+// This file defines the entry point for translating PNaCl bitcode into
+// native code.
 //
 //===----------------------------------------------------------------------===//
 
-#include <fstream>
-#include <iostream>
-
-#include "llvm/ADT/STLExtras.h"
-#include "llvm/IR/LLVMContext.h"
-#include "llvm/IR/Module.h"
-#include "llvm/IRReader/IRReader.h"
-#include "llvm/Support/CommandLine.h"
-#include "llvm/Support/FileSystem.h"
-#include "llvm/Support/raw_os_ostream.h"
-#include "llvm/Support/SourceMgr.h"
-#include "llvm/Support/StreamingMemoryObject.h"
-
-#include "IceCfg.h"
-#include "IceClFlags.h"
-#include "IceConverter.h"
-#include "IceELFObjectWriter.h"
-#include "IceELFStreamer.h"
-#include "PNaClTranslator.h"
-
-using namespace llvm;
-
-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_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_AddrOpt, "addropt", "Address mode optimization"),
-        clEnumValN(Ice::IceV_Random, "random", "Randomization details"),
-        clEnumValN(Ice::IceV_All, "all", "Use all verbose options"),
-        clEnumValN(Ice::IceV_Most, "most",
-                   "Use all verbose options except 'regalloc' and 'time'"),
-        clEnumValN(Ice::IceV_None, "none", "No verbosity"), clEnumValEnd));
-static cl::opt<Ice::TargetArch> TargetArch(
-    "target", cl::desc("Target architecture:"), cl::init(Ice::Target_X8632),
-    cl::values(
-        clEnumValN(Ice::Target_X8632, "x8632", "x86-32"),
-        clEnumValN(Ice::Target_X8632, "x86-32", "x86-32 (same as x8632)"),
-        clEnumValN(Ice::Target_X8632, "x86_32", "x86-32 (same as x8632)"),
-        clEnumValN(Ice::Target_X8664, "x8664", "x86-64"),
-        clEnumValN(Ice::Target_X8664, "x86-64", "x86-64 (same as x8664)"),
-        clEnumValN(Ice::Target_X8664, "x86_64", "x86-64 (same as x8664)"),
-        clEnumValN(Ice::Target_ARM32, "arm", "arm32"),
-        clEnumValN(Ice::Target_ARM32, "arm32", "arm32 (same as arm)"),
-        clEnumValN(Ice::Target_ARM64, "arm64", "arm64"), clEnumValEnd));
-
-cl::opt<Ice::TargetInstructionSet> InstructionSet(
-    "mattr", cl::desc("Target architecture attributes"),
-    cl::init(Ice::X86InstructionSet_SSE2),
-    cl::values(clEnumValN(Ice::X86InstructionSet_SSE2, "sse2",
-                          "Enable SSE2 instructions (default)"),
-               clEnumValN(Ice::X86InstructionSet_SSE4_1, "sse4.1",
-                          "Enable SSE 4.1 instructions"),
-               clEnumValEnd));
-
-static cl::opt<bool> UseSandboxing("sandbox", cl::desc("Use sandboxing"));
-static cl::opt<bool>
-    FunctionSections("ffunction-sections",
-                     cl::desc("Emit functions into separate sections"));
-static cl::opt<bool>
-    DataSections("fdata-sections",
-                 cl::desc("Emit (global) data into separate sections"));
-static cl::opt<Ice::OptLevel>
-    OptLevel(cl::desc("Optimization level"), cl::init(Ice::Opt_m1),
-             cl::value_desc("level"),
-             cl::values(clEnumValN(Ice::Opt_m1, "Om1", "-1"),
-                        clEnumValN(Ice::Opt_m1, "O-1", "-1"),
-                        clEnumValN(Ice::Opt_0, "O0", "0"),
-                        clEnumValN(Ice::Opt_1, "O1", "1"),
-                        clEnumValN(Ice::Opt_2, "O2", "2"), clEnumValEnd));
-static cl::opt<std::string> IRFilename(cl::Positional, cl::desc("<IR file>"),
-                                       cl::init("-"));
-static cl::opt<std::string> OutputFilename("o",
-                                           cl::desc("Override output filename"),
-                                           cl::init("-"),
-                                           cl::value_desc("filename"));
-static cl::opt<std::string> LogFilename("log", cl::desc("Set log 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("externalize",
-                                     cl::desc("Externalize all symbols"));
-static cl::opt<bool>
-    DisableTranslation("notranslate", cl::desc("Disable Subzero translation"));
-// Note: Modifiable only if ALLOW_DISABLE_IR_GEN.
-static cl::opt<bool>
-    DisableIRGeneration("no-ir-gen",
-                        cl::desc("Disable generating Subzero IR."));
-static cl::opt<std::string>
-    TranslateOnly("translate-only",
-                  cl::desc("Translate only the given function"), cl::init(""));
-
-static cl::opt<bool> SubzeroTimingEnabled(
-    "timing", cl::desc("Enable breakdown timing of Subzero translation"));
-
-static cl::opt<bool> TimeEachFunction(
-    "timing-funcs", cl::desc("Print total translation time for each function"));
-
-static cl::opt<std::string> TimingFocusOn(
-    "timing-focus",
-    cl::desc("Break down timing for a specific function (use '*' for all)"),
-    cl::init(""));
-
-static cl::opt<std::string> VerboseFocusOn(
-    "verbose-focus",
-    cl::desc("Temporarily enable full verbosity for a specific function"),
-    cl::init(""));
-
-static cl::opt<bool>
-    EnablePhiEdgeSplit("phi-edge-split",
-                       cl::desc("Enable edge splitting for Phi lowering"),
-                       cl::init(true));
-
-static cl::opt<bool> DecorateAsm(
-    "asm-verbose",
-    cl::desc("Decorate textual asm output with register liveness info"));
-
-static cl::opt<bool>
-    DumpStats("szstats",
-              cl::desc("Print statistics after translating each function"));
-
-// This is currently needed by crosstest.py.
-static cl::opt<bool> AllowUninitializedGlobals(
-    "allow-uninitialized-globals",
-    cl::desc("Allow global variables to be uninitialized"));
-
-static cl::opt<NaClFileFormat> InputFileFormat(
-    "bitcode-format", cl::desc("Define format of input file:"),
-    cl::values(clEnumValN(LLVMFormat, "llvm", "LLVM file (default)"),
-               clEnumValN(PNaClFormat, "pnacl", "PNaCl bitcode file"),
-               clEnumValEnd),
-    cl::init(LLVMFormat));
-
-static cl::opt<std::string>
-    DefaultGlobalPrefix("default-global-prefix",
-                        cl::desc("Define default global prefix for naming "
-                                 "unnamed globals"),
-                        cl::init("Global"));
-
-static cl::opt<std::string>
-    DefaultFunctionPrefix("default-function-prefix",
-                          cl::desc("Define default function prefix for naming "
-                                   "unnamed functions"),
-                          cl::init("Function"));
-
-// Note: While this flag isn't used in the minimal build, we keep this
-// flag so that tests can set this command-line flag without concern
-// to the type of build. We double check that this flag at runtime
-// to make sure the consistency is maintained.
-static cl::opt<bool>
-    BuildOnRead("build-on-read",
-                cl::desc("Build ICE instructions when reading bitcode"),
-                cl::init(true));
-
-static cl::opt<bool> AllowErrorRecovery(
-    "allow-pnacl-reader-error-recovery",
-    cl::desc("Allow error recovery when reading PNaCl bitcode."),
-    cl::init(false));
-
-static cl::opt<bool> LLVMVerboseErrors(
-    "verbose-llvm-parse-errors",
-    cl::desc("Print out more descriptive PNaCl bitcode parse errors when "
-             "building LLVM IR first"),
-    cl::init(false));
-
-static cl::opt<Ice::FileType> OutFileType(
-    "filetype", cl::desc("Output file type"), cl::init(Ice::FT_Iasm),
-    cl::values(clEnumValN(Ice::FT_Elf, "obj", "Native ELF object ('.o') file"),
-               clEnumValN(Ice::FT_Asm, "asm", "Assembly ('.s') file"),
-               clEnumValN(Ice::FT_Iasm, "iasm",
-                          "Low-level integrated assembly ('.s') file"),
-               clEnumValEnd));
-
-static cl::opt<bool> AlwaysExitSuccess(
-    "exit-success", cl::desc("Exit with success status, even if errors found"),
-    cl::init(false));
-
-static cl::opt<bool> GenerateBuildAtts(
-    "build-atts", cl::desc("Generate list of build attributes associated with "
-                           "this executable."),
-    cl::init(false));
-
-// Number of translation threads (in addition to the parser thread and
-// the emitter thread).  The special case of 0 means purely
-// sequential, i.e. parser, translator, and emitter all within the
-// same single thread.  (This may need a slight rework if we expand to
-// multiple parser or emitter threads.)
-static cl::opt<uint32_t> NumThreads(
-    "threads",
-    cl::desc("Number of translation threads (0 for purely sequential)"),
-    // TODO(stichnot): Settle on a good default.  Consider
-    // something related to std::thread::hardware_concurrency().
-    cl::init(2));
-
-static cl::opt<bool> DoNopInsertion("nop-insertion",
-                                    cl::desc("Randomly insert NOPs"),
-                                    cl::init(false));
-
-static cl::opt<int> MaxNopsPerInstruction(
-    "max-nops-per-instruction",
-    cl::desc("Max number of nops to insert per instruction"), cl::init(1));
-
-static cl::opt<int> NopProbabilityAsPercentage(
-    "nop-insertion-percentage",
-    cl::desc("Nop insertion probability as percentage"), cl::init(10));
-
-static cl::opt<bool>
-    RandomizeRegisterAllocation("randomize-regalloc",
-                                cl::desc("Randomize register allocation"),
-                                cl::init(false));
-
-// TODO(stichnot): See if we can easily use LLVM's -rng-seed option
-// and implementation.  I expect the implementation is different and
-// therefore the tests would need to be changed.
-cl::opt<unsigned long long>
-    RandomSeed("sz-seed", cl::desc("Seed the random number generator"),
-               cl::init(time(0)));
-
-static int GetReturnValue(int Val) {
-  if (AlwaysExitSuccess)
-    return 0;
-  return Val;
-}
-
-static struct {
-  const char *FlagName;
-  int FlagValue;
-} ConditionalBuildAttributes[] = {{"dump", ALLOW_DUMP},
-                                  {"disable_ir_gen", ALLOW_DISABLE_IR_GEN},
-                                  {"llvm_cl", ALLOW_LLVM_CL},
-                                  {"llvm_ir", ALLOW_LLVM_IR},
-                                  {"llvm_ir_as_input", ALLOW_LLVM_IR_AS_INPUT},
-                                  {"minimal_build", ALLOW_MINIMAL_BUILD}};
-
-// Validates values of build attributes. Prints them to Stream if
-// Stream is non-null.
-static void ValidateAndGenerateBuildAttributes(Ice::Ostream *Stream) {
-
-  if (Stream)
-    *Stream << TargetArch << "\n";
-
-  for (size_t i = 0; i < array_lengthof(ConditionalBuildAttributes); ++i) {
-    switch (ConditionalBuildAttributes[i].FlagValue) {
-    case 0:
-      if (Stream)
-        *Stream << "no_" << ConditionalBuildAttributes[i].FlagName << "\n";
-      break;
-    case 1:
-      if (Stream)
-        *Stream << "allow_" << ConditionalBuildAttributes[i].FlagName << "\n";
-      break;
-    default: {
-      std::string Buffer;
-      raw_string_ostream StrBuf(Buffer);
-      StrBuf << "Flag " << ConditionalBuildAttributes[i].FlagName
-             << " must be defined as 0/1. Found: "
-             << ConditionalBuildAttributes[i].FlagValue;
-      report_fatal_error(StrBuf.str());
-    }
-    }
-  }
-}
+#include "IceBrowserCompileServer.h"
+#include "IceCompiler.h"
+#include "IceCompileServer.h"
 
 int main(int argc, char **argv) {
-
-  cl::ParseCommandLineOptions(argc, argv);
-
-  if (DisableIRGeneration)
-    DisableTranslation = true;
-
-  Ice::VerboseMask VMask = Ice::IceV_None;
-  // Don't generate verbose messages if routines
-  // to dump messages are not available.
-  if (ALLOW_DUMP) {
-    for (unsigned i = 0; i != VerboseList.size(); ++i)
-      VMask |= VerboseList[i];
-  }
-
-  std::ofstream Lfs;
-  std::unique_ptr<Ice::Ostream> Ls;
-  if (LogFilename != "-") {
-    Lfs.open(LogFilename.c_str(), std::ofstream::out);
-    Ls.reset(new raw_os_ostream(Lfs));
-  } else {
-    Ls.reset(new raw_os_ostream(std::cout));
-  }
-  Ls->SetUnbuffered();
-
-  ValidateAndGenerateBuildAttributes(GenerateBuildAtts ? Ls.get() : nullptr);
-  if (GenerateBuildAtts)
-    return GetReturnValue(Ice::EC_None);
-
-  if (!ALLOW_DISABLE_IR_GEN && DisableIRGeneration) {
-    *Ls << "Error: Build doesn't allow --no-ir-gen when not "
-        << "ALLOW_DISABLE_IR_GEN!\n";
-    return GetReturnValue(Ice::EC_Args);
-  }
-
-  Ice::ClFlags Flags;
-  Flags.setAllowErrorRecovery(AllowErrorRecovery);
-  Flags.setAllowUninitializedGlobals(AllowUninitializedGlobals);
-  Flags.setDataSections(DataSections);
-  Flags.setDecorateAsm(DecorateAsm);
-  Flags.setDefaultFunctionPrefix(DefaultFunctionPrefix);
-  Flags.setDefaultGlobalPrefix(DefaultGlobalPrefix);
-  Flags.setDisableInternal(DisableInternal);
-  Flags.setDisableIRGeneration(DisableIRGeneration);
-  Flags.setDisableTranslation(DisableTranslation);
-  Flags.setDumpStats(DumpStats);
-  Flags.setFunctionSections(FunctionSections);
-  Flags.setNumTranslationThreads(NumThreads);
-  Flags.setOptLevel(OptLevel);
-  Flags.setPhiEdgeSplit(EnablePhiEdgeSplit);
-  Flags.setRandomSeed(RandomSeed);
-  Flags.setShouldDoNopInsertion(DoNopInsertion);
-  Flags.setShouldRandomizeRegAlloc(RandomizeRegisterAllocation);
-  Flags.setSubzeroTimingEnabled(SubzeroTimingEnabled);
-  Flags.setTargetArch(TargetArch);
-  Flags.setTargetInstructionSet(InstructionSet);
-  Flags.setTestPrefix(TestPrefix);
-  Flags.setTimeEachFunction(TimeEachFunction);
-  Flags.setTimingFocusOn(TimingFocusOn);
-  Flags.setTranslateOnly(TranslateOnly);
-  Flags.setUseSandboxing(UseSandboxing);
-  Flags.setVerboseFocusOn(VerboseFocusOn);
-  Flags.setOutFileType(OutFileType);
-  Flags.setMaxNopsPerInstruction(MaxNopsPerInstruction);
-  Flags.setNopProbabilityAsPercentage(NopProbabilityAsPercentage);
-  Flags.setVerbose(VMask);
-
-  // Force -build-on-read=0 for .ll files.
-  const std::string LLSuffix = ".ll";
-  if (IRFilename.length() >= LLSuffix.length() &&
-      IRFilename.compare(IRFilename.length() - LLSuffix.length(),
-                         LLSuffix.length(), LLSuffix) == 0)
-    BuildOnRead = false;
-
-  // With the ELF writer, use a raw_fd_ostream to allow seeking.
-  // Also don't buffer, otherwise it gets pretty slow.
-  std::unique_ptr<Ice::Ostream> Os;
-  std::unique_ptr<Ice::ELFStreamer> ELFStr;
-  std::ofstream Ofs;
-  switch (OutFileType) {
-  case Ice::FT_Elf: {
-    if (OutputFilename == "-") {
-      *Ls << "Error: writing binary ELF to stdout is unsupported\n";
-      return GetReturnValue(Ice::EC_Args);
-    }
-    std::error_code EC;
-    raw_fd_ostream *FdOs =
-        new raw_fd_ostream(OutputFilename, EC, sys::fs::F_None);
-    // NaCl sets st_blksize to 0, and LLVM uses that to pick the
-    // default preferred buffer size. Set to something non-zero.
-    FdOs->SetBufferSize(1 << 14);
-    Os.reset(FdOs);
-    if (EC) {
-      *Ls << "Failed to open output file: " << OutputFilename << ":\n"
-          << EC.message() << "\n";
-      return GetReturnValue(Ice::EC_Args);
-    }
-    ELFStr.reset(new Ice::ELFStreamer(*FdOs));
-  } break;
-  case Ice::FT_Asm:
-  case Ice::FT_Iasm: {
-    if (OutputFilename != "-") {
-      Ofs.open(OutputFilename.c_str(), std::ofstream::out);
-      Os.reset(new raw_os_ostream(Ofs));
-    } else {
-      Os.reset(new raw_os_ostream(std::cout));
-    }
-    Os->SetUnbuffered();
-  } break;
-  }
-
-  Ice::GlobalContext Ctx(Ls.get(), Os.get(), ELFStr.get(), Flags);
-
-  Ice::TimerMarker T(Ice::TimerStack::TT_szmain, &Ctx);
-
-  if (OutFileType == Ice::FT_Elf) {
-    Ice::TimerMarker T1(Ice::TimerStack::TT_emit, &Ctx);
-    Ctx.getObjectWriter()->writeInitialELFHeader();
-  }
-
-  Ctx.startWorkerThreads();
-
-  std::unique_ptr<Ice::Translator> Translator;
-  if (BuildOnRead) {
-    std::unique_ptr<Ice::PNaClTranslator> PTranslator(
-        new Ice::PNaClTranslator(&Ctx));
-    std::string StrError;
-    std::unique_ptr<DataStreamer> FileStreamer(
-        getDataFileStreamer(IRFilename, &StrError));
-    if (!StrError.empty() || !FileStreamer) {
-      SMDiagnostic Err(IRFilename, SourceMgr::DK_Error, StrError);
-      Err.print(argv[0], errs());
-      return GetReturnValue(Ice::EC_Bitcode);
-    }
-    std::unique_ptr<StreamingMemoryObject> MemObj(
-        new StreamingMemoryObjectImpl(FileStreamer.release()));
-    PTranslator->translate(IRFilename, std::move(MemObj));
-    Translator.reset(PTranslator.release());
-  } else if (ALLOW_LLVM_IR) {
-    // Parse the input LLVM IR file into a module.
-    SMDiagnostic Err;
-    Ice::TimerMarker T1(Ice::TimerStack::TT_parse, &Ctx);
-    raw_ostream *Verbose = LLVMVerboseErrors ? &errs() : nullptr;
-    std::unique_ptr<Module> Mod = NaClParseIRFile(
-        IRFilename, InputFileFormat, Err, Verbose, getGlobalContext());
-    if (!Mod) {
-      Err.print(argv[0], errs());
-      return GetReturnValue(Ice::EC_Bitcode);
-    }
-
-    std::unique_ptr<Ice::Converter> Converter(
-        new Ice::Converter(Mod.get(), &Ctx));
-    Converter->convertToIce();
-    Translator.reset(Converter.release());
-  } else {
-    *Ls << "Error: Build doesn't allow LLVM IR, "
-        << "--build-on-read=0 not allowed\n";
-    return GetReturnValue(Ice::EC_Args);
-  }
-
-  Ctx.waitForWorkerThreads();
-  Translator->transferErrorCode();
-  Translator->emitConstants();
-
-  if (OutFileType == Ice::FT_Elf) {
-    Ice::TimerMarker T1(Ice::TimerStack::TT_emit, &Ctx);
-    Ctx.getObjectWriter()->setUndefinedSyms(Ctx.getConstantExternSyms());
-    Ctx.getObjectWriter()->writeNonUserSections();
-  }
-  if (SubzeroTimingEnabled)
-    Ctx.dumpTimers();
-  if (TimeEachFunction) {
-    const bool DumpCumulative = false;
-    Ctx.dumpTimers(Ice::GlobalContext::TSK_Funcs, DumpCumulative);
-  }
-  const bool FinalStats = true;
-  Ctx.dumpStats("_FINAL_", FinalStats);
-  return GetReturnValue(Ctx.getErrorStatus()->value());
+  // Start file server and "wait" for compile request.
+  Ice::Compiler Comp;
+// Can only compile the BrowserCompileServer w/ the NaCl compiler.
+#if PNACL_BROWSER_TRANSLATOR
+  // There are no real commandline arguments in the browser case.
+  // They are supplied via IPC.
+  assert(argc == 1);
+  (void)argv;
+  Ice::BrowserCompileServer Server(Comp);
+  Server.run();
+  return Server.getErrorCode().value();
+#else  // !PNACL_BROWSER_TRANSLATOR
+  Ice::CLCompileServer Server(Comp, argc, argv);
+  Server.run();
+  return Server.getErrorCode().value();
+#endif // !PNACL_BROWSER_TRANSLATOR
 }