File: idispatcher.c

package info (click to toggle)
psyco-doc 1.6-2
  • links: PTS
  • area: contrib
  • in suites: squeeze
  • size: 1,832 kB
  • ctags: 3,236
  • sloc: ansic: 23,895; python: 5,646; perl: 1,309; makefile: 153
file content (272 lines) | stat: -rw-r--r-- 9,438 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
#include "idispatcher.h"
#include "../dispatcher.h"
#include "../codemanager.h"
#include "ipyencoding.h"


/***************************************************************/
 /***   the hard processor-dependent part of dispatching:     ***/
  /***   Unification.                                          ***/

#define RUNTIME_STACK(v)     getstack((v)->source)
#define RUNTIME_STACK_NONE   RunTime_StackNone


struct dmove_s {
  PsycoObject* po;
  int original_stack_depth;
  char* usages;   /* buffer: array of vinfo_t*, see ORIGINAL_VINFO() below */
  int usages_size;
  code_t* code_origin;
  code_t* code_limit;
  code_t* code;   /* only used by data_update_stack() */
  CodeBufferObject* private_codebuf;
};

static code_t* data_new_buffer(code_t* code, struct dmove_s* dm)
{
  /* creates a new buffer containing a copy of the already-written code */
  CodeBufferObject* codebuf;
  int codesize;
  if (dm->private_codebuf != NULL)
    {
      /* overflowing the regular (large) code buffer */
      psyco_emergency_enlarge_buffer(&code, &dm->code_limit);
      return code;
    }
  else
    {
      /* overflowing the small buffer, start a new (regular) one */
      codebuf = psyco_new_code_buffer(NULL, NULL, &dm->code_limit);
      codebuf->snapshot.fz_stuff.fz_stack_depth = dm->original_stack_depth;
      /* the new buffer should be at least as large as the old one */
      codesize = code - dm->code_origin;
      code = insn_code_label(codebuf->codestart);
      if (code + codesize > dm->code_limit)
        Py_FatalError("psyco: unexpected unify buffer overflow");
      /* copy old code to new buffer */
      memcpy(code, dm->code_origin, codesize+POST_CODEBUFFER_SIZE);
      dm->private_codebuf = codebuf;
#if PSYCO_DEBUG
      dm->code_origin = (code_t*) 0xCDCDCDCD;
#endif
      return code + codesize;
    }
}

#define ORIGINAL_VINFO(spos)    (*(vinfo_t**)(dm->usages + (            \
		extra_assert(0 <= (spos) && (spos) < dm->usages_size),  \
                (spos))))

static void data_original_table(vinfo_t* a, RunTimeSource bsource,
                                struct dmove_s* dm)
{
  /* called on each run-time vinfo_t in the FrozenPsycoObject.
     Record in the array dm->usages which vinfo_t is found at what position
     in the stack. Ignore the ones after dm->usages_size: they correspond to
     stack positions which will soon be deleted (because the stack will
     shrink). */
  if (RUNTIME_STACK(a) < dm->usages_size)
    ORIGINAL_VINFO(RUNTIME_STACK(a)) = a;
}

static void data_update_stack(vinfo_t* a, RunTimeSource bsource,
                              struct dmove_s* dm)
{
  PsycoObject* po = dm->po;
  code_t* code = dm->code;
  long dststack = getstack(bsource);
  long srcstack = getstack(a->source);
  vinfo_t* overridden;
  RunTimeSource osrc;
  
  /* check for values passing from no-reference to reference */
  if ((bsource & RunTime_NoRef) == 0) {  /* destination has ref */
    if ((a->source & RunTime_NoRef) == 0)   /* source has ref too */
      {
        /* remove the reference from 'a' because it now belongs
           to 'b' ('b->source' itself is in the frozen snapshot
           and must not be modified!) */
        a->source = remove_rtref(a->source);
      }
    else
      {
        /* create a new reference for 'b'. Note that if the same
           'a' is copied to several 'b's during data_update_stack()
           as is allowed by graph quotient detection in
           psyco_compatible(), then only the first copy will get
           the original reference owned by 'a' (if any) and for
           the following copies the following increfing code is
           executed as well. */
        INSN_rt_push(a->source);
	INSN_incref();
      }
  }
  /* 'a' must no longer own a reference at this point.
     The case of 'b' wanting no reference but 'a' having one
     is forbidden by psyco_compatible() because decrefing 'a'
     would potentially leave a freed pointer in 'b'. */
  extra_assert(!has_rtref(a->source));

  /* The operation below is: copy the value currently held by 'a'
     into the stack position 'dststack'. */
  if (dststack == RUNTIME_STACK_NONE || dststack == srcstack)
    ;  /* nothing to do */
  else
    {
      /* is there already a pending value at 'dststack'? */
      overridden = ORIGINAL_VINFO(dststack);
      if (overridden == NULL || RUNTIME_STACK(overridden) != dststack)
        goto can_save_only; /* no -- just save the new value to 'dststack'.
                               The case RUNTIME_STACK(overridden) != dststack
                               corresponds to a vinfo_t which has been moved
                               elsewhere in the mean time. */
      
      /* yes -- careful! We have to save the current value of
         'dststack' before we can overwrite it. */
      osrc = overridden->source;
      INSN_rt_push(osrc);
      osrc = set_rtstack_to_none(osrc);
      INSNPUSHED(1);
      overridden->source = set_rtstack_to(osrc, po->stack_depth);
      
    can_save_only:
      /* copy 'a' to 'dststack' */
      INSN_rt_push(a->source); INSNPUSHED(1);
      INSN_rt_pop(bsource);    INSNPOPPED(1);

      /* Now 'a' is at 'dststack' */
      a->source = RunTime_New1(dststack, false, false);
      ORIGINAL_VINFO(dststack) = a; /* 'a' is now there */
      
      if (code > dm->code_limit)
        /* oops, buffer overflow. Start a new buffer */
        code = data_new_buffer(code, dm);
      
    }
  dm->code = code;
}

static code_t* data_free_unused(code_t* code, struct dmove_s* dm,
                                vinfo_array_t* aa)
{
  /* decref any object that would be present in 'po' but not at all in
     the snapshot. Note that it is uncommon that this function actually
     finds any unused object at all. */
  int i = aa->count;
  while (i--)
    {
      vinfo_t* a = aa->items[i];
      if (a != NULL)
        {
          if (has_rtref(a->source))
            {
              PsycoObject* po = dm->po;
              code_t* saved_code;
              a->source = remove_rtref(a->source);
              
              saved_code = po->code;
              po->code = code;
              psyco_decref_rt(po, a);
              code = po->code;
              po->code = saved_code;

              if (code > dm->code_limit)
                /* oops, buffer overflow. Start a new buffer */
                code = data_new_buffer(code, dm);
            }
          if (a->array != NullArray)
            code = data_free_unused(code, dm, a->array);
        }
    }
  return code;
}

DEFINEFN
code_t* psyco_unify(PsycoObject* po, vcompatible_t* lastmatch,
                    CodeBufferObject** target)
{
  /* Update 'po' to match 'lastmatch', then jump to 'lastmatch'. */

  struct dmove_s dm;
  code_t* code = po->code;
  CodeBufferObject* target_codebuf = lastmatch->matching;
  int sdepth = get_stack_depth(&target_codebuf->snapshot);
#if PSYCO_DEBUG
  bool has_ccreg = HAS_CCREG(po);
#endif

  extra_assert(lastmatch->diff == NullArray);  /* unify with exact match only */
  psyco_assert_coherent(po);
  dm.usages_size = sdepth + sizeof(vinfo_t**);
  dm.usages = (char*) PyMem_MALLOC(dm.usages_size);
  if (dm.usages == NULL)
    OUT_OF_MEMORY();
  memset(dm.usages, 0, dm.usages_size);   /* set to all NULL */
  fz_find_runtimes(&po->vlocals, &target_codebuf->snapshot,
                   (fz_find_fn) &data_original_table,
                   &dm, false);

  dm.po = po;
  dm.original_stack_depth = po->stack_depth;
  dm.code_origin = code;
  dm.code_limit = po->codelimit == NULL ? code : po->codelimit;
  dm.private_codebuf = NULL;

  if (sdepth > po->stack_depth)
    {
      /* more items in the target stack (uncommon case).
         Let the stack grow. */
      STACK_CORRECTION(sdepth - po->stack_depth);
      po->stack_depth = sdepth;
    }

  /* update the stack */
  dm.code = code;
  fz_find_runtimes(&po->vlocals, &target_codebuf->snapshot,
                   (fz_find_fn) &data_update_stack,
                   &dm, true);
  code = dm.code;

  /* decref any object that would be present in 'po' but not at all in
     the snapshot (data_update_stack() has removed the 'ref' tag of all
     vinfo_ts it actually used from 'po') */
  code = data_free_unused(code, &dm, &po->vlocals);

  /* done */
  STACK_CORRECTION(sdepth - po->stack_depth);
  if (code > dm.code_limit)  /* start a new buffer if we wrote past the end */
    code = data_new_buffer(code, &dm);
#if PSYCO_DEBUG
  extra_assert(has_ccreg == HAS_CCREG(po));
#endif
  JUMP_TO((code_t*) target_codebuf->codestart);
  
  /* start a new buffer if the last JUMP_TO overflowed,
     but not if we had no room at all in the first place. */
  if (code > dm.code_limit && po->codelimit != NULL)
    {
      /* Note that the JMP instruction emitted by JUMP_TO() is
         position-independent (a property of the vm) */
      code = data_new_buffer(code, &dm);
      psyco_assert(code <= dm.code_limit);
    }
  
  PyMem_FREE(dm.usages);
  if (dm.private_codebuf == NULL)
    {
      Py_INCREF(target_codebuf);      /* no new buffer created */
      *target = target_codebuf;
    }
  else
    {
      SHRINK_CODE_BUFFER(dm.private_codebuf, code, "unify");
      *target = dm.private_codebuf;
      /* add a jump from the original code buffer to the new one */
      code = po->code;
      JUMP_TO((code_t*) dm.private_codebuf->codestart);
      dump_code_buffers();
    }
  PsycoObject_Delete(po);
  return code;
}