int beam_load_emit_op()

in erts/emulator/beam/jit/asm_load.c [225:540]


int beam_load_emit_op(LoaderState *stp, BeamOp *tmp_op) {
    const char *sign;
    int arg;

    /*
     * Verify and massage the operands for the specific instruction.
     *
     * After the massaging, TAG_i denotes a tagged immediate value,
     * either NIL, an atom, or a small integer. The tags TAG_n and
     * TAG_a are no longer used. TAG_f can either be a non-zero label
     * number (in guards) or 0 (in function bodies) to indicate that
     * an exception should be generated on failure. TAG_j is no longer
     * used.
     */
    sign = opc[stp->specific_op].sign;
    arg = 0;
    ASSERT(sign != NULL);
    while (*sign) {
        Uint tag;
        BeamOpArg *curr = &stp->genop->a[arg];

        ASSERT(arg < stp->genop->arity);
        tag = curr->type;

        switch (*sign) {
        case 'n': /* Nil */
            ASSERT(tag != TAG_r);
            curr->type = TAG_i;
            curr->val = NIL;
            BeamLoadVerifyTag(stp, tag_to_letter[tag], *sign);
            break;
        case 'x': /* x(N) */
        case 'y': /* y(N) */
            BeamLoadVerifyTag(stp, tag_to_letter[tag], *sign);
            break;
        case 'a': /* Tagged atom */
            BeamLoadVerifyTag(stp, tag_to_letter[tag], *sign);
            curr->type = TAG_i;
            break;
        case 'c': /* Tagged constant */
            switch (tag) {
            case TAG_i:
                curr->val = make_small((Uint)curr->val);
                break;
            case TAG_a:
                curr->type = TAG_i;
                break;
            case TAG_n:
                curr->val = NIL;
                curr->type = TAG_i;
                break;
            case TAG_q:
                break;
            default:
                BeamLoadError1(stp,
                               "bad tag %d for tagged constant",
                               curr->type);
                break;
            }
            break;
        case 's':
            /* Any source (tagged constant or register) */
            switch (tag) {
            case TAG_x:
                break;
            case TAG_y:
                break;
            case TAG_i:
                curr->val = (BeamInstr)make_small(curr->val);
                break;
            case TAG_a:
                curr->type = TAG_i;
                break;
            case TAG_n:
                curr->type = TAG_i;
                curr->val = NIL;
                break;
            case TAG_q: {
                Eterm term = beamfile_get_literal(&stp->beam, curr->val);
                switch (loader_tag(term)) {
                case LOADER_X_REG:
                case LOADER_Y_REG:
                    BeamLoadError1(stp,
                                   "the term '%T' would be confused "
                                   "with a register",
                                   term);
                }
            } break;
            default:
                BeamLoadError1(stp,
                               "bad tag %d for general source",
                               curr->type);
                break;
            }
            break;
        case 'd': /* Destination (x(N), y(N) */
        case 'S': /* Source (x(N), y(N)) */
            switch (tag) {
            case TAG_x:
                break;
            case TAG_y:
                break;
            default:
                BeamLoadError1(stp, "bad tag %d for destination", curr->type);
                break;
            }
            break;
        case 't': /* Small untagged integer (16 bits) -- can be packed. */
        case 'I': /* Untagged integer (32 bits) -- can be packed.  */
        case 'W': /* Untagged integer or pointer (machine word). */
#ifdef DEBUG
            switch (*sign) {
            case 't':
                /* 't'-typed values must fit in 16 bits. */
                ASSERT((curr->val >> 16) == 0);
                break;
#    ifdef ARCH_64
            case 'I':
            case 'V':
                /* 'I'- and 'V'-typed values must fit in 32 bits. */
                ASSERT((curr->val >> 32) == 0);
                break;
#    endif
            }
#endif
            BeamLoadVerifyTag(stp, tag, TAG_u);
            break;
        case 'A': /* Arity value. */
            BeamLoadVerifyTag(stp, tag, TAG_u);
            curr->val = make_arityval(curr->val);
            break;
        case 'f': /* Destination label */
            BeamLoadVerifyTag(stp, tag_to_letter[tag], *sign);
            break;
        case 'j': /* 'f' or 'p' */
            switch (tag) {
            case TAG_p:
                curr->type = TAG_f;
                curr->val = 0;
                break;
            case TAG_f:
                break;
            default:
                BeamLoadError3(stp,
                               "bad tag %d; expected %d or %d",
                               tag,
                               TAG_f,
                               TAG_p);
            }

            break;
        case 'L': /* Define label */
            ASSERT(stp->specific_op == op_label_L ||
                   stp->specific_op == op_aligned_label_L);
            BeamLoadVerifyTag(stp, tag, TAG_u);
            stp->last_label = curr->val;
            if (stp->last_label < 0 ||
                stp->last_label >= stp->beam.code.label_count) {
                BeamLoadError2(stp,
                               "invalid label num %u (0 < label < %u)",
                               curr->val,
                               stp->beam.code.label_count);
            }
            if (stp->labels[stp->last_label].value != 0) {
                BeamLoadError1(stp,
                               "label %d defined more than once",
                               stp->last_label);
            }
            stp->labels[stp->last_label].value = 1;
            break;
        case 'e': /* Export entry */
            BeamLoadVerifyTag(stp, tag, TAG_u);
            if (curr->val >= stp->beam.imports.count) {
                BeamLoadError1(stp, "invalid import table index %d", curr->val);
            }
            curr->type = TAG_r;
            break;
        case 'b': {
            int i = tmp_op->a[arg].val;
            BeamLoadVerifyTag(stp, tag, TAG_u);
            if (i >= stp->beam.imports.count) {
                BeamLoadError1(stp, "invalid import table index %d", i);
            } else if (stp->bif_imports[i] == NULL) {
                BeamLoadError1(stp, "import %d not a BIF", i);
            } else {
                curr->val = (BeamInstr)stp->bif_imports[i]->f;
            }
        } break;
        case 'P': /* Byte offset into tuple */
            BeamLoadVerifyTag(stp, tag, TAG_u);
            curr->val = (BeamInstr)((curr->val + 1) * sizeof(Eterm));
            break;
        case 'l': /* Floating point register. */
            BeamLoadVerifyTag(stp, tag_to_letter[tag], *sign);
            curr->val = curr->val * sizeof(FloatDef);
            break;
        case 'q': /* Literal */
            BeamLoadVerifyTag(stp, tag, TAG_q);
            break;
        case 'F': /* Fun entry */
            BeamLoadVerifyTag(stp, tag, TAG_u);
            break;
        default:
            BeamLoadError1(stp, "bad argument tag: %d", *sign);
        }
        sign++;
        arg++;
    }

    /*
     * Verify and massage any list arguments according to the primitive tags.
     *
     * TAG_i will denote a tagged immediate value (NIL, small integer,
     * atom, or tuple arity). TAG_n, TAG_a, and TAG_v will no longer be used.
     */
    for (; arg < tmp_op->arity; arg++) {
        BeamOpArg *curr = &tmp_op->a[arg];

        switch (tmp_op->a[arg].type) {
        case TAG_i:
            curr->val = make_small(tmp_op->a[arg].val);
            break;
        case TAG_n:
            curr->val = NIL;
            curr->type = TAG_i;
            break;
        case TAG_a:
        case TAG_v:
            curr->type = TAG_i;
            break;
        case TAG_u:
        case TAG_f:
        case TAG_x:
        case TAG_y:
        case TAG_q:
            break;
        default:
            BeamLoadError1(stp,
                           "unsupported primitive type '%c'",
                           tag_to_letter[tmp_op->a[arg].type]);
        }
    }

    /* Handle a few special cases. */
    switch (stp->specific_op) {
    case op_i_func_info_IaaI:
        if (stp->function_number >= stp->beam.code.function_count) {
            BeamLoadError1(stp,
                           "too many functions in module (header said %u)",
                           stp->beam.code.function_count);
        }

        /* Save current offset in the function table, pointing before the
         * func_info instruction. */
        stp->codev[stp->function_number] = beamasm_get_offset(stp->ba);
        stp->function_number++;

        /* Save context for error messages. */
        stp->function = tmp_op->a[2].val;
        stp->arity = tmp_op->a[3].val;

        if (stp->arity > MAX_ARG) {
            BeamLoadError1(stp, "too many arguments: %d", stp->arity);
        }

        break;
    case op_func_line_I:
        /* This is the first line instruction of a function, preceding
         * the func_info instruction. */
        if (stp->func_line) {
            stp->func_line[stp->function_number] = stp->current_li;
        }

        break;
    }

    /* Generate assembly code for the specific instruction. */
    if (beamasm_emit(stp->ba, stp->specific_op, tmp_op) == 0) {
        BeamLoadError1(stp, "failed to emit asm for %d", stp->specific_op);
    }

    switch (stp->specific_op) {
    case op_func_line_I:
        /* Since this is the beginning of a new function, force insertion
         * of the line entry even if it happens to be a duplicate of the
         * previous one. */
        if (add_line_entry(stp, tmp_op->a[0].val, 1)) {
            goto load_error;
        }
        break;
    case op_line_I:
        /* We'll save some memory by not inserting a line entry that
         * is equal to the previous one. */
        if (add_line_entry(stp, tmp_op->a[0].val, 0)) {
            goto load_error;
        }
        break;
    case op_int_code_end:
        /* End of code found. */
        if (stp->function_number != stp->beam.code.function_count) {
            BeamLoadError2(stp,
                           "too few functions (%u) in module (header said %u)",
                           stp->function_number,
                           stp->beam.code.function_count);
        }

        stp->function = THE_NON_VALUE;
        stp->genop = NULL;
        stp->specific_op = -1;
    }

    return 1;

load_error:
    return 0;
}