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;
}