diff --git a/src/parsers/end.c b/src/parsers/end.c
index 85499d9348cd1df6503428a55d7a2ab878d1ef63..3643f6c7f109c93dd60d143c2c94c06adc06846f 100644
--- a/src/parsers/end.c
+++ b/src/parsers/end.c
@@ -1,3 +1,10 @@
+#ifdef HAMMER_LLVM_BACKEND
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpedantic"
+#include <llvm-c/Core.h>
+#pragma GCC diagnostic pop
+#include "../backends/llvm/llvm.h"
+#endif
 #include "parser_internal.h"
 
 static HParseResult* parse_end(void *env, HParseState *state) {
@@ -19,12 +26,92 @@ static bool end_ctrvm(HRVMProg *prog, void *env) {
   return true;
 }
 
+#ifdef HAMMER_LLVM_BACKEND
+
+static bool end_llvm(HLLVMParserCompileContext *ctxt, void* env) {
+  if (!ctxt) return false;
+
+  /* Set up some basic blocks */
+  LLVMBasicBlockRef entry = LLVMAppendBasicBlock(ctxt->func, "end_entry");
+  LLVMBasicBlockRef success = LLVMAppendBasicBlock(ctxt->func, "end_success");
+  LLVMBasicBlockRef end = LLVMAppendBasicBlock(ctxt->func, "end_end");
+
+  /* Basic block: entry */
+  LLVMBuildBr(ctxt->builder, entry);
+  LLVMPositionBuilderAtEnd(ctxt->builder, entry);
+
+  /*
+   * This needs to test if we're at the end of the input stream by
+   * comparing the index and length fields; build a struct GEP to
+   * get at their values.
+   */
+  LLVMValueRef gep_indices[2];
+  /* The struct itself */
+  gep_indices[0] = LLVMConstInt(LLVMInt32Type(), 0, 0);
+  /* The index field (see HInputStream in internal.h */
+  gep_indices[1] = LLVMConstInt(LLVMInt32Type(), 2, 0);
+  /* GEP */
+  LLVMValueRef index_ptr = LLVMBuildGEP(ctxt->builder,
+      ctxt->stream, gep_indices, 2, "index_ptr");
+  /* The length field (see HInputStream in internal.h */
+  gep_indices[1] = LLVMConstInt(LLVMInt32Type(), 3, 0);
+  /* GEP */
+  LLVMValueRef length_ptr = LLVMBuildGEP(ctxt->builder,
+      ctxt->stream, gep_indices, 2, "length_ptr");
+  /* Now load them */
+  LLVMValueRef index = LLVMBuildLoad(ctxt->builder,
+      index_ptr, "index");
+  LLVMValueRef length = LLVMBuildLoad(ctxt->builder,
+      length_ptr, "length");
+  /* Compare */
+  LLVMValueRef icmp = LLVMBuildICmp(ctxt->builder, LLVMIntEQ, index, length, "index == length");
+  /* Branch on comparison */
+  LLVMBuildCondBr(ctxt->builder, icmp, success, end);
+
+  /* Basic block: success */
+  LLVMPositionBuilderAtEnd(ctxt->builder, success);
+  /* Set up a call to h_arena_malloc() to get an HParseResult */
+  LLVMValueRef make_result_args[] = {
+    ctxt->arena,
+    LLVMConstNull(ctxt->llvm_parsedtokenptr)
+  };
+  LLVMValueRef result_ptr = LLVMBuildCall(ctxt->builder,
+      LLVMGetNamedFunction(ctxt->mod, "make_result"),
+      make_result_args, 2, "result_ptr");
+
+  /* Branch to end */
+  LLVMBuildBr(ctxt->builder, end);
+
+  /* Basic block: end */
+  LLVMPositionBuilderAtEnd(ctxt->builder, end);
+  /* Set up a phi depending on whether we have a token or not */
+  LLVMValueRef rv = LLVMBuildPhi(ctxt->builder, ctxt->llvm_parseresultptr, "rv");
+  LLVMBasicBlockRef rv_phi_incoming_blocks[] = {
+    success,
+    entry
+  };
+  LLVMValueRef rv_phi_incoming_values[] = {
+    result_ptr,
+    LLVMConstNull(ctxt->llvm_parseresultptr)
+  };
+  LLVMAddIncoming(rv, rv_phi_incoming_values, rv_phi_incoming_blocks, 2);
+  /* Return it */
+  LLVMBuildRet(ctxt->builder, rv);
+
+  return true;
+}
+
+#endif /* defined(HAMMER_LLVM_BACKEND) */
+
 static const HParserVtable end_vt = {
   .parse = parse_end,
   .isValidRegular = h_true,
   .isValidCF = h_true,
   .desugar = desugar_end,
   .compile_to_rvm = end_ctrvm,
+#ifdef HAMMER_LLVM_BACKEND
+  .llvm = end_llvm,
+#endif
   .higher = false,
 };