Btw, I took some time to work on this further.  Only _very_ lightly
tested (make check passes)

# GC::Profiler.report doesn't even show anything with this patch applied
# because GC never happens.
h = {}
GC::Profiler.enable
10000000.times do
  h["HI"] = 0
  h["HI"]
end
GC::Profiler.report

---------------------------8<-------------------------------
Subject: [PATCH] prefreeze literal strings for hash aset/aref

Based on a patch by Charlie Somerville and Aman Gupta:

http://mid.gmane.org/redmine.journal-43505.20131208105635 / ruby-lang.org
---
  The following changes since commit 12b09864056bfb961f06b0ef675b9fc2fabb9238:

    * properties. (2014-01-03 01:51:05 +0000)

  are available in the git repository at:

    git://80x24.org/ruby.git opt_aref_aset_str

  for you to fetch changes up to 2906ef4bf2aaa0873f198cc1a949c1cc7740be7f:

    prefreeze literal strings for hash aset/aref (2014-01-03 03:44:51 +0000)

  ----------------------------------------------------------------
  Eric Wong (1):
        prefreeze literal strings for hash aset/aref

   compile.c              | 36 ++++++++++++++++++++++++++++++++++++
   hash.c                 |  2 +-
   insns.def              | 41 +++++++++++++++++++++++++++++++++++++++++
   test/ruby/test_hash.rb |  9 ++++++++-
   4 files changed, 86 insertions(+), 2 deletions(-)

diff --git a/compile.c b/compile.c
index 5b28401..f12f40d 100644
--- a/compile.c
+++ b/compile.c
@@ -4330,6 +4330,23 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
 	    }
 	    break;
 	}
+	if (node->nd_mid == idAREF &&
+	    node->nd_recv != (NODE *)1 &&
+	    node->nd_args &&
+	    nd_type(node->nd_args) == NODE_ARRAY &&
+	    node->nd_args->nd_alen == 1 &&
+	    nd_type(node->nd_args->nd_head) == NODE_STR)
+	{
+	    VALUE str = rb_fstring(node->nd_args->nd_head->nd_lit);
+	    node->nd_args->nd_head->nd_lit = str;
+	    COMPILE(ret, "recv", node->nd_recv);
+	    ADD_INSN2(ret, line, opt_aref_str,
+		      new_callinfo(iseq, idAREF, 1, 0, 0), str);
+	    if (poped) {
+		ADD_INSN(ret, line, pop);
+	    }
+	    break;
+	}
       case NODE_FCALL:
       case NODE_VCALL:{		/* VCALL: variable or call */
 	/*
@@ -5300,6 +5317,25 @@ iseq_compile_each(rb_iseq_t *iseq, LINK_ANCHOR *ret, NODE * node, int poped)
 	VALUE flag = 0;
 	VALUE argc;
 
+	if (node->nd_mid == idASET &&
+	    node->nd_recv != (NODE *)1 &&
+	    node->nd_args &&
+	    nd_type(node->nd_args) == NODE_ARRAY &&
+	    node->nd_args->nd_alen == 2 &&
+	    nd_type(node->nd_args->nd_head) == NODE_STR)
+	{
+	    VALUE str = rb_fstring(node->nd_args->nd_head->nd_lit);
+	    node->nd_args->nd_head->nd_lit = str;
+	    COMPILE(ret, "recv", node->nd_recv);
+	    COMPILE(ret, "value", node->nd_args->nd_next->nd_head);
+	    ADD_INSN2(ret, line, opt_aset_str,
+		      new_callinfo(iseq, idASET, 2, 0, 0), str);
+	    if (poped) {
+		ADD_INSN(ret, line, pop);
+	    }
+	    break;
+	}
+
 	INIT_ANCHOR(recv);
 	INIT_ANCHOR(args);
 	argc = setup_args(iseq, args, node->nd_args, &flag);
diff --git a/hash.c b/hash.c
index 0eca4b9..3cf7d8d 100644
--- a/hash.c
+++ b/hash.c
@@ -2390,7 +2390,7 @@ static VALUE rb_hash_compare_by_id_p(VALUE hash);
  *     h1["a"]        #=> 100
  *     h1.compare_by_identity
  *     h1.compare_by_identity? #=> true
- *     h1["a"]        #=> nil  # different objects.
+ *     h1["a".dup]    #=> nil  # different objects.
  *     h1[:c]         #=> "c"  # same symbols are all same.
  *
  */
diff --git a/insns.def b/insns.def
index ad4bba6..616838d 100644
--- a/insns.def
+++ b/insns.def
@@ -1903,6 +1903,47 @@ opt_aset
 
 /**
   @c optimize
+  @e recv[str] = set
+  @j 最適化された recv[str] = set。
+ */
+DEFINE_INSN
+opt_aset_str
+(CALL_INFO ci, VALUE key)
+(VALUE recv, VALUE val)
+(VALUE val)
+{
+    if (!SPECIAL_CONST_P(recv) && RBASIC_CLASS(recv) == rb_cHash && BASIC_OP_UNREDEFINED_P(BOP_ASET, HASH_REDEFINED_OP_FLAG)) {
+	rb_hash_aset(recv, key, val);
+    } else {
+	PUSH(recv);
+	PUSH(rb_str_resurrect(key));
+	PUSH(val);
+	CALL_SIMPLE_METHOD(recv);
+    }
+}
+
+/**
+  @c optimize
+  @e recv[str]
+  @j 最適化された recv[str]。
+ */
+DEFINE_INSN
+opt_aref_str
+(CALL_INFO ci, VALUE key)
+(VALUE recv)
+(VALUE val)
+{
+    if (!SPECIAL_CONST_P(recv) && RBASIC_CLASS(recv) == rb_cHash && BASIC_OP_UNREDEFINED_P(BOP_AREF, HASH_REDEFINED_OP_FLAG)) {
+	val = rb_hash_aref(recv, key);
+    } else {
+	PUSH(recv);
+	PUSH(rb_str_resurrect(key));
+	CALL_SIMPLE_METHOD(recv);
+    }
+}
+
+/**
+  @c optimize
   @e optimized length
   @j 最適化された recv.length()。
  */
diff --git a/test/ruby/test_hash.rb b/test/ruby/test_hash.rb
index 70c0442..f5af4dd 100644
--- a/test/ruby/test_hash.rb
+++ b/test/ruby/test_hash.rb
@@ -209,6 +209,13 @@ class TestHash < Test::Unit::TestCase
     assert_equal(256,     h[z])
   end
 
+  def test_ASET_fstring_key
+    a, b = {}, {}
+    a["abc"] = 1
+    b["abc"] = 1
+    assert_same a.keys[0], b.keys[0]
+  end
+
   def test_NEWHASH_fstring_key
     a = {"ABC" => :t}
     b = {"ABC" => :t}
@@ -946,7 +953,7 @@ class TestHash < Test::Unit::TestCase
     h = @cls[]
     h.compare_by_identity
     h["a"] = 1
-    h["a"] = 2
+    h["a".dup] = 2
     assert_equal(["a",1], h.assoc("a"))
   end