diff --git a/src/builtin_functions.c b/src/builtin_functions.c
index 7353f968834c9e254fa3ff6f06dd5dfc3b20913d..a3fd4aacac254ee37ea633026acdb6a5af3d14ef 100644
--- a/src/builtin_functions.c
+++ b/src/builtin_functions.c
@@ -2,7 +2,7 @@
 || This file is part of Pike. For copyright information see COPYRIGHT.
 || Pike is distributed under GPL, LGPL and MPL. See the file COPYING
 || for more information.
-|| $Id: builtin_functions.c,v 1.613 2006/03/11 13:23:44 grubba Exp $
+|| $Id: builtin_functions.c,v 1.614 2006/03/11 17:19:46 grubba Exp $
 */
 
 #include "global.h"
@@ -3269,14 +3269,6 @@ PMOD_EXPORT void f_reverse(INT32 args)
   }
 }
 
-struct tupel
-{
-  int prefix;
-  int is_prefix;
-  struct pike_string *ind;
-  struct pike_string *val;
-};
-
 /* Magic, magic and more magic */
 /* Returns the index in v for the string that is the longest prefix of
  * str (if any).
@@ -3287,12 +3279,12 @@ struct tupel
  * a is the lower bound.
  * b is the upper bound + 1.
  */
-static int find_longest_prefix(char *str,
-			       ptrdiff_t len,
-			       int size_shift,
-			       struct tupel *v,
-			       INT32 a,
-			       INT32 b)
+int find_longest_prefix(char *str,
+			ptrdiff_t len,
+			int size_shift,
+			struct replace_many_tupel *v,
+			INT32 a,
+			INT32 b)
 {
   INT32 c, match=-1, match_len=-1, d;
   ptrdiff_t tmp;
@@ -3357,126 +3349,116 @@ static int find_longest_prefix(char *str,
 }
 			       
 
-static int replace_sortfun(struct tupel *a,struct tupel *b)
+static int replace_sortfun(struct replace_many_tupel *a,
+			   struct replace_many_tupel *b)
 {
   return DO_NOT_WARN((int)my_quick_strcmp(a->ind, b->ind));
 }
 
-struct replace_many_context
+void free_replace_many_context(struct replace_many_context *ctx)
 {
-  struct string_builder ret;
-  struct tupel *v;
-};
-
-static void free_replace_many_context (struct replace_many_context *ctx)
-{
-  free_string_builder (&ctx->ret);
-  free ((char *) ctx->v);
+  if (ctx->v) {
+    if (ctx->flags) {
+      /* Used for the precompiled case. */
+      int e = ctx->num;
+      while (e--) {
+	free_string(ctx->v[e].ind);
+	free_string(ctx->v[e].val);
+      }
+      if (ctx->empty_repl) {
+	free_string(ctx->empty_repl);
+      }
+    }
+    free ((char *) ctx->v);
+    ctx->v = NULL;
+  }
 }
 
-static struct pike_string *replace_many(struct pike_string *str,
-					struct array *from,
-					struct array *to)
+void compile_replace_many(struct replace_many_context *ctx,
+			  struct array *from,
+			  struct array *to,
+			  int reference_strings)
 {
   INT32 e, num;
-  ptrdiff_t s, length;
-  struct replace_many_context ctx;
-  ONERROR uwp;
-  struct pike_string *empty_repl = NULL;
-
-  int set_start[256];
-  int set_end[256];
-  int other_start;
-
-  if(from->size != to->size)
-    Pike_error("Replace must have equal-sized from and to arrays.\n");
-
-  if(!from->size)
-  {
-    reference_shared_string(str);
-    return str;
-  }
-
-  if( (from->type_field & ~BIT_STRING) &&
-      (array_fix_type_field(from) & ~BIT_STRING) )
-    Pike_error("replace: from array not array(string).\n");
-
-  if( (to->type_field & ~BIT_STRING) &&
-      (array_fix_type_field(to) & ~BIT_STRING) )
-    Pike_error("replace: to array not array(string).\n");
 
-  if (from->size == 1) {
-    /* Just a single string... */
-    return string_replace(str, from->item[0].u.string, to->item[0].u.string);
-  }
+  ctx->v = NULL;
+  ctx->empty_repl = NULL;
 
   /* NOTE: The following test is needed, since sizeof(struct tupel)
    *       is somewhat greater than sizeof(struct svalue).
    */
-  if (from->size > (ptrdiff_t)(LONG_MAX/sizeof(struct tupel)))
+  if (from->size > (ptrdiff_t)(LONG_MAX/sizeof(struct replace_many_tupel)))
     Pike_error("Array too large (size %" PRINTPTRDIFFT "d "
 	       "exceeds %" PRINTSIZET "u).\n",
-	       from->size, (size_t)(LONG_MAX/sizeof(struct tupel)));
-  ctx.v=(struct tupel *)xalloc(sizeof(struct tupel)*from->size);
-  init_string_builder(&ctx.ret,str->size_shift);
-  SET_ONERROR (uwp, free_replace_many_context, &ctx);
+	       from->size,
+	       (size_t)(LONG_MAX/sizeof(struct replace_many_tupel)));
+  ctx->v = (struct replace_many_tupel *)
+    xalloc(sizeof(struct replace_many_tupel) * from->size);
 
   for(num=e=0;e<from->size;e++)
   {
-    if(ITEM(from)[e].u.string->size_shift > str->size_shift)
-      continue;
-
     if (!ITEM(from)[e].u.string->len) {
       if (ITEM(to)[e].u.string->len) {
-	empty_repl = ITEM(to)[e].u.string;
+	ctx->empty_repl = ITEM(to)[e].u.string;
       }
       continue;
     }
 
-    ctx.v[num].ind=ITEM(from)[e].u.string;
-    ctx.v[num].val=ITEM(to)[e].u.string;
-    ctx.v[num].prefix=-2; /* Uninitialized */
-    ctx.v[num].is_prefix=0;
+    ctx->v[num].ind=ITEM(from)[e].u.string;
+    ctx->v[num].val=ITEM(to)[e].u.string;
+    ctx->v[num].prefix=-2; /* Uninitialized */
+    ctx->v[num].is_prefix=0;
     num++;
   }
 
-  fsort((char *)ctx.v,num,sizeof(struct tupel),(fsortfun)replace_sortfun);
+  ctx->flags = reference_strings;
+  if (reference_strings) {
+    /* Used for the precompiled compiled case. */
+    if (ctx->empty_repl) add_ref(ctx->empty_repl);
+    for (e = 0; e < num; e++) {
+      add_ref(ctx->v[e].ind);
+      add_ref(ctx->v[e].val);
+    }
+  }
 
-  MEMSET(set_start, 0, sizeof(set_start));
-  MEMSET(set_end, 0, sizeof(set_end));
-  other_start = num;
+  fsort((char *)ctx->v, num, sizeof(struct replace_many_tupel),
+	(fsortfun)replace_sortfun);
+
+  MEMSET(ctx->set_start, 0, sizeof(ctx->set_start));
+  MEMSET(ctx->set_end, 0, sizeof(ctx->set_end));
+  ctx->other_start = num;
 
   for(e=0;e<num;e++)
   {
     {
       p_wchar2 x;
 
-      if (ctx.v[num-1-e].ind->len) {
-	x=index_shared_string(ctx.v[num-1-e].ind,0);
-	if (x<NELEM(set_start))
-	  set_start[x]=num-e-1;
+      if (ctx->v[num-1-e].ind->len) {
+	x=index_shared_string(ctx->v[num-1-e].ind,0);
+	if (x<NELEM(ctx->set_start))
+	  ctx->set_start[x]=num-e-1;
 	else
-	  other_start = num-e-1;
+	  ctx->other_start = num-e-1;
       }
 
-      if (ctx.v[e].ind->len) {
-	x=index_shared_string(ctx.v[e].ind,0);
-	if (x<NELEM(set_end))
-	  set_end[x]=e+1;
+      if (ctx->v[e].ind->len) {
+	x=index_shared_string(ctx->v[e].ind,0);
+	if (x<NELEM(ctx->set_end))
+	  ctx->set_end[x]=e+1;
       }
     }
     {
       INT32 prefix = e-1;
       if (prefix >= 0) {
 	ptrdiff_t tmp =
-	  generic_find_binary_prefix(ctx.v[e].ind->str,
-				     ctx.v[e].ind->len,
-				     ctx.v[e].ind->size_shift,
-				     ctx.v[prefix].ind->str,
-				     ctx.v[prefix].ind->len,
-				     ctx.v[prefix].ind->size_shift);
+	  generic_find_binary_prefix(ctx->v[e].ind->str,
+				     ctx->v[e].ind->len,
+				     ctx->v[e].ind->size_shift,
+				     ctx->v[prefix].ind->str,
+				     ctx->v[prefix].ind->len,
+				     ctx->v[prefix].ind->size_shift);
 	if (!tmp) {
-	  /* ctx.v[prefix] is a valid prefix to ctx.v[e]. */
+	  /* ctx->v[prefix] is a valid prefix to ctx->v[e]. */
 	} if (tmp == 1) {
 	  /* Optimization. */
 	  prefix = -1;
@@ -3489,19 +3471,28 @@ static struct pike_string *replace_many(struct pike_string *str,
 	   * the initial strings differed.
 	   */
 	  while (prefix >= 0) {
-	    if (ctx.v[prefix].ind->len < tmp) break;
-	    prefix = ctx.v[prefix].prefix;
+	    if (ctx->v[prefix].ind->len < tmp) break;
+	    prefix = ctx->v[prefix].prefix;
 	  }
 	}
 	if (prefix >= 0) {
-	  ctx.v[prefix].is_prefix = 1;
+	  ctx->v[prefix].is_prefix = 1;
 	}
       }
-      ctx.v[e].prefix = prefix;
+      ctx->v[e].prefix = prefix;
     }
   }
+  ctx->num = num;
+}
 
-  length=str->len;
+struct pike_string *execute_replace_many(struct replace_many_context *ctx,
+					 struct pike_string *str)
+{
+  struct string_builder ret;
+  ONERROR uwp;
+
+  init_string_builder(&ret, str->size_shift);
+  SET_ONERROR(uwp, free_string_builder, &ret);
 
   /* FIXME: We really ought to build a trie! */
 
@@ -3511,6 +3502,7 @@ static struct pike_string *replace_many(struct pike_string *str,
       {							\
 	PIKE_CONCAT(p_wchar, SZ) *ss =			\
 	  PIKE_CONCAT(STR, SZ)(str);			\
+	ptrdiff_t e, s, length = str->len;		\
 	for(e = s = 0;length > 0;)			\
 	{						\
 	  INT32 a, b;					\
@@ -3518,13 +3510,13 @@ static struct pike_string *replace_many(struct pike_string *str,
 							\
 	  ch = ss[s];					\
 	  if(OPT_IS_CHAR(ch)) {				\
-	    b=set_end[ch];				\
+	    b = ctx->set_end[ch];			\
 	    if (!b)					\
 	      goto PIKE_CONCAT(next_char, SZ);		\
-	    a=set_start[ch];				\
+	    a = ctx->set_start[ch];			\
 	  } else {					\
-	    b=num;					\
-	    a=other_start;				\
+	    b = ctx->num;				\
+	    a = ctx->other_start;			\
 	  }						\
 	  if (a >= b)					\
 	    goto PIKE_CONCAT(next_char, SZ);		\
@@ -3532,25 +3524,25 @@ static struct pike_string *replace_many(struct pike_string *str,
 	  a = find_longest_prefix((char *)(ss + s),	\
 				  length,		\
 				  SZ,			\
-				  ctx.v, a, b);		\
+				  ctx->v, a, b);	\
 							\
 	  if(a >= 0)					\
 	  {						\
 	    if (s != e) {				\
 	      PIKE_CONCAT(string_builder_binary_strcat,	\
-			  SZ)(&ctx.ret, ss+e, s-e);	\
+			  SZ)(&ret, ss+e, s-e);		\
 	    }						\
-	    ch = ctx.v[a].ind->len;			\
+	    ch = ctx->v[a].ind->len;			\
 	    s += ch;					\
 	    length -= ch;				\
 	    e = s;					\
-	    string_builder_shared_strcat(&ctx.ret,	\
-					 ctx.v[a].val);	\
-	    if (empty_repl && length) {			\
+	    string_builder_shared_strcat(&ret,		\
+					 ctx->v[a].val);	\
+	    if (ctx->empty_repl && length) {		\
 	      /* Append the replacement for		\
 	       * the empty string too. */		\
-	      string_builder_shared_strcat(&ctx.ret,	\
-					   empty_repl);	\
+	      string_builder_shared_strcat(&ret,	\
+					   ctx->empty_repl);	\
 	    }						\
 	    continue;					\
 	  }						\
@@ -3558,35 +3550,74 @@ static struct pike_string *replace_many(struct pike_string *str,
 	PIKE_CONCAT(next_char, SZ):			\
 	  s++;						\
 	  length--;					\
-	  if (empty_repl && length) {			\
+	  if (ctx->empty_repl && length) {		\
 	    /* We have a replace with the empty string,	\
 	     * and we're not on the last character	\
 	     * in the source string.			\
 	     */						\
-	    string_builder_putchar(&ctx.ret, ch);	\
-	    string_builder_shared_strcat(&ctx.ret,	\
-					 empty_repl);	\
+	    string_builder_putchar(&ret, ch);		\
+	    string_builder_shared_strcat(&ret,		\
+					 ctx->empty_repl);	\
 	    e = s;					\
 	  }						\
 	}						\
 	if (e < s) {					\
 	  PIKE_CONCAT(string_builder_binary_strcat, SZ)	\
-	    (&ctx.ret, ss+e, s-e);			\
+	    (&ret, ss+e, s-e);				\
 	}						\
       }							\
     break
 #define OPT_IS_CHAR(X)	1
     CASE(0);
 #undef OPT_IS_CHAR
-#define OPT_IS_CHAR(X)	((X) < NELEM(set_end))
+#define OPT_IS_CHAR(X)	((X) < NELEM(ctx->set_end))
     CASE(1);
     CASE(2);
 #undef OPT_IS_CHAR
   }
 
-  UNSET_ONERROR (uwp);
-  free((char *)ctx.v);
-  return finish_string_builder(&ctx.ret);
+  UNSET_ONERROR(uwp);
+  return finish_string_builder(&ret);
+}
+
+static struct pike_string *replace_many(struct pike_string *str,
+					struct array *from,
+					struct array *to)
+{
+  struct replace_many_context ctx;
+  ONERROR uwp;
+  struct pike_string *ret;
+
+  if(from->size != to->size)
+    Pike_error("Replace must have equal-sized from and to arrays.\n");
+
+  if(!from->size)
+  {
+    reference_shared_string(str);
+    return str;
+  }
+
+  if( (from->type_field & ~BIT_STRING) &&
+      (array_fix_type_field(from) & ~BIT_STRING) )
+    Pike_error("replace: from array not array(string).\n");
+
+  if( (to->type_field & ~BIT_STRING) &&
+      (array_fix_type_field(to) & ~BIT_STRING) )
+    Pike_error("replace: to array not array(string).\n");
+
+  if (from->size == 1) {
+    /* Just a single string... */
+    return string_replace(str, from->item[0].u.string, to->item[0].u.string);
+  }
+
+  compile_replace_many(&ctx, from, to, 0);
+  SET_ONERROR(uwp, free_replace_many_context, &ctx);
+
+  ret = execute_replace_many(&ctx, str);
+
+  CALL_AND_UNSET_ONERROR(uwp);
+
+  return ret;
 }
 
 /*! @decl string replace(string s, string from, string to)
@@ -3731,10 +3762,18 @@ node *optimize_replace(node *n)
     node **arg1 = my_get_arg(&_CDR(n), 1);
     node **arg2 = my_get_arg(&_CDR(n), 2);
 
-    if (arg1 && pike_types_le((*arg1)->type, array_type_string) &&
-	arg2 && pike_types_le((*arg2)->type, array_type_string)) {
-      /* The second and third arguments are arrays. */
-      if (!is_const(*arg0) && is_const(*arg1) && is_const(*arg2)) {
+    if (arg1 && ((pike_types_le((*arg1)->type, array_type_string) &&
+		  arg2 &&
+		  (pike_types_le((*arg2)->type, array_type_string) ||
+		   pike_types_le((*arg2)->type, string_type_string))) ||
+		 (pike_types_le((*arg1)->type, mapping_type_string)))) {
+      /* Handle the cases:
+       *
+       *   replace(string, array, array)
+       *   replace(string, array, string)
+       *   replace(string, mapping(string:string))
+       */
+      if (!is_const(*arg0) && is_const(*arg1) && (!arg2 || is_const(*arg2))) {
 	/* The second and third arguments are constants. */
 	struct svalue *save_sp = Pike_sp;
 	JMP_BUF tmp;
@@ -3755,8 +3794,11 @@ node *optimize_replace(node *n)
 	  INT16 lfun;
 	  struct object *replace_obj;
 	  node *ret = NULL;
-	  INT32 args = eval_low(*arg1,1);	/* NOTE: Addition splitted to ensure */
-	  args += eval_low(*arg2,1);		/*       correct evaluation order.   */
+	  INT32 args;
+	  args = eval_low(*arg1, 1);
+	  if (arg2) {
+	    args += eval_low(*arg2,1);
+	  }
 
 	  replace_obj = clone_object(multi_string_replace_program, args);
 
@@ -3775,7 +3817,6 @@ node *optimize_replace(node *n)
 
 	    free_type(array_zero);
 	    free_type(mapping_zero);
-
 	    return ret;
 	  }
 	}