From e674e755dcf79cac58ab8d37048860c75ac40e52 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Henrik=20Grubbstr=C3=B6m=20=28Grubba=29?=
 <grubba@grubba.org>
Date: Sun, 27 Aug 2000 19:27:46 +0200
Subject: [PATCH] Fixed bug in dependency analyzer [Bug 106].

Rev: src/las.c:1.195
---
 src/las.c | 384 +++++++++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 337 insertions(+), 47 deletions(-)

diff --git a/src/las.c b/src/las.c
index 5797ebf94f..3410c3bb25 100644
--- a/src/las.c
+++ b/src/las.c
@@ -5,7 +5,7 @@
 \*/
 /**/
 #include "global.h"
-RCSID("$Id: las.c,v 1.194 2000/08/27 14:29:09 grubba Exp $");
+RCSID("$Id: las.c,v 1.195 2000/08/27 17:27:46 grubba Exp $");
 
 #include "language.h"
 #include "interpret.h"
@@ -1899,34 +1899,164 @@ void print_tree(node *n)
 
 
 /* The following routines needs much better commenting */
+/* They also need to support lexical scoping and external variables.
+ * /grubba 2000-08-27
+ */
+
+#if MAX_LOCAL > MAX_GLOBAL
+#define MAX_VAR	MAX_LOCAL
+#else /* MAX_LOCAL <= MAX_GLOBAL */
+#define MAX_VAR MAX_GLOBAL
+#endif /* MAX_LOCAL > MAX_GLOBAL */
+
+struct scope_info
+{
+  struct scope_info *next;
+  int scope_id;
+  char vars[MAX_VAR];
+};
+
 struct used_vars
 {
   int err;
-  char locals[MAX_LOCAL];
-  char globals[MAX_GLOBAL];
+  int ext_flags;
+  struct scope_info *locals;
+  struct scope_info *externals;
 };
 
 #define VAR_BLOCKED   0
 #define VAR_UNUSED    1
 #define VAR_USED      3
 
+/* FIXME: Shouldn't these two be named "or_vars"? */
+static void low_and_vars(struct scope_info **a, struct scope_info *b)
+{
+  while (*a && b) {
+    if ((*a)->scope_id < b->scope_id) {
+      a = &((*a)->next);
+    } else if ((*a)->scope_id > b->scope_id) {
+      struct scope_info *tmp = *a;
+      *a = b;
+      b = b->next;
+      (*a)->next = tmp;
+    } else {
+      struct scope_info *tmp = b;
+      int e;
+      for (e = 0; e < MAX_VAR; e++) {
+	(*a)->vars[e] |= b->vars[e];
+      }
+      a = &((*a)->next);
+      b = b->next;
+      free(tmp);
+    }
+  }
+  if (!*a) {
+    *a = b;
+  }
+}
+
+/* NOTE: The second argument will be freed. */
 static void do_and_vars(struct used_vars *a,struct used_vars *b)
 {
-  int e;
-  for(e=0;e<MAX_LOCAL;e++) a->locals[e]|=b->locals[e];
-  for(e=0;e<MAX_GLOBAL;e++) a->globals[e]|=b->globals[e];
-  a->err|=b->err;
-  free((char *)b);
+  low_and_vars(&(a->locals), b->locals);
+  low_and_vars(&(a->externals), b->externals);
+
+  a->err |= b->err;
+  a->ext_flags |= b->ext_flags;
+  free(b);
 }
 
 static struct used_vars *copy_vars(struct used_vars *a)
 {
   struct used_vars *ret;
+  struct scope_info *src;
+  struct scope_info **dst;
   ret=(struct used_vars *)xalloc(sizeof(struct used_vars));
-  MEMCPY((char *)ret,(char *)a,sizeof(struct used_vars));
+  src = a->locals;
+  dst = &(ret->locals);
+  *dst = NULL;
+  while (src) {
+    *dst = malloc(sizeof(struct scope_info));
+    if (!*dst) {
+      src = ret->locals;
+      while(src) {
+	struct scope_info *tmp = src->next;
+	free(src);
+	src = tmp;
+      }
+      free(ret);
+      error("Out of memory in copy_vars.\n");
+      return NULL;	/* Make sure that the optimizer knows we exit here. */
+    }
+    MEMCPY(*dst, src, sizeof(struct scope_info));
+    dst = &((*dst)->next);
+    *dst = NULL;
+  }
+  src = a->externals;
+  dst = &(ret->externals);
+  *dst = NULL;
+  while (src) {
+    *dst = malloc(sizeof(struct scope_info));
+    if (!*dst) {
+      src = ret->locals;
+      while(src) {
+	struct scope_info *tmp = src->next;
+	free(src);
+	src = tmp;
+      }
+      src = ret->externals;
+      while(src) {
+	struct scope_info *tmp = src->next;
+	free(src);
+	src = tmp;
+      }
+      free(ret);
+      error("Out of memory in copy_vars.\n");
+      return NULL;	/* Make sure that the optimizer knows we exit here. */
+    }
+    MEMCPY(*dst, src, sizeof(struct scope_info));
+    dst = &((*dst)->next);
+    *dst = NULL;
+  }
+  
+  ret->err = a->err;
+  ret->ext_flags = a->ext_flags;
   return ret;
 }
 
+char *find_q(struct scope_info **a, int num, int scope_id)
+{
+  struct scope_info *new;
+
+#ifdef PIKE_DEBUG
+  if (l_flag > 3) {
+    fprintf(stderr, "find_q %d:%d\n", scope_id, num);
+  }
+#endif /* PIKE_DEBUG */
+  while (*a && ((*a)->scope_id < scope_id)) {
+    a = &((*a)->next);
+  }
+  if ((*a) && ((*a)->scope_id == scope_id)) {
+#ifdef PIKE_DEBUG
+    if (l_flag > 4) {
+      fprintf(stderr, "scope found.\n");
+    }
+#endif /* PIKE_DEBUG */
+    return (*a)->vars + num;
+  }
+#ifdef PIKE_DEBUG
+  if (l_flag > 4) {
+    fprintf(stderr, "Creating new scope.\n");
+  }
+#endif /* PIKE_DEBUG */
+  new = (struct scope_info *)xalloc(sizeof(struct scope_info));
+  MEMSET(new, VAR_UNUSED, sizeof(struct scope_info));
+  new->next = *a;
+  new->scope_id = scope_id;
+  *a = new;
+  return new->vars + num;
+}
+
 static int find_used_variables(node *n,
 			       struct used_vars *p,
 			       int noblock,
@@ -1939,26 +2069,70 @@ static int find_used_variables(node *n,
   switch(n->token)
   {
   case F_LOCAL:
-    /* FIXME: handle local variable depth */
-    q=p->locals+n->u.integer.a;
+    q = find_q(&(p->locals), n->u.integer.a, n->u.integer.b);
+#ifdef PIKE_DEBUG
+    if (l_flag > 2) {
+      fprintf(stderr, "local %d:%d is ",
+	      n->u.integer.b, n->u.integer.a);
+    }
+#endif /* PIKE_DEBUG */
+    goto set_pointer;
+
+  case F_EXTERNAL:
+    q = find_q(&(p->externals), n->u.integer.b, n->u.integer.a);
+#ifdef PIKE_DEBUG
+    if (l_flag > 2) {
+      fprintf(stderr, "external %d:%d is ",
+	      n->u.integer.a, n->u.integer.b);
+    }
+#endif /* PIKE_DEBUG */
     goto set_pointer;
 
   case F_IDENTIFIER:
-    q=p->globals+n->u.id.number;
+    q = find_q(&(p->externals), n->u.id.number,
+	       Pike_compiler->new_program->id);
     if(n->u.id.number > MAX_GLOBAL)
     {
       p->err=1;
       return 0;
     }
+#ifdef PIKE_DEBUG
+      if (l_flag > 2) {
+	fprintf(stderr, "external %d:%d is ",
+		Pike_compiler->new_program->id, n->u.id.number);
+      }
+#endif /* PIKE_DEBUG */
 
   set_pointer:
     if(overwrite)
     {
-      if(*q == VAR_UNUSED && !noblock) *q = VAR_BLOCKED;
+      if(*q == VAR_UNUSED && !noblock) {
+	*q = VAR_BLOCKED;
+#ifdef PIKE_DEBUG
+	if (l_flag > 2) {
+	  fprintf(stderr, "blocked\n");
+	}
+      } else {
+	if (l_flag > 2) {
+	  fprintf(stderr, "overwritten\n");
+	}
+#endif /* PIKE_DEBUG */
+      }
     }
     else
     {
-      if(*q == VAR_UNUSED) *q = VAR_USED;
+      if(*q == VAR_UNUSED) {
+	*q = VAR_USED;
+#ifdef PIKE_DEBUG
+	if (l_flag > 2) {
+	  fprintf(stderr, "used\n");
+	}
+      } else {
+	if (l_flag > 2) {
+	  fprintf(stderr, "kept\n");
+	}
+#endif /* PIKE_DEBUG */
+      }
     }
     break;
 
@@ -1972,7 +2146,7 @@ static int find_used_variables(node *n,
     a=copy_vars(p);
     find_used_variables(CADR(n),a,noblock,0);
     find_used_variables(CDDR(n),p,noblock,0);
-    do_and_vars(p,a);
+    do_and_vars(p, a);
     break;
 
   case F_INC_NEQ_LOOP:
@@ -1984,20 +2158,20 @@ static int find_used_variables(node *n,
     find_used_variables(CAR(n),p,noblock,0);
     a=copy_vars(p);
     find_used_variables(CDR(n),a,noblock,0);
-    do_and_vars(p,a);
+    do_and_vars(p, a);
     break;
 
   case F_SWITCH:
     find_used_variables(CAR(n),p,noblock,0);
     a=copy_vars(p);
     find_used_variables(CDR(n),a,1,0);
-    do_and_vars(p,a);
+    do_and_vars(p, a);
     break;
 
    case F_DO:
     a=copy_vars(p);
     find_used_variables(CAR(n),a,noblock,0);
-    do_and_vars(p,a);
+    do_and_vars(p, a);
     find_used_variables(CDR(n),p,noblock,0);
     break;
 
@@ -2018,24 +2192,52 @@ static void find_written_vars(node *n,
   switch(n->token)
   {
   case F_LOCAL:
-    if(lvalue) p->locals[n->u.integer.a]=VAR_USED;
+    if(lvalue) {
+#ifdef PIKE_DEBUG
+      if (l_flag > 2) {
+	fprintf(stderr, "local %d:%d is written\n",
+		n->u.integer.b, n->u.integer.a);
+      }
+#endif /* PIKE_DEBUG */
+      *find_q(&(p->locals), n->u.integer.a, n->u.integer.b) = VAR_USED;
+    }
+    break;
+
+  case F_EXTERNAL:
+    if(lvalue) {
+#ifdef PIKE_DEBUG
+      if (l_flag > 2) {
+	fprintf(stderr, "external %d:%d is written\n",
+		n->u.integer.a, n->u.integer.b);
+      }
+#endif /* PIKE_DEBUG */
+      *find_q(&(p->externals), n->u.integer.b, n->u.integer.a) = VAR_USED;
+    }
     break;
 
   case F_IDENTIFIER:
-     if(lvalue)
-     {
-       if(n->u.id.number>=MAX_GLOBAL)
-       {
-	 p->err=1;
-	 return;
-       }
-       p->globals[n->u.id.number]=VAR_USED;
-     }
+    if(lvalue)
+    {
+      if(n->u.id.number >= MAX_VAR)
+      {
+	p->err=1;
+	return;
+      }
+#ifdef PIKE_DEBUG
+      if (l_flag > 2) {
+	fprintf(stderr, "external %d:%d is written\n",
+		Pike_compiler->new_program->id, n->u.id.number);
+      }
+#endif /* PIKE_DEBUG */
+      *find_q(&(p->externals), n->u.id.number,
+	      Pike_compiler->new_program->id) = VAR_USED;
+    }
     break;
 
   case F_APPLY:
-    if(n->tree_info & OPT_SIDE_EFFECT)
-      MEMSET(p->globals, VAR_USED, MAX_GLOBAL);
+    if(n->tree_info & OPT_SIDE_EFFECT) {
+      p->ext_flags = VAR_USED;
+    }
     break;
 
   case F_INDEX:
@@ -2095,32 +2297,120 @@ static void find_written_vars(node *n,
   }
 }
 
+void free_vars(struct used_vars *a)
+{
+  struct scope_info *tmp;
+
+  tmp = a->locals;
+  while(tmp) {
+    struct scope_info *next = tmp->next;
+    free(tmp);
+    tmp = next;
+  }
+  tmp = a->externals;
+  while(tmp) {
+    struct scope_info *next = tmp->next;
+    free(tmp);
+    tmp = next;
+  }
+}
+
 /* return 1 if A depends on B */
-static int depend_p2(node *a,node *b)
+static int depend_p2(node *a, node *b)
 {
-  struct used_vars aa,bb;
+  struct used_vars aa, bb;
   int e;
+  ONERROR free_aa;
+  ONERROR free_bb;
 
   if(!a || !b || is_const(a)) return 0;
-  aa.err=0;
-  bb.err=0;
-  MEMSET((char *)aa.locals, VAR_UNUSED, MAX_LOCAL);
-  MEMSET((char *)bb.locals, VAR_UNUSED,  MAX_LOCAL);
-  MEMSET((char *)aa.globals, VAR_UNUSED, MAX_GLOBAL);
-  MEMSET((char *)bb.globals, VAR_UNUSED, MAX_GLOBAL);
+  aa.err = 0;
+  bb.err = 0;
+  aa.ext_flags = 0;
+  bb.ext_flags = 0;
+  aa.locals = NULL;
+  bb.locals = NULL;
+  aa.externals = NULL;
+  bb.externals = NULL;
+
+  SET_ONERROR(free_aa, free_vars, &aa);
+  SET_ONERROR(free_bb, free_vars, &bb);
+
+  find_used_variables(a, &aa, 0, 0);
+  find_written_vars(b, &bb, 0);
+
+  UNSET_ONERROR(free_bb);
+  UNSET_ONERROR(free_aa);
+
+  if(aa.err || bb.err) {
+    free_vars(&aa);
+    free_vars(&bb);
+    return 1;
+  }
+
+  {
+    struct scope_info *aaa = aa.locals;
+    struct scope_info *bbb = bb.locals;
+
+    while (aaa) {
+      while (bbb && (bbb->scope_id < aaa->scope_id)) {
+	bbb = bbb->next;
+      }
+      if (!bbb) break;
+      if (bbb->scope_id == aaa->scope_id) {
+	for (e = 0; e < MAX_VAR; e++) {
+	  if ((aaa->vars[e] == VAR_USED) &&
+	      (bbb->vars[e] != VAR_UNUSED)) {
+	    free_vars(&aa);
+	    free_vars(&bb);
+	    return 1;
+	  }	  
+	}
+      }
+      aaa = aaa->next;
+    }
+  }
 
-  find_used_variables(a,&aa,0,0);
-  find_written_vars(b,&bb,0);
+  if (bb.ext_flags == VAR_USED) {
+    /* No need to look closer at b */
+    struct scope_info *aaa = aa.externals;
 
-  if(aa.err || bb.err) return 1;
+    /* FIXME: Probably only needed to check if aaa is NULL or not. */
 
-  for(e=0;e<MAX_LOCAL;e++)
-    if(aa.locals[e]==VAR_USED && bb.locals[e]!=VAR_UNUSED)
-      return 1;
+    while (aaa) {
+      for (e = 0; e < MAX_VAR; e++) {
+	if (aaa->vars[e] == VAR_USED) {
+	  free_vars(&aa);
+	  free_vars(&bb);
+	  return 1;
+	}
+      }
+      aaa = aaa->next;
+    }    
+  } else {
+    struct scope_info *aaa = aa.externals;
+    struct scope_info *bbb = bb.externals;
 
-  for(e=0;e<MAX_GLOBAL;e++)
-    if(aa.globals[e]==VAR_USED && bb.globals[e]!=VAR_UNUSED)
-      return 1;
+    while (aaa) {
+      while (bbb && (bbb->scope_id < aaa->scope_id)) {
+	bbb = bbb->next;
+      }
+      if (!bbb) break;
+      if (bbb->scope_id == aaa->scope_id) {
+	for (e = 0; e < MAX_VAR; e++) {
+	  if ((aaa->vars[e] == VAR_USED) &&
+	      (bbb->vars[e] != VAR_UNUSED)) {
+	    free_vars(&aa);
+	    free_vars(&bb);
+	    return 1;
+	  }	  
+	}
+      }
+      aaa = aaa->next;
+    }
+  }
+  free_vars(&aa);
+  free_vars(&bb);
   return 0;
 }
 
-- 
GitLab