diff --git a/src/backends/llvm.c b/src/backends/llvm.c
new file mode 100644
index 0000000000000000000000000000000000000000..d7904a0ec52bc46d13b5fce71b2988d5459f2398
--- /dev/null
+++ b/src/backends/llvm.c
@@ -0,0 +1,101 @@
+#include <llvm-c/Analysis.h>
+#include <llvm-c/Core.h>
+#include <llvm-c/ExecutionEngine.h>
+#include "../internal.h"
+#include "../parsers/parser_internal.h"
+
+typedef struct HLLVMParser_ {
+  LLVMModuleRef mod;
+  LLVMValueRef func;
+  LLVMExecutionEngineRef engine;
+  LLVMBuilderRef builder;
+} HLLVMParser;
+
+int h_llvm_walk(const HParser* parser, LLVMBuilderRef builder, LLVMModuleRef mod) {
+  return 0;
+}
+
+void h_llvm_declare_common(LLVMModuleRef mod) {
+  LLVMTypeRef readbits_pt[] = {
+    LLVMPointerType(LLVMStructCreateNamed(LLVMGetGlobalContext(), "%struct.HInputStream_"), 0),
+    LLVMInt32Type(),
+    LLVMInt8Type()
+  };
+  LLVMTypeRef readbits_ret = LLVMFunctionType(LLVMInt64Type(), readbits_pt, 3, 0);
+  LLVMAddFunction(mod, "h_read_bits", readbits_ret);
+
+  LLVMTypeRef amalloc_pt[] = {
+    LLVMPointerType(LLVMStructCreateNamed(LLVMGetGlobalContext(), "%struct.HArena_"), 0),
+    LLVMInt32Type()
+  };
+  LLVMTypeRef amalloc_ret = LLVMFunctionType(LLVMPointerType(LLVMVoidType(), 0), amalloc_pt, 2, 0);
+  LLVMAddFunction(mod, "h_arena_malloc", amalloc_ret);
+
+  LLVMTypeRef makeresult_pt[] = {
+    LLVMPointerType(LLVMStructCreateNamed(LLVMGetGlobalContext(), "%struct.HArena_"), 0),
+    LLVMPointerType(LLVMStructCreateNamed(LLVMGetGlobalContext(), "%struct.HParsedToken_"), 0)
+  };
+  LLVMTypeRef makeresult_ret = LLVMFunctionType(LLVMPointerType(LLVMStructCreateNamed(LLVMGetGlobalContext(), "%struct.HParseResult_"), 0), makeresult_pt, 2, 0);
+  LLVMAddFunction(mod, "make_result", makeresult_ret);
+}
+
+int h_llvm_compile(HAllocator* mm__, HParser* parser, const void* params) {
+  const char* name = params ? (const char*)params : "parse";
+  LLVMModuleRef mod = LLVMModuleCreateWithName(name);
+  h_llvm_declare_common(mod);
+  // FIXME size_t isn't necessarily 32 bits everywhere
+  LLVMTypeRef param_types[] = { LLVMPointerType(LLVMInt8Type(), 0), LLVMInt32Type() };
+  LLVMTypeRef ret_type = LLVMFunctionType(LLVMPointerType(LLVMStructCreateNamed(LLVMGetGlobalContext(), "%struct.HParser_"), 0), param_types, 2, 0);
+  LLVMValueRef parse_func = LLVMAddFunction(mod, name, ret_type);
+  // function is now declared; time to define it
+  LLVMBasicBlockRef entry = LLVMAppendBasicBlock(parse_func, "entry");
+  LLVMBuilderRef builder = LLVMCreateBuilder();
+  LLVMPositionBuilderAtEnd(builder, entry);
+  if (0 == h_llvm_walk(parser, builder, mod)) {
+    char *error = NULL;
+    LLVMVerifyModule(mod, LLVMAbortProcessAction, &error);
+    LLVMDisposeMessage(error);
+    error = NULL;
+    LLVMInitializeNativeTarget();
+    LLVMLinkInMCJIT();
+    LLVMExecutionEngineRef engine = NULL;
+    LLVMCreateExecutionEngineForModule(&engine, mod, &error);
+    if (error) {
+      fprintf(stderr, "error: %s\n", error);
+      LLVMDisposeMessage(error);
+      return -1;
+    }
+    HLLVMParser *llvm_parser = h_new(HLLVMParser, 1);
+    llvm_parser->mod = mod;
+    llvm_parser->func = parse_func;
+    llvm_parser->engine = engine;
+    llvm_parser->builder = builder;
+    parser->backend_data = llvm_parser;
+    return 0;
+  } else {
+    return -1;
+  }
+}
+
+void h_llvm_free(HParser *parser) {
+  HLLVMParser *llvm_parser = parser->backend_data;
+  LLVMDisposeBuilder(llvm_parser->builder);
+  LLVMDisposeExecutionEngine(llvm_parser->engine);
+  LLVMDisposeModule(llvm_parser->mod);
+}
+
+HParseResult *h_llvm_parse(HAllocator* mm__, const HParser* parser, HInputStream *input_stream) {
+  const HLLVMParser *llvm_parser = parser->backend_data;
+  LLVMGenericValueRef args[] = {
+    LLVMCreateGenericValueOfPointer((uint8_t*)input_stream->input),
+    LLVMCreateGenericValueOfInt(LLVMInt32Type(), input_stream->length, 0)
+  };
+  LLVMGenericValueRef ret = LLVMRunFunction(llvm_parser->engine, llvm_parser->func, 2, args);
+  return (HParseResult*)LLVMGenericValueToPointer(ret);
+}
+
+HParserBackendVTable h__llvm_backend_vtable = {
+  .compile = h_llvm_compile,
+  .parse = h_llvm_parse,
+  .free = h_llvm_free
+};
diff --git a/src/hammer.c b/src/hammer.c
index 70ebc8a4943d8e1b3a25e036a745c2296bf8ddfd..2a7d5bc5be51059115b1af20a40c6a5de6633f2a 100644
--- a/src/hammer.c
+++ b/src/hammer.c
@@ -31,6 +31,7 @@ static HParserBackendVTable *backends[PB_MAX + 1] = {
   &h__llk_backend_vtable,
   &h__lalr_backend_vtable,
   &h__glr_backend_vtable,
+  &h__llvm_backend_vtable,
 };
 
 
diff --git a/src/hammer.h b/src/hammer.h
index 1be297c7a3b1230f2595ba47366a6591946b8777..f06728eb3120b3f1ed0341a6e6f26dc4ed8059de 100644
--- a/src/hammer.h
+++ b/src/hammer.h
@@ -46,7 +46,8 @@ typedef enum HParserBackend_ {
   PB_LLk,
   PB_LALR,
   PB_GLR,
-  PB_MAX = PB_GLR
+  PB_LLVM,
+  PB_MAX = PB_LLVM
 } HParserBackend;
 
 typedef enum HTokenType_ {
diff --git a/src/internal.h b/src/internal.h
index 776f636811183a4b3e19de9de8c8ce5b2b66f27d..6f71f916a8f0a1404b10c4d333bc6860882cc410 100644
--- a/src/internal.h
+++ b/src/internal.h
@@ -25,6 +25,7 @@
 #include <stdint.h>
 #include <assert.h>
 #include <string.h>
+#include <llvm-c/Core.h>
 #include "hammer.h"
 #include "platform.h"
 
@@ -322,6 +323,7 @@ extern HParserBackendVTable h__packrat_backend_vtable;
 extern HParserBackendVTable h__llk_backend_vtable;
 extern HParserBackendVTable h__lalr_backend_vtable;
 extern HParserBackendVTable h__glr_backend_vtable;
+extern HParserBackendVTable h__llvm_backend_vtable;
 // }}}
 
 // TODO(thequux): Set symbol visibility for these functions so that they aren't exported.
@@ -419,6 +421,7 @@ struct HParserVtable_ {
   bool (*isValidCF)(void *env);
   bool (*compile_to_rvm)(HRVMProg *prog, void* env); // FIXME: forgot what the bool return value was supposed to mean.
   void (*desugar)(HAllocator *mm__, HCFStack *stk__, void *env);
+  bool (*llvm)(LLVMBuilderRef builder, LLVMModuleRef mod, void *env);
   bool higher; // false if primitive
 };