diff --git a/SConstruct b/SConstruct
index fd1a7e3eb6c35e126fdd3361a175bd0dc6ee97a0..7979b3393d58e2fef09992d8626d3ba2960f6839 100644
--- a/SConstruct
+++ b/SConstruct
@@ -73,6 +73,12 @@ AddOption('--coverage',
           action='store_true',
           help='Build with coverage instrumentation')
 
+AddOption('--force-debug',
+          dest='force_debug',
+          default=False,
+          action='store_true',
+          help='Build with debug symbols, even in the opt variant')
+
 AddOption('--gprof',
           dest='gprof',
           default=False,
@@ -135,6 +141,12 @@ if GetOption('coverage'):
     else:
         env.ParseConfig('llvm-config --ldflags')
 
+if GetOption('force_debug'):
+    if env['CC'] == 'cl':
+        env.Append(CCFLAGS=['/Z7'])
+    else:
+        env.Append(CCFLAGS=['-g'])
+
 if GetOption('gprof'):
     if env['CC'] == 'gcc' and env['CXX'] == 'g++':
         env.Append(CCFLAGS=['-pg'],
diff --git a/src/backends/packrat.c b/src/backends/packrat.c
index 276dfd171f4c8a13ab68953a69f5bbd733c522ab..91e4a09224cb03c4eac23d1713dbbc70941d6618 100644
--- a/src/backends/packrat.c
+++ b/src/backends/packrat.c
@@ -3,6 +3,8 @@
 #include "../internal.h"
 #include "../parsers/parser_internal.h"
 
+static uint32_t cache_key_hash(const void* key);
+
 // short-hand for creating lowlevel parse cache values (parse result case)
 static
 HParserCacheValue * cached_result(HParseState *state, HParseResult *result) {
@@ -56,31 +58,38 @@ static inline HParseResult* perform_lowlevel_parse(HParseState *state, const HPa
   return tmp_res;
 }
 
-HParserCacheValue* recall(HParserCacheKey *k, HParseState *state) {
-  HParserCacheValue *cached = h_hashtable_get(state->cache, k);
+HParserCacheValue* recall(HParserCacheKey *k, HParseState *state, HHashValue keyhash) {
+  HParserCacheValue *cached = h_hashtable_get_precomp(state->cache, k, keyhash);
   HRecursionHead *head = h_hashtable_get(state->recursion_heads, &k->input_pos);
-  if (!head) { // No heads found
+
+  if (!head) {
+    /* No heads found */
     return cached;
-  } else { // Some heads found
+  } else {
+    /* Some heads found */
     if (!cached && head->head_parser != k->parser && !h_slist_find(head->involved_set, k->parser)) {
-      // Nothing in the cache, and the key parser is not involved
+      /* Nothing in the cache, and the key parser is not involved */
       cached = cached_result(state, NULL);
       cached->input_stream = k->input_pos;
     }
     if (h_slist_find(head->eval_set, k->parser)) {
-      // Something is in the cache, and the key parser is in the eval set. Remove the key parser from the eval set of the head. 
+      /*
+       * Something is in the cache, and the key parser is in the eval set.
+       * Remove the key parser from the eval set of the head.
+       */
       head->eval_set = h_slist_remove_all(head->eval_set, k->parser);
       HParseResult *tmp_res = perform_lowlevel_parse(state, k->parser);
-      // update the cache
+      /* update the cache */
       if (!cached) {
-	cached = cached_result(state, tmp_res);
-	h_hashtable_put(state->cache, k, cached);
+        cached = cached_result(state, tmp_res);
+        h_hashtable_put_precomp(state->cache, k, cached, keyhash);
       } else {
-	cached->value_type = PC_RIGHT;
-	cached->right = tmp_res;
-	cached->input_stream = state->input_stream;
+        cached->value_type = PC_RIGHT;
+        cached->right = tmp_res;
+        cached->input_stream = state->input_stream;
       }
     }
+
     return cached;
   }
 }
@@ -180,36 +189,50 @@ HParseResult* lr_answer(HParserCacheKey *k, HParseState *state, HLeftRec *growab
 /* Warth's recursion. Hi Alessandro! */
 HParseResult* h_do_parse(const HParser* parser, HParseState *state) {
   HParserCacheKey *key = a_new(HParserCacheKey, 1);
+  HHashValue keyhash;
+  HLeftRec *base = NULL;
+  HParserCacheValue *m = NULL, *cached = NULL;
+
   key->input_pos = state->input_stream; key->parser = parser;
-  HParserCacheValue *m = NULL;
+  keyhash = cache_key_hash(key);
+
   if (parser->vtable->higher) {
-    m = recall(key, state);
+    m = recall(key, state, keyhash);
   }
-  // check to see if there is already a result for this object...
+
+  /* check to see if there is already a result for this object... */
   if (!m) {
-    // It doesn't exist, so create a dummy result to cache
-    HLeftRec *base = NULL;
-    // But only cache it now if there's some chance it could grow; primitive parsers can't
+    /*
+     * But only cache it now if there's some chance it could grow; primitive
+     * parsers can't
+     */
     if (parser->vtable->higher) {
       base = a_new(HLeftRec, 1);
       base->seed = NULL; base->rule = parser; base->head = NULL;
       h_slist_push(state->lr_stack, base);
-      // cache it
-      h_hashtable_put(state->cache, key, cached_lr(state, base));
-      // parse the input
+      /* cache it */
+      h_hashtable_put_precomp(state->cache, key,
+                              cached_lr(state, base), keyhash);
     }
+
+    /* parse the input */
     HParseResult *tmp_res = perform_lowlevel_parse(state, parser);
     if (parser->vtable->higher) {
-      // the base variable has passed equality tests with the cache
+      /* the base variable has passed equality tests with the cache */
       h_slist_pop(state->lr_stack);
-      // update the cached value to our new position
-      HParserCacheValue *cached = h_hashtable_get(state->cache, key);
+      /* update the cached value to our new position */
+      cached = h_hashtable_get_precomp(state->cache, key, keyhash);
       assert(cached != NULL);
       cached->input_stream = state->input_stream;
     }
-    // setupLR, used below, mutates the LR to have a head if appropriate, so we check to see if we have one
+
+    /*
+     * setupLR, used below, mutates the LR to have a head if appropriate,
+     * so we check to see if we have one
+     */
     if (!base || NULL == base->head) {
-      h_hashtable_put(state->cache, key, cached_result(state, tmp_res));
+      h_hashtable_put_precomp(state->cache, key,
+                              cached_result(state, tmp_res), keyhash);
       return tmp_res;
     } else {
       base->seed = tmp_res;
@@ -217,7 +240,7 @@ HParseResult* h_do_parse(const HParser* parser, HParseState *state) {
       return res;
     }
   } else {
-    // it exists!
+    /* it exists! */
     state->input_stream = m->input_stream;
     if (PC_LEFT == m->value_type) {
       setupLR(parser, state, m->left);
diff --git a/src/datastructures.c b/src/datastructures.c
index 451afb94ec39932dfe1f8c58aa82c0777f73b011..99d821f1a93e8fbfa2189a581b6fd59afb367bc7 100644
--- a/src/datastructures.c
+++ b/src/datastructures.c
@@ -149,13 +149,14 @@ HHashTable* h_hashtable_new(HArena *arena, HEqualFunc equalFunc, HHashFunc hashF
   return ht;
 }
 
-void* h_hashtable_get(const HHashTable* ht, const void* key) {
-  HHashValue hashval = ht->hashFunc(key);
+void * h_hashtable_get_precomp(const HHashTable *ht, const void *key,
+                               HHashValue hashval) {
+  HHashTableEntry *hte = NULL;
+
 #ifdef CONSISTENCY_CHECK
   assert((ht->capacity & (ht->capacity - 1)) == 0); // capacity is a power of 2
 #endif
 
-  HHashTableEntry *hte = NULL;
   for (hte = &ht->contents[hashval & (ht->capacity - 1)];
        hte != NULL;
        hte = hte->next) {
@@ -169,9 +170,16 @@ void* h_hashtable_get(const HHashTable* ht, const void* key) {
       return hte->value;
     }
   }
+
   return NULL;
 }
 
+void * h_hashtable_get(const HHashTable *ht, const void *key) {
+  HHashValue hashval = ht->hashFunc(key);
+
+  return h_hashtable_get_precomp(ht, key, hashval);
+}
+
 void h_hashtable_put_raw(HHashTable* ht, HHashTableEntry* new_entry);
 
 void h_hashtable_ensure_capacity(HHashTable* ht, size_t n) {
@@ -197,7 +205,21 @@ void h_hashtable_ensure_capacity(HHashTable* ht, size_t n) {
   //h_arena_free(ht->arena, old_contents);
 }
 
-void h_hashtable_put(HHashTable* ht, const void* key, void* value) {
+void h_hashtable_put_precomp(HHashTable *ht, const void *key, void *value,
+                             HHashValue hashval) {
+  HHashTableEntry entry = {
+    .key = key,
+    .value = value,
+    .hashval = hashval
+  };
+
+  /* Rebalance if necessary */
+  h_hashtable_ensure_capacity(ht, ht->used + 1);
+  /* Insert it */
+  h_hashtable_put_raw(ht, &entry);
+}
+
+void h_hashtable_put(HHashTable *ht, const void *key, void *value) {
   // # Start with a rebalancing
   h_hashtable_ensure_capacity(ht, ht->used + 1);
 
diff --git a/src/internal.h b/src/internal.h
index 324fcbafc5ef7601fac70ceaea04894b8d46010d..07420681275a989925a08f6c596e3bc4a59202c1 100644
--- a/src/internal.h
+++ b/src/internal.h
@@ -367,16 +367,21 @@ HSlist* h_slist_remove_all(HSlist *slist, const void* item);
 void h_slist_free(HSlist *slist);
 static inline bool h_slist_empty(const HSlist *sl) { return (sl->head == NULL); }
 
-HHashTable* h_hashtable_new(HArena *arena, HEqualFunc equalFunc, HHashFunc hashFunc);
-void* h_hashtable_get(const HHashTable* ht, const void* key);
-void  h_hashtable_put(HHashTable* ht, const void* key, void* value);
-void  h_hashtable_update(HHashTable* dst, const HHashTable *src);
-void  h_hashtable_merge(void *(*combine)(void *v1, const void *v2),
+HHashTable* h_hashtable_new(HArena *arena, HEqualFunc equalFunc,
+                            HHashFunc hashFunc);
+void * h_hashtable_get_precomp(const HHashTable *ht, const void *key,
+                               HHashValue hashval);
+void * h_hashtable_get(const HHashTable *ht, const void *key);
+void   h_hashtable_put_precomp(HHashTable *ht, const void *key,
+                               void *value, HHashValue hashval);
+void   h_hashtable_put(HHashTable *ht, const void *key, void *value);
+void   h_hashtable_update(HHashTable *dst, const HHashTable *src);
+void   h_hashtable_merge(void *(*combine)(void *v1, const void *v2),
                         HHashTable *dst, const HHashTable *src);
-int   h_hashtable_present(const HHashTable* ht, const void* key);
-void  h_hashtable_del(HHashTable* ht, const void* key);
-void  h_hashtable_free(HHashTable* ht);
-static inline bool h_hashtable_empty(const HHashTable* ht) { return (ht->used == 0); }
+int   h_hashtable_present(const HHashTable *ht, const void *key);
+void  h_hashtable_del(HHashTable *ht, const void *key);
+void  h_hashtable_free(HHashTable *ht);
+static inline bool h_hashtable_empty(const HHashTable *ht) { return (ht->used == 0); }
 
 typedef HHashTable HHashSet;
 #define h_hashset_new(a,eq,hash) h_hashtable_new(a,eq,hash)