|
/* Code location emitting code. See locations.md for a description of the format. */ |
|
|
|
#define MSB 0x80 |
|
|
|
static void |
|
write_location_byte(struct assembler* a, int val) |
|
{ |
|
PyBytes_AS_STRING(a->a_linetable)[a->a_location_off] = val&255; |
|
a->a_location_off++; |
|
} |
|
|
|
|
|
static uint8_t * |
|
location_pointer(struct assembler* a) |
|
{ |
|
return (uint8_t *)PyBytes_AS_STRING(a->a_linetable) + |
|
a->a_location_off; |
|
} |
|
|
|
static void |
|
write_location_first_byte(struct assembler* a, int code, int length) |
|
{ |
|
a->a_location_off += write_location_entry_start( |
|
location_pointer(a), code, length); |
|
} |
|
|
|
static void |
|
write_location_varint(struct assembler* a, unsigned int val) |
|
{ |
|
uint8_t *ptr = location_pointer(a); |
|
a->a_location_off += write_varint(ptr, val); |
|
} |
|
|
|
|
|
static void |
|
write_location_signed_varint(struct assembler* a, int val) |
|
{ |
|
uint8_t *ptr = location_pointer(a); |
|
a->a_location_off += write_signed_varint(ptr, val); |
|
} |
|
|
|
static void |
|
write_location_info_short_form(struct assembler* a, int length, int column, int end_column) |
|
{ |
|
assert(length > 0 && length <= 8); |
|
int column_low_bits = column & 7; |
|
int column_group = column >> 3; |
|
assert(column < 80); |
|
assert(end_column >= column); |
|
assert(end_column - column < 16); |
|
write_location_first_byte(a, PY_CODE_LOCATION_INFO_SHORT0 + column_group, length); |
|
write_location_byte(a, (column_low_bits << 4) | (end_column - column)); |
|
} |
|
|
|
static void |
|
write_location_info_oneline_form(struct assembler* a, int length, int line_delta, int column, int end_column) |
|
{ |
|
assert(length > 0 && length <= 8); |
|
assert(line_delta >= 0 && line_delta < 3); |
|
assert(column < 128); |
|
assert(end_column < 128); |
|
write_location_first_byte(a, PY_CODE_LOCATION_INFO_ONE_LINE0 + line_delta, length); |
|
write_location_byte(a, column); |
|
write_location_byte(a, end_column); |
|
} |
|
|
|
static void |
|
write_location_info_long_form(struct assembler* a, location loc, int length) |
|
{ |
|
assert(length > 0 && length <= 8); |
|
write_location_first_byte(a, PY_CODE_LOCATION_INFO_LONG, length); |
|
write_location_signed_varint(a, loc.lineno - a->a_lineno); |
|
assert(loc.end_lineno >= loc.lineno); |
|
write_location_varint(a, loc.end_lineno - loc.lineno); |
|
write_location_varint(a, loc.col_offset + 1); |
|
write_location_varint(a, loc.end_col_offset + 1); |
|
} |
|
|
|
static void |
|
write_location_info_none(struct assembler* a, int length) |
|
{ |
|
write_location_first_byte(a, PY_CODE_LOCATION_INFO_NONE, length); |
|
} |
|
|
|
static void |
|
write_location_info_no_column(struct assembler* a, int length, int line_delta) |
|
{ |
|
write_location_first_byte(a, PY_CODE_LOCATION_INFO_NO_COLUMNS, length); |
|
write_location_signed_varint(a, line_delta); |
|
} |
|
|
|
#define THEORETICAL_MAX_ENTRY_SIZE 25 /* 1 + 6 + 6 + 6 + 6 */ |
|
|
|
|
|
static int |
|
write_location_info_entry(struct assembler* a, location loc, int isize) |
|
{ |
|
Py_ssize_t len = PyBytes_GET_SIZE(a->a_linetable); |
|
if (a->a_location_off + THEORETICAL_MAX_ENTRY_SIZE >= len) { |
|
assert(len > THEORETICAL_MAX_ENTRY_SIZE); |
|
RETURN_IF_ERROR(_PyBytes_Resize(&a->a_linetable, len*2)); |
|
} |
|
if (loc.lineno < 0) { |
|
write_location_info_none(a, isize); |
|
return SUCCESS; |
|
} |
|
int line_delta = loc.lineno - a->a_lineno; |
|
int column = loc.col_offset; |
|
int end_column = loc.end_col_offset; |
|
assert(column >= -1); |
|
assert(end_column >= -1); |
|
if (column < 0 || end_column < 0) { |
|
if (loc.end_lineno == loc.lineno || loc.end_lineno == -1) { |
|
write_location_info_no_column(a, isize, line_delta); |
|
a->a_lineno = loc.lineno; |
|
return SUCCESS; |
|
} |
|
} |
|
else if (loc.end_lineno == loc.lineno) { |
|
if (line_delta == 0 && column < 80 && end_column - column < 16 && end_column >= column) { |
|
write_location_info_short_form(a, isize, column, end_column); |
|
return SUCCESS; |
|
} |
|
if (line_delta >= 0 && line_delta < 3 && column < 128 && end_column < 128) { |
|
write_location_info_oneline_form(a, isize, line_delta, column, end_column); |
|
a->a_lineno = loc.lineno; |
|
return SUCCESS; |
|
} |
|
} |
|
write_location_info_long_form(a, loc, isize); |
|
a->a_lineno = loc.lineno; |
|
return SUCCESS; |
|
} |
|
|
|
static int |
|
assemble_emit_location(struct assembler* a, location loc, int isize) |
|
{ |
|
if (isize == 0) { |
|
return SUCCESS; |
|
} |
|
while (isize > 8) { |
|
RETURN_IF_ERROR(write_location_info_entry(a, loc, 8)); |
|
isize -= 8; |
|
} |
|
return write_location_info_entry(a, loc, isize); |
|
} |
|
|
|
static int |
|
assemble_location_info(struct assembler *a, instr_sequence *instrs, |
|
int firstlineno) |
|
{ |
|
a->a_lineno = firstlineno; |
|
location loc = NO_LOCATION; |
|
int size = 0; |
|
for (int i = 0; i < instrs->s_used; i++) { |
|
instruction *instr = &instrs->s_instrs[i]; |
|
if (!same_location(loc, instr->i_loc)) { |
|
RETURN_IF_ERROR(assemble_emit_location(a, loc, size)); |
|
loc = instr->i_loc; |
|
size = 0; |
|
} |
|
size += instr_size(instr); |
|
} |
|
RETURN_IF_ERROR(assemble_emit_location(a, loc, size)); |
|
return SUCCESS; |
|
} |
|
|
|
static void |
|
write_instr(_Py_CODEUNIT *codestr, instruction *instr, int ilen) |
|
{ |
|
int opcode = instr->i_opcode; |
|
assert(!IS_PSEUDO_INSTR(opcode)); |
|
int oparg = instr->i_oparg; |
|
assert(OPCODE_HAS_ARG(opcode) || oparg == 0); |
|
int caches = _PyOpcode_Caches[opcode]; |
|
switch (ilen - caches) { |
|
case 4: |
|
codestr->op.code = EXTENDED_ARG; |
|
codestr->op.arg = (oparg >> 24) & 0xFF; |
|
codestr++; |
|
/* fall through */ |
|
case 3: |
|
codestr->op.code = EXTENDED_ARG; |
|
codestr->op.arg = (oparg >> 16) & 0xFF; |
|
codestr++; |
|
/* fall through */ |
|
case 2: |
|
codestr->op.code = EXTENDED_ARG; |
|
codestr->op.arg = (oparg >> 8) & 0xFF; |
|
codestr++; |
|
/* fall through */ |
|
case 1: |
|
codestr->op.code = opcode; |
|
codestr->op.arg = oparg & 0xFF; |
|
codestr++; |
|
break; |
|
default: |
|
Py_UNREACHABLE(); |
|
} |
|
while (caches--) { |
|
codestr->op.code = CACHE; |
|
codestr->op.arg = 0; |
|
codestr++; |
|
} |
|
} |
|
|
|
/* assemble_emit_instr() |
|
Extend the bytecode with a new instruction. |
|
Update lnotab if necessary. |
|
*/ |
|
|
|
static int |
|
assemble_emit_instr(struct assembler *a, instruction *instr) |
|
{ |
|
Py_ssize_t len = PyBytes_GET_SIZE(a->a_bytecode); |
|
_Py_CODEUNIT *code; |
|
|
|
int size = instr_size(instr); |
|
if (a->a_offset + size >= len / (int)sizeof(_Py_CODEUNIT)) { |
|
if (len > PY_SSIZE_T_MAX / 2) { |
|
return ERROR; |
|
} |
|
RETURN_IF_ERROR(_PyBytes_Resize(&a->a_bytecode, len * 2)); |
|
} |
|
code = (_Py_CODEUNIT *)PyBytes_AS_STRING(a->a_bytecode) + a->a_offset; |
|
a->a_offset += size; |
|
write_instr(code, instr, size); |
|
return SUCCESS; |
|
} |
|
|
|
static int |
|
assemble_emit(struct assembler *a, instr_sequence *instrs, |
|
int first_lineno, PyObject *const_cache) |
|
{ |
|
RETURN_IF_ERROR(assemble_init(a, first_lineno)); |
|
|
|
for (int i = 0; i < instrs->s_used; i++) { |
|
instruction *instr = &instrs->s_instrs[i]; |
|
RETURN_IF_ERROR(assemble_emit_instr(a, instr)); |
|
} |
|
|
|
RETURN_IF_ERROR(assemble_location_info(a, instrs, a->a_lineno)); |
|
|
|
RETURN_IF_ERROR(assemble_exception_table(a, instrs)); |
|
|
|
RETURN_IF_ERROR(_PyBytes_Resize(&a->a_except_table, a->a_except_table_off)); |
|
RETURN_IF_ERROR(_PyCompile_ConstCacheMergeOne(const_cache, &a->a_except_table)); |
|
|
|
RETURN_IF_ERROR(_PyBytes_Resize(&a->a_linetable, a->a_location_off)); |
|
RETURN_IF_ERROR(_PyCompile_ConstCacheMergeOne(const_cache, &a->a_linetable)); |
|
|
|
RETURN_IF_ERROR(_PyBytes_Resize(&a->a_bytecode, a->a_offset * sizeof(_Py_CODEUNIT))); |
|
RETURN_IF_ERROR(_PyCompile_ConstCacheMergeOne(const_cache, &a->a_bytecode)); |
|
return SUCCESS; |
|
} |