diff --git a/src/librustc_trans/driver/driver.rs b/src/librustc_trans/driver/driver.rs
index a5709a0219ba9..aeef16276e5b5 100644
--- a/src/librustc_trans/driver/driver.rs
+++ b/src/librustc_trans/driver/driver.rs
@@ -51,6 +51,12 @@ pub fn compile_input(sess: Session,
                      outdir: &Option<Path>,
                      output: &Option<Path>,
                      addl_plugins: Option<Plugins>) {
+    // These may be left in an incoherent state after a previous compile.
+    // `clear_tables` and `get_ident_interner().clear()` can be used to free
+    // memory, but they do not restore the initial state.
+    syntax::ext::mtwt::reset_tables();
+    token::reset_ident_interner();
+
     // We need nested scopes here, because the intermediate results can keep
     // large chunks of memory alive and we want to free them as soon as
     // possible to keep the peak memory usage low
diff --git a/src/libsyntax/ext/mtwt.rs b/src/libsyntax/ext/mtwt.rs
index e6d886e28ba7f..6ba90bbebed01 100644
--- a/src/libsyntax/ext/mtwt.rs
+++ b/src/libsyntax/ext/mtwt.rs
@@ -136,6 +136,16 @@ pub fn clear_tables() {
     with_resolve_table_mut(|table| *table = HashMap::new());
 }
 
+/// Reset the tables to their initial state
+pub fn reset_tables() {
+    with_sctable(|table| {
+        *table.table.borrow_mut() = vec!(EmptyCtxt, IllegalCtxt);
+        *table.mark_memo.borrow_mut() = HashMap::new();
+        *table.rename_memo.borrow_mut() = HashMap::new();
+    });
+    with_resolve_table_mut(|table| *table = HashMap::new());
+}
+
 /// Add a value to the end of a vec, return its index
 fn idx_push<T>(vec: &mut Vec<T>, val: T) -> u32 {
     vec.push(val);
diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs
index 583ace977fe46..37df2bf14c2e1 100644
--- a/src/libsyntax/parse/token.rs
+++ b/src/libsyntax/parse/token.rs
@@ -564,6 +564,12 @@ pub fn get_ident_interner() -> Rc<IdentInterner> {
     KEY.with(|k| k.clone())
 }
 
+/// Reset the ident interner to its initial state.
+pub fn reset_ident_interner() {
+    let interner = get_ident_interner();
+    interner.reset(mk_fresh_ident_interner());
+}
+
 /// Represents a string stored in the task-local interner. Because the
 /// interner lives for the life of the task, this can be safely treated as an
 /// immortal string, as long as it never crosses between tasks.
diff --git a/src/libsyntax/util/interner.rs b/src/libsyntax/util/interner.rs
index ede967bba25cd..590a04ce2210f 100644
--- a/src/libsyntax/util/interner.rs
+++ b/src/libsyntax/util/interner.rs
@@ -214,6 +214,11 @@ impl StrInterner {
         *self.map.borrow_mut() = HashMap::new();
         *self.vect.borrow_mut() = Vec::new();
     }
+
+    pub fn reset(&self, other: StrInterner) {
+        *self.map.borrow_mut() = other.map.into_inner();
+        *self.vect.borrow_mut() = other.vect.into_inner();
+    }
 }
 
 #[cfg(test)]
diff --git a/src/test/run-make/issue-19371/Makefile b/src/test/run-make/issue-19371/Makefile
new file mode 100644
index 0000000000000..9f3ec78465b25
--- /dev/null
+++ b/src/test/run-make/issue-19371/Makefile
@@ -0,0 +1,9 @@
+-include ../tools.mk
+
+# This test ensures that rustc compile_input can be called twice in one task
+# without causing a panic.
+# The program needs the path to rustc to get sysroot.
+
+all:
+	$(RUSTC) foo.rs
+	$(call RUN,foo $(TMPDIR) $(RUSTC))
diff --git a/src/test/run-make/issue-19371/foo.rs b/src/test/run-make/issue-19371/foo.rs
new file mode 100644
index 0000000000000..715fae314b673
--- /dev/null
+++ b/src/test/run-make/issue-19371/foo.rs
@@ -0,0 +1,62 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+extern crate rustc;
+extern crate rustc_trans;
+extern crate syntax;
+
+use rustc::session::{build_session, Session};
+use rustc::session::config::{basic_options, build_configuration, OutputTypeExe};
+use rustc_trans::driver::driver::{Input, StrInput, compile_input};
+use syntax::diagnostics::registry::Registry;
+
+fn main() {
+    let src = r#"
+    fn main() {}
+    "#;
+
+    let args = std::os::args();
+
+    if args.len() < 4 {
+        panic!("expected rustc path");
+    }
+
+    let tmpdir = Path::new(args[1].as_slice());
+
+    let mut sysroot = Path::new(args[3].as_slice());
+    sysroot.pop();
+    sysroot.pop();
+
+    compile(src.to_string(), tmpdir.join("out"), sysroot.clone());
+
+    compile(src.to_string(), tmpdir.join("out"), sysroot.clone());
+}
+
+fn basic_sess(sysroot: Path) -> Session {
+    let mut opts = basic_options();
+    opts.output_types = vec![OutputTypeExe];
+    opts.maybe_sysroot = Some(sysroot);
+
+    let descriptions = Registry::new(&rustc::DIAGNOSTICS);
+    let sess = build_session(opts, None, descriptions);
+    sess
+}
+
+fn compile(code: String, output: Path, sysroot: Path) {
+    let sess = basic_sess(sysroot);
+    let cfg = build_configuration(&sess);
+
+    compile_input(sess,
+            cfg,
+            &StrInput(code),
+            &None,
+            &Some(output),
+            None);
+}