in erts/emulator/beam/emu/emu_load.c [799:1467]
int beam_load_emit_op(LoaderState *stp, BeamOp *tmp_op) {
/* The size of the loaded func_info instruction is needed by both the nif
* functionality and line instructions. */
enum {
FUNC_INFO_SZ = sizeof(ErtsCodeInfo) / sizeof(Eterm)
};
const char *sign;
int arg;
Uint last_instr_start;
int codev_size, ci;
BeamInstr *code;
#if defined(BEAM_WIDE_SHIFT)
int num_trailing_f; /* Number of extra 'f' arguments in a list */
#endif
codev_size = stp->codev_size;
code = stp->codev;
ci = stp->ci;
ASSERT(ci <= codev_size);
CodeNeed(opc[stp->specific_op].sz + 16); /* Extra margin for packing */
last_instr_start = ci + opc[stp->specific_op].adjust;
/* Load the found specific operation. */
code[ci++] = BeamOpCodeAddr(stp->specific_op);
sign = opc[stp->specific_op].sign;
ASSERT(sign != NULL);
arg = 0;
while (*sign) {
Uint tag;
ASSERT(arg < stp->genop->arity);
tag = stp->genop->a[arg].type;
switch (*sign) {
case 'r': /* x(0) */
case 'n': /* Nil */
BeamLoadVerifyTag(stp, tag_to_letter[tag], *sign);
break;
case 'x': /* x(N) */
BeamLoadVerifyTag(stp, tag_to_letter[tag], *sign);
code[ci++] = tmp_op->a[arg].val * sizeof(Eterm);
break;
case 'y': /* y(N) */
BeamLoadVerifyTag(stp, tag_to_letter[tag], *sign);
code[ci++] = (tmp_op->a[arg].val + CP_SIZE) * sizeof(Eterm);
break;
case 'a': /* Tagged atom */
BeamLoadVerifyTag(stp, tag_to_letter[tag], *sign);
code[ci++] = tmp_op->a[arg].val;
break;
case 'c': /* Tagged constant */
switch (tag) {
case TAG_i:
code[ci++] = (BeamInstr) make_small((Uint) tmp_op->a[arg].val);
break;
case TAG_a:
code[ci++] = tmp_op->a[arg].val;
break;
case TAG_n:
code[ci++] = NIL;
break;
case TAG_q:
new_literal_patch(stp, ci);
code[ci++] = tmp_op->a[arg].val;
break;
default:
BeamLoadError1(stp, "bad tag %d for tagged constant",
tmp_op->a[arg].type);
break;
}
break;
case 's': /* Any source (tagged constant or register) */
switch (tag) {
case TAG_x:
code[ci++] = make_loader_x_reg(tmp_op->a[arg].val);
break;
case TAG_y:
code[ci++] = make_loader_y_reg(tmp_op->a[arg].val + CP_SIZE);
break;
case TAG_i:
code[ci++] = (BeamInstr) make_small((Uint)tmp_op->a[arg].val);
break;
case TAG_n:
ASSERT(tmp_op->a[arg].val == NIL);
/* ! Fall through ! */
case TAG_a:
code[ci++] = tmp_op->a[arg].val;
break;
case TAG_q:
{
BeamInstr val = tmp_op->a[arg].val;
Eterm term = beamfile_get_literal(&stp->beam, val);
new_literal_patch(stp, ci);
code[ci++] = 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",
tmp_op->a[arg].type);
break;
}
break;
case 'd': /* Destination (x(N), y(N) */
case 'S': /* Source (x(N), y(N)) */
switch (tag) {
case TAG_x:
code[ci++] = tmp_op->a[arg].val * sizeof(Eterm);
break;
case TAG_y:
code[ci++] = (tmp_op->a[arg].val + CP_SIZE) * sizeof(Eterm) + 1;
break;
default:
BeamLoadError1(stp, "bad tag %d for destination",
tmp_op->a[arg].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((tmp_op->a[arg].val >> 16) == 0);
break;
#ifdef ARCH_64
case 'I':
case 'V':
/* 'I'- and 'V'-typed values must fit in 32 bits. */
ASSERT((tmp_op->a[arg].val >> 32) == 0);
break;
#endif
}
#endif
BeamLoadVerifyTag(stp, tag, TAG_u);
code[ci++] = tmp_op->a[arg].val;
break;
case 'A': /* Arity value. */
BeamLoadVerifyTag(stp, tag, TAG_u);
code[ci++] = make_arityval(tmp_op->a[arg].val);
break;
case 'f': /* Destination label */
BeamLoadVerifyTag(stp, tag_to_letter[tag], *sign);
register_label_patch(stp, tmp_op->a[arg].val, ci,
-last_instr_start);
ci++;
break;
case 'j': /* 'f' or 'p' */
if (tag == TAG_p) {
code[ci] = 0;
} else if (tag == TAG_f) {
register_label_patch(stp, tmp_op->a[arg].val, ci,
-last_instr_start);
} else {
BeamLoadError3(stp, "bad tag %d; expected %d or %d",
tag, TAG_f, TAG_p);
}
ci++;
break;
case 'L': /* Define label */
ci--; /* Remove label from loaded code */
ASSERT(stp->specific_op == op_label_L);
BeamLoadVerifyTag(stp, tag, TAG_u);
stp->last_label = tmp_op->a[arg].val;
if (stp->last_label < 0 ||
stp->last_label >= stp->beam.code.label_count) {
BeamLoadError2(stp, "invalid label num %u (0 < label < %u)",
stp->last_label, 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 = ci;
break;
case 'e': /* Export entry */
BeamLoadVerifyTag(stp, tag, TAG_u);
if (tmp_op->a[arg].val >= stp->beam.imports.count) {
BeamLoadError1(stp, "invalid import table index %d",
tmp_op->a[arg].val);
} else {
code[ci] = stp->import_patches[tmp_op->a[arg].val];
stp->import_patches[tmp_op->a[arg].val] = ci;
ci++;
}
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 {
code[ci++] = (BeamInstr)stp->bif_imports[i]->f;
}
}
break;
case 'P': /* Byte offset into tuple or stack */
case 'Q': /* Like 'P', but packable */
BeamLoadVerifyTag(stp, tag, TAG_u);
code[ci++] = (BeamInstr) ((tmp_op->a[arg].val+1) * sizeof(Eterm));
break;
case 'l': /* Floating point register. */
BeamLoadVerifyTag(stp, tag_to_letter[tag], *sign);
code[ci++] = tmp_op->a[arg].val * sizeof(FloatDef);
break;
case 'q': /* Literal */
new_literal_patch(stp, ci);
code[ci++] = tmp_op->a[arg].val;
break;
case 'F': /* Fun entry */
new_lambda_patch(stp, ci);
code[ci++] = tmp_op->a[arg].val;
break;
default:
BeamLoadError1(stp, "bad argument tag: %d", *sign);
}
sign++;
arg++;
}
/* The packing engine. */
if (opc[stp->specific_op].pack[0]) {
char* prog; /* Program for packing engine. */
struct pack_stack {
BeamInstr instr;
Uint* patch_pos;
} stack[8]; /* Stack. */
struct pack_stack* sp = stack; /* Points to next free position. */
BeamInstr packed = 0; /* Accumulator for packed operations. */
LabelPatch* packed_label = 0;
for (prog = opc[stp->specific_op].pack; *prog; prog++) {
switch (*prog) {
case 'g': /* Get operand and push on stack. */
ci--;
sp->instr = code[ci];
sp->patch_pos = 0;
sp++;
break;
case 'f': /* Get possible 'f' operand and push on stack. */
{
Uint w = code[--ci];
sp->instr = w;
sp->patch_pos = 0;
if (w != 0) {
LabelPatch* lbl_p;
int num_patches;
int patch;
ASSERT(w < stp->beam.code.label_count);
lbl_p = stp->labels[w].patches;
num_patches = stp->labels[w].num_patches;
for (patch = num_patches - 1; patch >= 0; patch--) {
if (lbl_p[patch].pos == ci) {
sp->patch_pos = &lbl_p[patch].pos;
break;
}
}
ASSERT(sp->patch_pos);
}
sp++;
}
break;
case 'q': /* Get possible 'q' operand and push on stack. */
{
LiteralPatch* lp;
ci--;
sp->instr = code[ci];
sp->patch_pos = 0;
for (lp = stp->literal_patches;
lp && lp->pos > ci - ERTS_BEAM_MAX_OPARGS;
lp = lp->next) {
if (lp->pos == ci) {
sp->patch_pos = &lp->pos;
break;
}
}
sp++;
}
break;
#ifdef ARCH_64
case '1': /* Tightest shift (always 10 bits) */
ci--;
ASSERT((code[ci] & ~0x1FF8ull) == 0); /* Fits in 10 bits */
packed = (packed << BEAM_TIGHTEST_SHIFT);
packed |= code[ci] >> 3;
if (packed_label) {
packed_label->packed++;
}
break;
#endif
case '2': /* Tight shift (10 or 16 bits) */
packed = (packed << BEAM_TIGHT_SHIFT) | code[--ci];
if (packed_label) {
packed_label->packed++;
}
break;
case '3': /* Loose shift (16 bits) */
packed = (packed << BEAM_LOOSE_SHIFT) | code[--ci];
if (packed_label) {
packed_label->packed++;
}
break;
#ifdef ARCH_64
case '4': /* Wide shift (32 bits) */
{
Uint w = code[--ci];
if (packed_label) {
packed_label->packed++;
}
/*
* 'w' can handle both labels ('f' and 'j'), as well
* as 'I'. Test whether this is a label.
*/
if (w < stp->beam.code.label_count) {
/*
* Probably a label. Look for patch pointing to this
* position.
*/
LabelPatch* lp = stp->labels[w].patches;
int num_patches = stp->labels[w].num_patches;
int patch;
for (patch = num_patches - 1; patch >= 0; patch--) {
if (lp[patch].pos == ci) {
lp[patch].packed = 1;
packed_label = &lp[patch];
break;
}
}
}
packed = (packed << BEAM_WIDE_SHIFT) |
(code[ci] & BEAM_WIDE_MASK);
}
break;
#endif
case 'p': /* Put instruction (from stack). */
--sp;
code[ci] = sp->instr;
if (sp->patch_pos) {
*sp->patch_pos = ci;
}
ci++;
break;
case 'P': /* Put packed operands (on the stack). */
sp->instr = packed;
sp->patch_pos = 0;
if (packed_label) {
sp->patch_pos = &packed_label->pos;
packed_label = 0;
}
sp++;
packed = 0;
break;
#if defined(ARCH_64) && defined(CODE_MODEL_SMALL)
case '#': /* -1 */
case '$': /* -2 */
case '%': /* -3 */
case '&': /* -4 */
case '\'': /* -5 */
case '(': /* -6 */
/* Pack accumulator contents into instruction word. */
{
Sint pos = ci - (*prog - '#' + 1);
/* Are the high 32 bits of the instruction word zero? */
ASSERT((code[pos] & ~((1ull << BEAM_WIDE_SHIFT)-1)) == 0);
code[pos] |= packed << BEAM_WIDE_SHIFT;
if (packed_label) {
ASSERT(packed_label->packed == 1);
packed_label->pos = pos;
packed_label->packed = 2;
packed_label = 0;
}
packed >>= BEAM_WIDE_SHIFT;
}
break;
#endif
default:
erts_exit(ERTS_ERROR_EXIT, "beam_load: invalid packing op: %c\n", *prog);
}
}
ASSERT(sp == stack); /* Incorrect program? */
}
/* Load any list arguments using the primitive tags. */
#if defined(BEAM_WIDE_SHIFT)
num_trailing_f = 0;
#endif
for ( ; arg < tmp_op->arity; arg++) {
#if defined(BEAM_WIDE_SHIFT)
if (tmp_op->a[arg].type == TAG_f) {
num_trailing_f++;
} else {
num_trailing_f = 0;
}
#endif
CodeNeed(1);
switch (tmp_op->a[arg].type) {
case TAG_i:
code[ci++] = make_small(tmp_op->a[arg].val);
break;
case TAG_u:
case TAG_a:
case TAG_v:
code[ci++] = tmp_op->a[arg].val;
break;
case TAG_f:
register_label_patch(stp, tmp_op->a[arg].val, ci,
-last_instr_start);
ci++;
break;
case TAG_x:
code[ci++] = make_loader_x_reg(tmp_op->a[arg].val);
break;
case TAG_y:
code[ci++] = make_loader_y_reg(tmp_op->a[arg].val + CP_SIZE);
break;
case TAG_n:
code[ci++] = NIL;
break;
case TAG_q:
new_literal_patch(stp, ci);
code[ci++] = tmp_op->a[arg].val;
break;
default:
BeamLoadError1(stp, "unsupported primitive type '%c'",
tag_to_letter[tmp_op->a[arg].type]);
}
}
/* If all the extra arguments were 'f' operands, and the wordsize is 64
* bits, pack two 'f' operands into each word. */
#if defined(BEAM_WIDE_SHIFT)
if (num_trailing_f >= 1) {
Uint src_index = ci - num_trailing_f;
Uint src_limit = ci;
Uint dst_limit = src_index + (num_trailing_f+1)/2;
ci = src_index;
while (ci < dst_limit) {
Uint w[2];
BeamInstr packed = 0;
int wi;
w[0] = code[src_index];
if (src_index+1 < src_limit) {
w[1] = code[src_index+1];
} else {
w[1] = 0;
}
for (wi = 0; wi < 2; wi++) {
Uint lbl = w[wi];
LabelPatch* lp = stp->labels[lbl].patches;
int num_patches = stp->labels[lbl].num_patches;
#if defined(WORDS_BIGENDIAN)
packed <<= BEAM_WIDE_SHIFT;
packed |= lbl & BEAM_WIDE_MASK;
#else
packed >>= BEAM_WIDE_SHIFT;
packed |= lbl << BEAM_WIDE_SHIFT;
#endif
while (num_patches-- > 0) {
if (lp->pos == src_index + wi) {
lp->pos = ci;
#if defined(WORDS_BIGENDIAN)
lp->packed = 2 - wi;
#else
lp->packed = wi + 1;
#endif
break;
}
lp++;
}
}
code[ci++] = packed;
src_index += 2;
}
}
#endif
/*
* Handle a few special cases.
*/
switch (stp->specific_op) {
case op_i_func_info_IaaI:
{
Sint offset;
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 context for error messages.
*/
stp->function = code[ci-2];
stp->arity = code[ci-1];
/*
* Save current offset of into the line instruction array.
*/
if (stp->func_line) {
stp->func_line[stp->function_number] = stp->current_li;
}
stp->last_func_start = ci;
/* When this assert is triggered, it is normally a sign that the
* size of the ops.tab i_func_info instruction is not the same as
* FUNC_INFO_SZ.
*/
ASSERT(stp->labels[stp->last_label].value == ci - FUNC_INFO_SZ);
offset = stp->function_number;
register_label_patch(stp, stp->last_label, offset, 0);
stp->function_number++;
if (stp->arity > MAX_ARG) {
BeamLoadError1(stp, "too many arguments: %d", stp->arity);
}
#ifdef DEBUG
{
int i;
/* Should not be referenced. */
ASSERT(stp->labels[0].num_patches == 0);
for (i = 1; i < stp->beam.code.label_count; i++) {
ASSERT(stp->labels[i].num_patches <= stp->labels[i].num_allocated);
}
}
#endif
}
break;
case op_int_func_end:
{
/*
* Native function calls may be larger than their stubs, so
* we'll need to make sure any potentially-native function stub
* is padded with enough room.
*/
int padding_required;
ci--; /* Get rid of the instruction */
padding_required = stp->may_load_nif ||
is_bif(stp->module, stp->function, stp->arity);
ASSERT(stp->last_func_start);
if (padding_required) {
Sint pad = BEAM_NATIVE_MIN_FUNC_SZ - (ci - stp->last_func_start);
if (pad > 0) {
ASSERT(pad < BEAM_NATIVE_MIN_FUNC_SZ);
CodeNeed(pad);
while (pad-- > 0) {
/*
* Filling with actual instructions (instead
* of zeroes) will look nicer in a disassembly
* listing.
*/
code[ci++] = BeamOpCodeAddr(op_padding);
}
}
}
}
break;
case op_on_load:
ci--; /* Get rid of the instruction */
/* Remember offset for the on_load function. */
stp->on_load = ci;
break;
case op_bs_put_string_WW:
case op_i_bs_match_string_xfWW:
case op_i_bs_match_string_yfWW:
new_string_patch(stp, ci-1);
break;
case op_catch_yf:
/* code[ci-3] &&lb_catch_yf
* code[ci-2] y-register offset in E
* code[ci-1] label; index tagged as CATCH at runtime
*/
code[ci-3] = stp->catches;
stp->catches = ci-3;
break;
case op_line_I:
if (stp->line_instr) {
BeamInstr item = code[ci-1];
unsigned int li;
if (item >= stp->beam.lines.item_count) {
BeamLoadError2(stp, "line instruction index overflow (%u/%u)",
item, stp->beam.lines.item_count);
}
li = stp->current_li;
if (li >= stp->beam.lines.instruction_count) {
BeamLoadError2(stp, "line instruction table overflow (%u/%u)",
li, stp->beam.lines.instruction_count);
}
if (ci - 2 == stp->last_func_start) {
/*
* This line instruction directly follows the func_info
* instruction. Its address must be adjusted to point to
* func_info instruction.
*/
stp->line_instr[li].pos = stp->last_func_start - FUNC_INFO_SZ;
stp->line_instr[li].loc = item;
stp->current_li++;
} else if (li <= stp->func_line[stp->function_number - 1] ||
stp->line_instr[li-1].loc != item) {
/*
* Only store the location if it is different
* from the previous location in the same function.
*/
stp->line_instr[li].pos = ci - 2;
stp->line_instr[li].loc = item;
stp->current_li++;
}
}
ci -= 2; /* Get rid of the instruction */
break;
/* End of code found. */
case op_int_code_end:
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;
}
stp->codev_size = codev_size;
stp->ci = ci;
return 1;
load_error:
return 0;
}