1// -*- c -*-
2//
3// %CopyrightBegin%
4//
5// Copyright Ericsson AB 2020. All Rights Reserved.
6//
7// Licensed under the Apache License, Version 2.0 (the "License");
8// you may not use this file except in compliance with the License.
9// You may obtain a copy of the License at
10//
11//     http://www.apache.org/licenses/LICENSE-2.0
12//
13// Unless required by applicable law or agreed to in writing, software
14// distributed under the License is distributed on an "AS IS" BASIS,
15// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16// See the License for the specific language governing permissions and
17// limitations under the License.
18//
19// %CopyrightEnd%
20//
21
22// Generate the fastest instruction to fetch an integer from a binary.
23gen.get_integer2(Fail, Ms, Live, Size, Unit, Flags, Dst) {
24    BeamOp* op;
25    UWord bits;
26
27    $NewBeamOp(S, op);
28    $NativeEndian(Flags);
29    if (Size.type == TAG_i) {
30        if (!beam_load_safe_mul(Size.val, Unit.val, &bits)) {
31            goto error;
32        } else if ((Flags.val & BSF_SIGNED) != 0) {
33            goto generic;
34        } else if (bits == 8) {
35            $BeamOpNameArity(op, i_bs_get_integer_8, 3);
36            op->a[0] = Ms;
37            op->a[1] = Fail;
38            op->a[2] = Dst;
39        } else if (bits == 16 && (Flags.val & BSF_LITTLE) == 0) {
40            $BeamOpNameArity(op, i_bs_get_integer_16, 3);
41            op->a[0] = Ms;
42            op->a[1] = Fail;
43            op->a[2] = Dst;
44#ifdef ARCH_64
45        } else if (bits == 32 && (Flags.val & BSF_LITTLE) == 0) {
46            $BeamOpNameArity(op, i_bs_get_integer_32, 3);
47            op->a[0] = Ms;
48            op->a[1] = Fail;
49            op->a[2] = Dst;
50#endif
51        } else {
52        generic:
53            if (bits < SMALL_BITS) {
54                $BeamOpNameArity(op, i_bs_get_integer_small_imm, 5);
55                op->a[0] = Ms;
56                op->a[1].type = TAG_u;
57                op->a[1].val = bits;
58                op->a[2] = Fail;
59                op->a[3] = Flags;
60                op->a[4] = Dst;
61            } else {
62                $BeamOpNameArity(op, i_bs_get_integer_imm, 6);
63                op->a[0] = Ms;
64                op->a[1].type = TAG_u;
65                op->a[1].val = bits;
66                op->a[2] = Live;
67                op->a[3] = Fail;
68                op->a[4] = Flags;
69                op->a[5] = Dst;
70            }
71        }
72    } else if (Size.type == TAG_q) {
73        Eterm big = beamfile_get_literal(&S->beam, Size.val);
74        Uint bigval;
75
76        if (!term_to_Uint(big, &bigval)) {
77        error:
78            $BeamOpNameArity(op, jump, 1);
79            op->a[0] = Fail;
80        } else {
81            if (!beam_load_safe_mul(bigval, Unit.val, &bits)) {
82                goto error;
83            }
84            goto generic;
85        }
86    } else if (Size.type == TAG_x || Size.type == TAG_y) {
87        $BeamOpNameArity(op, i_bs_get_integer, 6);
88        op->a[0] = Ms;
89        op->a[1] = Fail;
90        op->a[2] = Live;
91        op->a[3].type = TAG_u;
92        op->a[3].val = (Unit.val << 3) | Flags.val;
93        op->a[4] = Size;
94        op->a[5] = Dst;
95        return op;
96    } else {
97        /* Invalid literal size. */
98        goto error;
99    }
100    return op;
101}
102
103gen.jump_tab(Src, Fail, Size, Rest) {
104    Sint min, max;
105    Sint i;
106    Sint size;
107    Sint arity;
108    int fixed_args;
109    BeamOp* op;
110
111    ASSERT(Size.val >= 2 && Size.val % 2 == 0);
112
113    /* Don't generate a jump table if there's only one choice */
114    if (Size.val == 2) {
115        BeamOp* jump;
116
117        $NewBeamOp(S, op);
118        $BeamOpNameArity(op, is_ne_exact, 3);
119        op->a[0] = Rest[1];
120        op->a[1] = Src;
121        op->a[2] = Rest[0];
122
123        $NewBeamOp(S, jump);
124        $BeamOpNameArity(jump, jump, 1);
125        jump->a[0] = Fail;
126
127        op->next = jump;
128        jump->next = NULL;
129        return op;
130    }
131
132    /* Calculate the minimum and maximum values and size of jump table. */
133    ASSERT(Rest[0].type == TAG_i);
134    min = max = Rest[0].val;
135    for (i = 2; i < Size.val; i += 2) {
136        ASSERT(Rest[i].type == TAG_i && Rest[i+1].type == TAG_f);
137        if (Rest[i].val < min) {
138            min = Rest[i].val;
139        } else if (max < Rest[i].val) {
140            max = Rest[i].val;
141        }
142    }
143    size = max - min + 1;
144
145    /* Allocate structure and fill in the fixed fields. */
146    $NewBeamOp(S, op);
147    op->next = NULL;
148    if (min == 0) {
149        $BeamOpNameArity(op, i_jump_on_val_zero, 3);
150    } else {
151        $BeamOpNameArity(op, i_jump_on_val, 4);
152    }
153    fixed_args = op->arity;
154    arity = fixed_args + size;
155    $BeamOpArity(op, arity);
156    op->a[0] = Src;
157    op->a[1] = Fail;
158    op->a[2].type = TAG_u;
159    op->a[2].val = size;
160    op->a[3].type = TAG_u;
161    op->a[3].val = min;
162
163    /* Fill in the jump table. */
164    for (i = fixed_args; i < arity; i++) {
165        op->a[i] = Fail;
166    }
167
168    for (i = 0; i < Size.val; i += 2) {
169        Sint index = fixed_args + Rest[i].val - min;
170        ASSERT(fixed_args <= index && index < arity);
171        op->a[index] = Rest[i+1];
172    }
173
174    return op;
175}
176
177//
178// Generate a select_val instruction.  We know that a jump table
179// is not suitable, and that all values are of the same type
180// (integer or atoms).
181//
182gen.select_val(Src, Fail, Size, Rest) {
183    BeamOp* op;
184    BeamOpArg *tmp;
185    int arity = Size.val + 3;
186    int size = Size.val / 2;
187    int i, j, align = 0;
188
189    if (size == 2) {
190        /*
191         * Use a special-cased instruction if there are only two values.
192         */
193
194        $NewBeamOp(S, op);
195        op->next = NULL;
196        $BeamOpNameArity(op, i_select_val2, 4);
197        $BeamOpArity(op, arity - 1);
198        op->a[0] = Src;
199        op->a[1] = Fail;
200        op->a[2] = Rest[0];
201        op->a[3] = Rest[2];
202        op->a[4] = Rest[1];
203        op->a[5] = Rest[3];
204
205        return op;
206    }
207
208    if (size <= 10) {
209        /* Use linear search. Reserve place for a sentinel. */
210        align = 1;
211    }
212
213    arity += 2*align;
214    size  += align;
215
216    $NewBeamOp(S, op);
217    op->next = NULL;
218    if (align == 0) {
219        $BeamOpNameArity(op, i_select_val_bins, 3);
220    } else {
221        $BeamOpNameArity(op, i_select_val_lins, 3);
222    }
223    $BeamOpArity(op, arity);
224    op->a[0] = Src;
225    op->a[1] = Fail;
226    op->a[2].type = TAG_u;
227    op->a[2].val = size;
228
229    tmp = (BeamOpArg *) erts_alloc(ERTS_ALC_T_LOADER_TMP, sizeof(BeamOpArg)*(arity-2*align));
230
231    for (i = 3; i < arity - 2*align; i++) {
232        tmp[i-3] = Rest[i-3];
233    }
234
235    /* Sort the values to make them useful for a binary or sentinel search. */
236    beam_load_sort_select_vals(tmp, size - align);
237
238    j = 3;
239    for (i = 3; i < arity - 2*align; i += 2) {
240        op->a[j]      = tmp[i-3];
241        op->a[j+size] = tmp[i-2];
242        j++;
243    }
244
245    erts_free(ERTS_ALC_T_LOADER_TMP, (void *) tmp);
246
247    if (align) {
248        /* Add sentinel for linear search. */
249        op->a[j].type = TAG_u;
250        op->a[j].val  = ~((BeamInstr)0);
251        op->a[j+size] = Fail;
252    }
253
254#ifdef DEBUG
255    for (i = 0; i < size - 1; i++) {
256        ASSERT(op->a[i+3].val <= op->a[i+4].val);
257    }
258#endif
259
260    return op;
261}
262
263//
264// Generate a select_val instruction for big numbers.
265//
266gen.select_literals(Src, Fail, Size, Rest) {
267    BeamOp* op;
268    BeamOp* jump;
269    BeamOp** prev_next = &op;
270
271    int i;
272
273    for (i = 0; i < Size.val; i += 2) {
274        BeamOp* op;
275        ASSERT(Rest[i].type == TAG_q);
276
277        $NewBeamOp(S, op);
278        $BeamOpNameArity(op, is_ne_exact, 3);
279        op->a[0] = Rest[i+1];
280        op->a[1] = Src;
281        op->a[2] = Rest[i];
282        *prev_next = op;
283        prev_next = &op->next;
284    }
285
286    $NewBeamOp(S, jump);
287    $BeamOpNameArity(jump, jump, 1);
288    jump->next = NULL;
289    jump->a[0] = Fail;
290    *prev_next = jump;
291    return op;
292}
293
294//
295// Replace a select_val instruction with a constant controlling expression
296// with a jump instruction.
297//
298gen.const_select_val(Src, Fail, Size, Rest) {
299    BeamOp* op;
300    int i;
301
302    ASSERT(Size.type == TAG_u);
303
304    $NewBeamOp(S, op);
305    $BeamOpNameArity(op, jump, 1);
306    op->next = NULL;
307
308    /* Search for a literal matching the controlling expression. */
309    switch (Src.type) {
310    case TAG_q:
311        {
312            Eterm expr = beamfile_get_literal(&S->beam, Src.val);
313            for (i = 0; i < Size.val; i += 2) {
314                if (Rest[i].type == TAG_q) {
315                    Eterm term = beamfile_get_literal(&S->beam, Rest[i].val);
316                    if (eq(term, expr)) {
317                        ASSERT(Rest[i+1].type == TAG_f);
318                        op->a[0] = Rest[i+1];
319                        return op;
320                    }
321                }
322            }
323        }
324        break;
325    case TAG_i:
326    case TAG_a:
327        for (i = 0; i < Size.val; i += 2) {
328            if (Rest[i].val == Src.val && Rest[i].type == Src.type) {
329                ASSERT(Rest[i+1].type == TAG_f);
330                op->a[0] = Rest[i+1];
331                return op;
332            }
333        }
334        break;
335    }
336
337    /*
338     * No match.  Use the failure label.
339     */
340
341    op->a[0] = Fail;
342    return op;
343}
344
345//
346// Split a list consisting of both small and bignumbers into two
347// select_val instructions.
348//
349gen.split_values(Src, TypeFail, Fail, Size, Rest) {
350    BeamOp* op1;
351    BeamOp* op2;
352    BeamOp* label;
353    BeamOp* is_integer;
354    int i;
355
356    ASSERT(Size.val >= 2 && Size.val % 2 == 0);
357
358    $NewBeamOp(S, is_integer);
359    $BeamOpNameArity(is_integer, is_integer, 2);
360    is_integer->a[0] = TypeFail;
361    is_integer->a[1] = Src;
362
363    $NewBeamOp(S, label);
364    $BeamOpNameArity(label, label, 1);
365    label->a[0].type = TAG_u;
366    label->a[0].val = beam_load_new_label(S);
367
368    $NewBeamOp(S, op1);
369    $BeamOpNameArity(op1, select_val, 3);
370    $BeamOpArity(op1, 3 + Size.val);
371    op1->a[0] = Src;
372    op1->a[1].type = TAG_f;
373    op1->a[1].val = label->a[0].val;
374    op1->a[2].type = TAG_u;
375    op1->a[2].val = 0;
376
377    $NewBeamOp(S, op2);
378    $BeamOpNameArity(op2, select_val, 3);
379    $BeamOpArity(op2, 3 + Size.val);
380    op2->a[0] = Src;
381    op2->a[1] = Fail;
382    op2->a[2].type = TAG_u;
383    op2->a[2].val = 0;
384
385    /* Split the list. */
386    ASSERT(Size.type == TAG_u);
387    for (i = 0; i < Size.val; i += 2) {
388        BeamOp* op = (Rest[i].type == TAG_q) ? op2 : op1;
389        int dst = 3 + op->a[2].val;
390
391        ASSERT(Rest[i+1].type == TAG_f);
392        op->a[dst] = Rest[i];
393        op->a[dst+1] = Rest[i+1];
394        op->arity += 2;
395        op->a[2].val += 2;
396    }
397    ASSERT(op1->a[2].val > 0);
398    ASSERT(op2->a[2].val > 0);
399
400    /* Order the instruction sequence appropriately. */
401    if (TypeFail.val == Fail.val) {
402        /*
403         * select_val L1 S ... (small numbers)
404         * label L1
405         * is_integer Fail S
406         * select_val Fail S ... (bignums)
407         */
408        op1->next = label;
409        label->next = is_integer;
410        is_integer->next = op2;
411    } else {
412        /*
413         * is_integer TypeFail S
414         * select_val L1 S ... (small numbers)
415         * label L1
416         * select_val Fail S ... (bignums)
417         */
418        is_integer->next = op1;
419        op1->next = label;
420        label->next = op2;
421        op1 = is_integer;
422    }
423    op2->next = NULL;
424
425    return op1;
426}
427
428
429//
430// Tag the list of values with tuple arity tags.
431//
432gen.select_tuple_arity(Src, Fail, Size, Rest) {
433    BeamOp* op;
434    BeamOpArg *tmp;
435    int arity = Size.val + 3;
436    int size = Size.val / 2;
437    int i, j, align = 0;
438
439    /* Verify the validity of the list. */
440
441    if (Size.val % 2 != 0) {
442        return NULL;
443    }
444
445    for (i = 0; i < Size.val; i += 2) {
446        if (Rest[i].type != TAG_u || Rest[i+1].type != TAG_f) {
447            return NULL;
448        }
449    }
450
451    /*
452     * Use a special-cased instruction if there are only two values.
453     */
454    if (size == 2) {
455        $NewBeamOp(S, op);
456        $BeamOpNameArity(op, i_select_tuple_arity2, 4);
457        $BeamOpArity(op, arity - 1);
458        op->next = NULL;
459        op->a[0] = Src;
460        op->a[1] = Fail;
461        op->a[2].type = TAG_u;
462        op->a[2].val  = Rest[0].val;
463        op->a[3].type = TAG_u;
464        op->a[3].val  = Rest[2].val;
465        op->a[4] = Rest[1];
466        op->a[5] = Rest[3];
467
468        return op;
469    }
470
471    /*
472     * Generate the generic instruction.
473     * Assumption:
474     *   Few different tuple arities to select on (fewer than 20).
475     *   Use linear scan approach.
476     */
477
478    align = 1;
479
480    arity += 2*align;
481    size  += align;
482
483    $NewBeamOp(S, op);
484    $BeamOpNameArity(op, i_select_tuple_arity, 3);
485    $BeamOpArity(op, arity);
486    op->next = NULL;
487    op->a[0] = Src;
488    op->a[1] = Fail;
489    op->a[2].type = TAG_u;
490    op->a[2].val = size;
491
492    tmp = (BeamOpArg *) erts_alloc(ERTS_ALC_T_LOADER_TMP, sizeof(BeamOpArg)*(arity-2*align));
493
494    for (i = 3; i < arity - 2*align; i+=2) {
495        tmp[i-3].type = TAG_v;
496        tmp[i-3].val  = make_arityval(Rest[i-3].val);
497        tmp[i-2]      = Rest[i-2];
498    }
499
500    /* Sort the values to make them useful for a binary or sentinel search. */
501    beam_load_sort_select_vals(tmp, size - align);
502
503    j = 3;
504    for (i = 3; i < arity - 2*align; i += 2) {
505        op->a[j]        = tmp[i-3];
506        op->a[j + size] = tmp[i-2];
507        j++;
508    }
509
510    erts_free(ERTS_ALC_T_LOADER_TMP, (void *) tmp);
511
512    op->a[j].type = TAG_u;
513    op->a[j].val  = ~((BeamInstr)0);
514    op->a[j+size] = Fail;
515
516    return op;
517}
518
519gen.new_small_map_lit(Dst, Live, Size, Rest) {
520    unsigned size = Size.val;
521    Uint lit;
522    unsigned i;
523    BeamOp* op;
524    BeamOpArg* dst;
525    Eterm* tmp;
526    Eterm* thp;
527    Eterm keys;
528
529    $NewBeamOp(S, op);
530    $BeamOpNameArity(op, i_new_small_map_lit, 3);
531    $BeamOpArity(op, 3 + size/2);
532    op->next = NULL;
533
534    tmp = thp = erts_alloc(ERTS_ALC_T_LOADER_TMP, (1 + size/2) * sizeof(*tmp));
535    keys = make_tuple(thp);
536    *thp++ = make_arityval(size/2);
537
538    dst = op->a+3;
539
540    for (i = 0; i < size; i += 2) {
541        switch (Rest[i].type) {
542        case TAG_a:
543            *thp++ = Rest[i].val;
544            ASSERT(is_atom(Rest[i].val));
545            break;
546        case TAG_i:
547            *thp++ = make_small(Rest[i].val);
548            break;
549        case TAG_n:
550            *thp++ = NIL;
551            break;
552        case TAG_q:
553            *thp++ = beamfile_get_literal(&S->beam, Rest[i].val);
554            break;
555        }
556        *dst++ = Rest[i + 1];
557    }
558
559    lit = beamfile_add_literal(&S->beam, keys);
560    erts_free(ERTS_ALC_T_LOADER_TMP, tmp);
561
562    op->a[0] = Dst;
563    op->a[1] = Live;
564    op->a[2].type = TAG_q;
565    op->a[2].val = lit;
566
567    return op;
568}
569
570// Macro for generating a timeout instruction for a literal timeout value.
571gen_literal_timeout(stp, fail, time, succ_instr, fail_instr) {
572    BeamOp* op;
573    Sint timeout;
574
575    $NewBeamOp($stp, op);
576    $BeamOpNameArity(op, $succ_instr, 2);
577    op->a[0].type = TAG_u;
578    op->a[1] = $fail;
579
580    if ($time.type == TAG_i && (timeout = $time.val) >= 0 &&
581#if defined(ARCH_64)
582        (timeout >> 32) == 0
583#else
584        1
585#endif
586        ) {
587        op->a[0].val = timeout;
588#if !defined(ARCH_64)
589    } else if ($time.type == TAG_q) {
590        Eterm big = beamfile_get_literal(&S->beam, $time.val);
591
592        if (is_not_big(big)) {
593            goto error;
594        }
595
596        if (big_arity(big) > 1 || big_sign(big)) {
597            goto error;
598        } else {
599            Uint u;
600            (void) term_to_Uint(big, &u);
601            op->a[0].val = (BeamInstr) u;
602        }
603#endif
604    } else {
605#if !defined(ARCH_64)
606    error:
607#endif
608        $BeamOpNameArity(op, $fail_instr, 0);
609    }
610    return op;
611}
612
613gen.literal_timeout(Fail, Time) {
614    $gen_literal_timeout(S, Fail, Time, wait_timeout_unlocked_int, i_wait_error);
615}
616
617gen.literal_timeout_locked(Fail, Time) {
618    $gen_literal_timeout(S, Fail, Time, wait_timeout_locked_int, i_wait_error_locked);
619}
620
621// Generate an instruction for element/2.
622gen.element(Fail, Index, Tuple, Dst) {
623    BeamOp* op;
624
625    $NewBeamOp(S, op);
626    op->next = NULL;
627
628    if (Index.type == TAG_i && Index.val > 0 &&
629        Index.val <= ERTS_MAX_TUPLE_SIZE &&
630        (Tuple.type == TAG_x || Tuple.type == TAG_y)) {
631        $BeamOpNameArity(op, i_fast_element, 4);
632        op->a[0] = Tuple;
633        op->a[1] = Fail;
634        op->a[2].type = TAG_u;
635        op->a[2].val = Index.val;
636        op->a[3] = Dst;
637    } else {
638        $BeamOpNameArity(op, i_element, 4);
639        op->a[0] = Tuple;
640        op->a[1] = Fail;
641        op->a[2] = Index;
642        op->a[3] = Dst;
643    }
644
645    return op;
646}
647
648// Generate the fastest instruction to fetch a binary from a binary.
649gen.get_binary2(Fail, Ms, Live, Size, Unit, Flags, Dst) {
650    BeamOp* op;
651
652    $NewBeamOp(S, op);
653    $NativeEndian(Flags);
654
655    if (Size.type == TAG_a && Size.val == am_all) {
656        $BeamOpNameArity(op, i_bs_get_binary_all2, 5);
657        op->a[0] = Ms;
658        op->a[1] = Fail;
659        op->a[2] = Live;
660        op->a[3] = Unit;
661        op->a[4] = Dst;
662    } else if (Size.type == TAG_i) {
663        $BeamOpNameArity(op, i_bs_get_binary_imm2, 6);
664        op->a[0] = Ms;
665        op->a[1] = Fail;
666        op->a[2] = Live;
667        op->a[3].type = TAG_u;
668        if (!beam_load_safe_mul(Size.val, Unit.val, &op->a[3].val)) {
669            goto error;
670        }
671        op->a[4] = Flags;
672        op->a[5] = Dst;
673    } else if (Size.type == TAG_q) {
674        Eterm big = beamfile_get_literal(&S->beam, Size.val);
675        Uint bigval;
676
677        if (!term_to_Uint(big, &bigval)) {
678        error:
679            $BeamOpNameArity(op, jump, 1);
680            op->a[0] = Fail;
681        } else {
682            $BeamOpNameArity(op, i_bs_get_binary_imm2, 6);
683            op->a[0] = Ms;
684            op->a[1] = Fail;
685            op->a[2] = Live;
686            op->a[3].type = TAG_u;
687            if (!beam_load_safe_mul(bigval, Unit.val, &op->a[3].val)) {
688                goto error;
689            }
690            op->a[4] = Flags;
691            op->a[5] = Dst;
692        }
693    } else if (Size.type == TAG_x || Size.type == TAG_y) {
694        $BeamOpNameArity(op, i_bs_get_binary2, 6);
695        op->a[0] = Ms;
696        op->a[1] = Fail;
697        op->a[2] = Live;
698        op->a[3] = Size;
699        op->a[4].type = TAG_u;
700        op->a[4].val = (Unit.val << 3) | Flags.val;
701        op->a[5] = Dst;
702    } else {
703        /* Invalid literal size. */
704        goto error;
705    }
706    op->next = NULL;
707    return op;
708}
709
710gen.is_function2(Fail, Fun, Arity) {
711    BeamOp* op;
712    int literal_arity = Arity.type == TAG_i;
713    int fun_is_reg = Fun.type == TAG_x || Fun.type == TAG_y;
714
715    $NewBeamOp(S, op);
716
717    if (fun_is_reg && literal_arity) {
718        /*
719         * Most common case. Fun in a register and arity
720         * is an integer literal.
721         */
722        if (Arity.val > MAX_ARG) {
723            /* Arity is negative or too big. */
724            $BeamOpNameArity(op, jump, 1);
725            op->a[0] = Fail;
726            return op;
727        } else {
728            $BeamOpNameArity(op, hot_is_function2, 3);
729            op->a[0] = Fail;
730            op->a[1] = Fun;
731            op->a[2].type = TAG_u;
732            op->a[2].val = Arity.val;
733            return op;
734        }
735    } else {
736        /*
737         * Handle extremely uncommon cases by a slower sequence.
738         */
739        BeamOp* move_fun;
740        BeamOp* move_arity;
741
742        $NewBeamOp(S, move_fun);
743        $NewBeamOp(S, move_arity);
744
745        move_fun->next = move_arity;
746        move_arity->next = op;
747
748        $BeamOpNameArity(move_fun, move, 2);
749        move_fun->a[0] = Fun;
750        move_fun->a[1].type = TAG_x;
751        move_fun->a[1].val = 1022;
752
753        $BeamOpNameArity(move_arity, move, 2);
754        move_arity->a[0] = Arity;
755        move_arity->a[1].type = TAG_x;
756        move_arity->a[1].val = 1023;
757
758        $BeamOpNameArity(op, cold_is_function2, 3);
759        op->a[0] = Fail;
760        op->a[1].type = TAG_x;
761        op->a[1].val = 1022;
762        op->a[2].type = TAG_x;
763        op->a[2].val = 1023;
764        return move_fun;
765    }
766}
767
768INIT_YREGS(S, N) {
769    int i;
770    for (i = 0; i < $N; i++) {
771	BeamOp* init;
772
773	$NewBeamOp($S, init);
774	$BeamOpNameArity(init, init, 1);
775	init->a[0] = Yregs[i];
776	*p = init;
777	p = &init->next;
778    }
779}
780
781gen.allocate(Ns, Live, N, Yregs) {
782    BeamOp* alloc;
783    BeamOp** p;
784
785    $NewBeamOp(S, alloc);
786    alloc->a[0] = Ns;
787    alloc->a[1] = Live;
788
789    if (Ns.val <= 2 * N.val) {
790	/*
791	 * At least half of the Y registers need explicit
792	 * initialization. It will be cheaper to zero all Y registers.
793	 */
794	$BeamOpNameArity(alloc, allocate_zero, 2);
795    } else {
796	$BeamOpNameArity(alloc, allocate, 2);
797	p = &alloc->next;
798	$INIT_YREGS(S, N.val);
799    }
800    return alloc;
801}
802
803gen.allocate_heap(Ns, Nh, Live, N, Yregs) {
804    BeamOp* alloc;
805    BeamOp** p;
806
807    $NewBeamOp(S, alloc);
808    alloc->a[0] = Ns;
809    alloc->a[1] = Nh;
810    alloc->a[2] = Live;
811
812    if (Ns.val <= 2 * N.val) {
813	/*
814	 * At least half of the Y registers need explicit
815	 * initialization. It will be cheaper to zero all Y registers.
816	 */
817	$BeamOpNameArity(alloc, allocate_heap_zero, 3);
818    } else {
819	$BeamOpNameArity(alloc, allocate_heap, 3);
820	p = &alloc->next;
821	$INIT_YREGS(S, N.val);
822    }
823    return alloc;
824}
825
826gen.init_yregs(N, Yregs) {
827    BeamOp* first = NULL;
828    BeamOp** p;
829
830    p = &first;
831    $INIT_YREGS(S, N.val);
832    return first;
833}
834