feat(Ubiquitous/RT_Thread): port micropython on RT-Thread for aiit-board
This commit is contained in:
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013, 2014 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "py/runtime.h"
|
||||
|
||||
void mp_arg_check_num_sig(size_t n_args, size_t n_kw, uint32_t sig) {
|
||||
// TODO maybe take the function name as an argument so we can print nicer error messages
|
||||
|
||||
// The reverse of MP_OBJ_FUN_MAKE_SIG
|
||||
bool takes_kw = sig & 1;
|
||||
size_t n_args_min = sig >> 17;
|
||||
size_t n_args_max = (sig >> 1) & 0xffff;
|
||||
|
||||
if (n_kw && !takes_kw) {
|
||||
#if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE
|
||||
mp_arg_error_terse_mismatch();
|
||||
#else
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("function doesn't take keyword arguments"));
|
||||
#endif
|
||||
}
|
||||
|
||||
if (n_args_min == n_args_max) {
|
||||
if (n_args != n_args_min) {
|
||||
#if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE
|
||||
mp_arg_error_terse_mismatch();
|
||||
#else
|
||||
mp_raise_msg_varg(&mp_type_TypeError,
|
||||
MP_ERROR_TEXT("function takes %d positional arguments but %d were given"),
|
||||
n_args_min, n_args);
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
if (n_args < n_args_min) {
|
||||
#if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE
|
||||
mp_arg_error_terse_mismatch();
|
||||
#else
|
||||
mp_raise_msg_varg(&mp_type_TypeError,
|
||||
MP_ERROR_TEXT("function missing %d required positional arguments"),
|
||||
n_args_min - n_args);
|
||||
#endif
|
||||
} else if (n_args > n_args_max) {
|
||||
#if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE
|
||||
mp_arg_error_terse_mismatch();
|
||||
#else
|
||||
mp_raise_msg_varg(&mp_type_TypeError,
|
||||
MP_ERROR_TEXT("function expected at most %d arguments, got %d"),
|
||||
n_args_max, n_args);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mp_arg_parse_all(size_t n_pos, const mp_obj_t *pos, mp_map_t *kws, size_t n_allowed, const mp_arg_t *allowed, mp_arg_val_t *out_vals) {
|
||||
size_t pos_found = 0, kws_found = 0;
|
||||
for (size_t i = 0; i < n_allowed; i++) {
|
||||
mp_obj_t given_arg;
|
||||
if (i < n_pos) {
|
||||
if (allowed[i].flags & MP_ARG_KW_ONLY) {
|
||||
goto extra_positional;
|
||||
}
|
||||
pos_found++;
|
||||
given_arg = pos[i];
|
||||
} else {
|
||||
mp_map_elem_t *kw = mp_map_lookup(kws, MP_OBJ_NEW_QSTR(allowed[i].qst), MP_MAP_LOOKUP);
|
||||
if (kw == NULL) {
|
||||
if (allowed[i].flags & MP_ARG_REQUIRED) {
|
||||
#if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE
|
||||
mp_arg_error_terse_mismatch();
|
||||
#else
|
||||
mp_raise_msg_varg(&mp_type_TypeError, MP_ERROR_TEXT("'%q' argument required"), allowed[i].qst);
|
||||
#endif
|
||||
}
|
||||
out_vals[i] = allowed[i].defval;
|
||||
continue;
|
||||
} else {
|
||||
kws_found++;
|
||||
given_arg = kw->value;
|
||||
}
|
||||
}
|
||||
if ((allowed[i].flags & MP_ARG_KIND_MASK) == MP_ARG_BOOL) {
|
||||
out_vals[i].u_bool = mp_obj_is_true(given_arg);
|
||||
} else if ((allowed[i].flags & MP_ARG_KIND_MASK) == MP_ARG_INT) {
|
||||
out_vals[i].u_int = mp_obj_get_int(given_arg);
|
||||
} else {
|
||||
assert((allowed[i].flags & MP_ARG_KIND_MASK) == MP_ARG_OBJ);
|
||||
out_vals[i].u_obj = given_arg;
|
||||
}
|
||||
}
|
||||
if (pos_found < n_pos) {
|
||||
extra_positional:
|
||||
#if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE
|
||||
mp_arg_error_terse_mismatch();
|
||||
#else
|
||||
// TODO better error message
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("extra positional arguments given"));
|
||||
#endif
|
||||
}
|
||||
if (kws_found < kws->used) {
|
||||
#if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE
|
||||
mp_arg_error_terse_mismatch();
|
||||
#else
|
||||
// TODO better error message
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("extra keyword arguments given"));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void mp_arg_parse_all_kw_array(size_t n_pos, size_t n_kw, const mp_obj_t *args, size_t n_allowed, const mp_arg_t *allowed, mp_arg_val_t *out_vals) {
|
||||
mp_map_t kw_args;
|
||||
mp_map_init_fixed_table(&kw_args, n_kw, args + n_pos);
|
||||
mp_arg_parse_all(n_pos, args, &kw_args, n_allowed, allowed, out_vals);
|
||||
}
|
||||
|
||||
NORETURN void mp_arg_error_terse_mismatch(void) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("argument num/types mismatch"));
|
||||
}
|
||||
|
||||
#if MICROPY_CPYTHON_COMPAT
|
||||
NORETURN void mp_arg_error_unimpl_kw(void) {
|
||||
mp_raise_NotImplementedError(MP_ERROR_TEXT("keyword argument(s) not yet implemented - use normal args instead"));
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,391 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014 Fabian Vogt
|
||||
* Copyright (c) 2013, 2014 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "py/mpconfig.h"
|
||||
|
||||
// wrapper around everything in this file
|
||||
#if MICROPY_EMIT_ARM
|
||||
|
||||
#include "py/asmarm.h"
|
||||
|
||||
#define SIGNED_FIT24(x) (((x) & 0xff800000) == 0) || (((x) & 0xff000000) == 0xff000000)
|
||||
|
||||
void asm_arm_end_pass(asm_arm_t *as) {
|
||||
if (as->base.pass == MP_ASM_PASS_EMIT) {
|
||||
#if defined(__linux__) && defined(__GNUC__)
|
||||
char *start = mp_asm_base_get_code(&as->base);
|
||||
char *end = start + mp_asm_base_get_code_size(&as->base);
|
||||
__builtin___clear_cache(start, end);
|
||||
#elif defined(__arm__)
|
||||
// flush I- and D-cache
|
||||
asm volatile (
|
||||
"0:"
|
||||
"mrc p15, 0, r15, c7, c10, 3\n"
|
||||
"bne 0b\n"
|
||||
"mov r0, #0\n"
|
||||
"mcr p15, 0, r0, c7, c7, 0\n"
|
||||
: : : "r0", "cc");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// Insert word into instruction flow
|
||||
STATIC void emit(asm_arm_t *as, uint op) {
|
||||
uint8_t *c = mp_asm_base_get_cur_to_write_bytes(&as->base, 4);
|
||||
if (c != NULL) {
|
||||
*(uint32_t *)c = op;
|
||||
}
|
||||
}
|
||||
|
||||
// Insert word into instruction flow, add "ALWAYS" condition code
|
||||
STATIC void emit_al(asm_arm_t *as, uint op) {
|
||||
emit(as, op | ASM_ARM_CC_AL);
|
||||
}
|
||||
|
||||
// Basic instructions without condition code
|
||||
STATIC uint asm_arm_op_push(uint reglist) {
|
||||
// stmfd sp!, {reglist}
|
||||
return 0x92d0000 | (reglist & 0xFFFF);
|
||||
}
|
||||
|
||||
STATIC uint asm_arm_op_pop(uint reglist) {
|
||||
// ldmfd sp!, {reglist}
|
||||
return 0x8bd0000 | (reglist & 0xFFFF);
|
||||
}
|
||||
|
||||
STATIC uint asm_arm_op_mov_reg(uint rd, uint rn) {
|
||||
// mov rd, rn
|
||||
return 0x1a00000 | (rd << 12) | rn;
|
||||
}
|
||||
|
||||
STATIC uint asm_arm_op_mov_imm(uint rd, uint imm) {
|
||||
// mov rd, #imm
|
||||
return 0x3a00000 | (rd << 12) | imm;
|
||||
}
|
||||
|
||||
STATIC uint asm_arm_op_mvn_imm(uint rd, uint imm) {
|
||||
// mvn rd, #imm
|
||||
return 0x3e00000 | (rd << 12) | imm;
|
||||
}
|
||||
|
||||
STATIC uint asm_arm_op_add_imm(uint rd, uint rn, uint imm) {
|
||||
// add rd, rn, #imm
|
||||
return 0x2800000 | (rn << 16) | (rd << 12) | (imm & 0xFF);
|
||||
}
|
||||
|
||||
STATIC uint asm_arm_op_add_reg(uint rd, uint rn, uint rm) {
|
||||
// add rd, rn, rm
|
||||
return 0x0800000 | (rn << 16) | (rd << 12) | rm;
|
||||
}
|
||||
|
||||
STATIC uint asm_arm_op_sub_imm(uint rd, uint rn, uint imm) {
|
||||
// sub rd, rn, #imm
|
||||
return 0x2400000 | (rn << 16) | (rd << 12) | (imm & 0xFF);
|
||||
}
|
||||
|
||||
STATIC uint asm_arm_op_sub_reg(uint rd, uint rn, uint rm) {
|
||||
// sub rd, rn, rm
|
||||
return 0x0400000 | (rn << 16) | (rd << 12) | rm;
|
||||
}
|
||||
|
||||
STATIC uint asm_arm_op_mul_reg(uint rd, uint rm, uint rs) {
|
||||
// mul rd, rm, rs
|
||||
assert(rd != rm);
|
||||
return 0x0000090 | (rd << 16) | (rs << 8) | rm;
|
||||
}
|
||||
|
||||
STATIC uint asm_arm_op_and_reg(uint rd, uint rn, uint rm) {
|
||||
// and rd, rn, rm
|
||||
return 0x0000000 | (rn << 16) | (rd << 12) | rm;
|
||||
}
|
||||
|
||||
STATIC uint asm_arm_op_eor_reg(uint rd, uint rn, uint rm) {
|
||||
// eor rd, rn, rm
|
||||
return 0x0200000 | (rn << 16) | (rd << 12) | rm;
|
||||
}
|
||||
|
||||
STATIC uint asm_arm_op_orr_reg(uint rd, uint rn, uint rm) {
|
||||
// orr rd, rn, rm
|
||||
return 0x1800000 | (rn << 16) | (rd << 12) | rm;
|
||||
}
|
||||
|
||||
void asm_arm_bkpt(asm_arm_t *as) {
|
||||
// bkpt #0
|
||||
emit_al(as, 0x1200070);
|
||||
}
|
||||
|
||||
// locals:
|
||||
// - stored on the stack in ascending order
|
||||
// - numbered 0 through num_locals-1
|
||||
// - SP points to first local
|
||||
//
|
||||
// | SP
|
||||
// v
|
||||
// l0 l1 l2 ... l(n-1)
|
||||
// ^ ^
|
||||
// | low address | high address in RAM
|
||||
|
||||
void asm_arm_entry(asm_arm_t *as, int num_locals) {
|
||||
assert(num_locals >= 0);
|
||||
|
||||
as->stack_adjust = 0;
|
||||
as->push_reglist = 1 << ASM_ARM_REG_R1
|
||||
| 1 << ASM_ARM_REG_R2
|
||||
| 1 << ASM_ARM_REG_R3
|
||||
| 1 << ASM_ARM_REG_R4
|
||||
| 1 << ASM_ARM_REG_R5
|
||||
| 1 << ASM_ARM_REG_R6
|
||||
| 1 << ASM_ARM_REG_R7
|
||||
| 1 << ASM_ARM_REG_R8;
|
||||
|
||||
// Only adjust the stack if there are more locals than usable registers
|
||||
if (num_locals > 3) {
|
||||
as->stack_adjust = num_locals * 4;
|
||||
// Align stack to 8 bytes
|
||||
if (num_locals & 1) {
|
||||
as->stack_adjust += 4;
|
||||
}
|
||||
}
|
||||
|
||||
emit_al(as, asm_arm_op_push(as->push_reglist | 1 << ASM_ARM_REG_LR));
|
||||
if (as->stack_adjust > 0) {
|
||||
emit_al(as, asm_arm_op_sub_imm(ASM_ARM_REG_SP, ASM_ARM_REG_SP, as->stack_adjust));
|
||||
}
|
||||
}
|
||||
|
||||
void asm_arm_exit(asm_arm_t *as) {
|
||||
if (as->stack_adjust > 0) {
|
||||
emit_al(as, asm_arm_op_add_imm(ASM_ARM_REG_SP, ASM_ARM_REG_SP, as->stack_adjust));
|
||||
}
|
||||
|
||||
emit_al(as, asm_arm_op_pop(as->push_reglist | (1 << ASM_ARM_REG_PC)));
|
||||
}
|
||||
|
||||
void asm_arm_push(asm_arm_t *as, uint reglist) {
|
||||
emit_al(as, asm_arm_op_push(reglist));
|
||||
}
|
||||
|
||||
void asm_arm_pop(asm_arm_t *as, uint reglist) {
|
||||
emit_al(as, asm_arm_op_pop(reglist));
|
||||
}
|
||||
|
||||
void asm_arm_mov_reg_reg(asm_arm_t *as, uint reg_dest, uint reg_src) {
|
||||
emit_al(as, asm_arm_op_mov_reg(reg_dest, reg_src));
|
||||
}
|
||||
|
||||
size_t asm_arm_mov_reg_i32(asm_arm_t *as, uint rd, int imm) {
|
||||
// Insert immediate into code and jump over it
|
||||
emit_al(as, 0x59f0000 | (rd << 12)); // ldr rd, [pc]
|
||||
emit_al(as, 0xa000000); // b pc
|
||||
size_t loc = mp_asm_base_get_code_pos(&as->base);
|
||||
emit(as, imm);
|
||||
return loc;
|
||||
}
|
||||
|
||||
void asm_arm_mov_reg_i32_optimised(asm_arm_t *as, uint rd, int imm) {
|
||||
// TODO: There are more variants of immediate values
|
||||
if ((imm & 0xFF) == imm) {
|
||||
emit_al(as, asm_arm_op_mov_imm(rd, imm));
|
||||
} else if (imm < 0 && imm >= -256) {
|
||||
// mvn is "move not", not "move negative"
|
||||
emit_al(as, asm_arm_op_mvn_imm(rd, ~imm));
|
||||
} else {
|
||||
asm_arm_mov_reg_i32(as, rd, imm);
|
||||
}
|
||||
}
|
||||
|
||||
void asm_arm_mov_local_reg(asm_arm_t *as, int local_num, uint rd) {
|
||||
// str rd, [sp, #local_num*4]
|
||||
emit_al(as, 0x58d0000 | (rd << 12) | (local_num << 2));
|
||||
}
|
||||
|
||||
void asm_arm_mov_reg_local(asm_arm_t *as, uint rd, int local_num) {
|
||||
// ldr rd, [sp, #local_num*4]
|
||||
emit_al(as, 0x59d0000 | (rd << 12) | (local_num << 2));
|
||||
}
|
||||
|
||||
void asm_arm_cmp_reg_i8(asm_arm_t *as, uint rd, int imm) {
|
||||
// cmp rd, #imm
|
||||
emit_al(as, 0x3500000 | (rd << 16) | (imm & 0xFF));
|
||||
}
|
||||
|
||||
void asm_arm_cmp_reg_reg(asm_arm_t *as, uint rd, uint rn) {
|
||||
// cmp rd, rn
|
||||
emit_al(as, 0x1500000 | (rd << 16) | rn);
|
||||
}
|
||||
|
||||
void asm_arm_setcc_reg(asm_arm_t *as, uint rd, uint cond) {
|
||||
emit(as, asm_arm_op_mov_imm(rd, 1) | cond); // movCOND rd, #1
|
||||
emit(as, asm_arm_op_mov_imm(rd, 0) | (cond ^ (1 << 28))); // mov!COND rd, #0
|
||||
}
|
||||
|
||||
void asm_arm_add_reg_reg_reg(asm_arm_t *as, uint rd, uint rn, uint rm) {
|
||||
// add rd, rn, rm
|
||||
emit_al(as, asm_arm_op_add_reg(rd, rn, rm));
|
||||
}
|
||||
|
||||
void asm_arm_sub_reg_reg_reg(asm_arm_t *as, uint rd, uint rn, uint rm) {
|
||||
// sub rd, rn, rm
|
||||
emit_al(as, asm_arm_op_sub_reg(rd, rn, rm));
|
||||
}
|
||||
|
||||
void asm_arm_mul_reg_reg_reg(asm_arm_t *as, uint rd, uint rs, uint rm) {
|
||||
// rs and rm are swapped because of restriction rd!=rm
|
||||
// mul rd, rm, rs
|
||||
emit_al(as, asm_arm_op_mul_reg(rd, rm, rs));
|
||||
}
|
||||
|
||||
void asm_arm_and_reg_reg_reg(asm_arm_t *as, uint rd, uint rn, uint rm) {
|
||||
// and rd, rn, rm
|
||||
emit_al(as, asm_arm_op_and_reg(rd, rn, rm));
|
||||
}
|
||||
|
||||
void asm_arm_eor_reg_reg_reg(asm_arm_t *as, uint rd, uint rn, uint rm) {
|
||||
// eor rd, rn, rm
|
||||
emit_al(as, asm_arm_op_eor_reg(rd, rn, rm));
|
||||
}
|
||||
|
||||
void asm_arm_orr_reg_reg_reg(asm_arm_t *as, uint rd, uint rn, uint rm) {
|
||||
// orr rd, rn, rm
|
||||
emit_al(as, asm_arm_op_orr_reg(rd, rn, rm));
|
||||
}
|
||||
|
||||
void asm_arm_mov_reg_local_addr(asm_arm_t *as, uint rd, int local_num) {
|
||||
// add rd, sp, #local_num*4
|
||||
emit_al(as, asm_arm_op_add_imm(rd, ASM_ARM_REG_SP, local_num << 2));
|
||||
}
|
||||
|
||||
void asm_arm_mov_reg_pcrel(asm_arm_t *as, uint reg_dest, uint label) {
|
||||
assert(label < as->base.max_num_labels);
|
||||
mp_uint_t dest = as->base.label_offsets[label];
|
||||
mp_int_t rel = dest - as->base.code_offset;
|
||||
rel -= 12 + 8; // adjust for load of rel, and then PC+8 prefetch of add_reg_reg_reg
|
||||
|
||||
// To load rel int reg_dest, insert immediate into code and jump over it
|
||||
emit_al(as, 0x59f0000 | (reg_dest << 12)); // ldr rd, [pc]
|
||||
emit_al(as, 0xa000000); // b pc
|
||||
emit(as, rel);
|
||||
|
||||
// Do reg_dest += PC
|
||||
asm_arm_add_reg_reg_reg(as, reg_dest, reg_dest, ASM_ARM_REG_PC);
|
||||
}
|
||||
|
||||
void asm_arm_lsl_reg_reg(asm_arm_t *as, uint rd, uint rs) {
|
||||
// mov rd, rd, lsl rs
|
||||
emit_al(as, 0x1a00010 | (rd << 12) | (rs << 8) | rd);
|
||||
}
|
||||
|
||||
void asm_arm_lsr_reg_reg(asm_arm_t *as, uint rd, uint rs) {
|
||||
// mov rd, rd, lsr rs
|
||||
emit_al(as, 0x1a00030 | (rd << 12) | (rs << 8) | rd);
|
||||
}
|
||||
|
||||
void asm_arm_asr_reg_reg(asm_arm_t *as, uint rd, uint rs) {
|
||||
// mov rd, rd, asr rs
|
||||
emit_al(as, 0x1a00050 | (rd << 12) | (rs << 8) | rd);
|
||||
}
|
||||
|
||||
void asm_arm_ldr_reg_reg(asm_arm_t *as, uint rd, uint rn, uint byte_offset) {
|
||||
// ldr rd, [rn, #off]
|
||||
emit_al(as, 0x5900000 | (rn << 16) | (rd << 12) | byte_offset);
|
||||
}
|
||||
|
||||
void asm_arm_ldrh_reg_reg(asm_arm_t *as, uint rd, uint rn) {
|
||||
// ldrh rd, [rn]
|
||||
emit_al(as, 0x1d000b0 | (rn << 16) | (rd << 12));
|
||||
}
|
||||
|
||||
void asm_arm_ldrb_reg_reg(asm_arm_t *as, uint rd, uint rn) {
|
||||
// ldrb rd, [rn]
|
||||
emit_al(as, 0x5d00000 | (rn << 16) | (rd << 12));
|
||||
}
|
||||
|
||||
void asm_arm_str_reg_reg(asm_arm_t *as, uint rd, uint rm, uint byte_offset) {
|
||||
// str rd, [rm, #off]
|
||||
emit_al(as, 0x5800000 | (rm << 16) | (rd << 12) | byte_offset);
|
||||
}
|
||||
|
||||
void asm_arm_strh_reg_reg(asm_arm_t *as, uint rd, uint rm) {
|
||||
// strh rd, [rm]
|
||||
emit_al(as, 0x1c000b0 | (rm << 16) | (rd << 12));
|
||||
}
|
||||
|
||||
void asm_arm_strb_reg_reg(asm_arm_t *as, uint rd, uint rm) {
|
||||
// strb rd, [rm]
|
||||
emit_al(as, 0x5c00000 | (rm << 16) | (rd << 12));
|
||||
}
|
||||
|
||||
void asm_arm_str_reg_reg_reg(asm_arm_t *as, uint rd, uint rm, uint rn) {
|
||||
// str rd, [rm, rn, lsl #2]
|
||||
emit_al(as, 0x7800100 | (rm << 16) | (rd << 12) | rn);
|
||||
}
|
||||
|
||||
void asm_arm_strh_reg_reg_reg(asm_arm_t *as, uint rd, uint rm, uint rn) {
|
||||
// strh doesn't support scaled register index
|
||||
emit_al(as, 0x1a00080 | (ASM_ARM_REG_R8 << 12) | rn); // mov r8, rn, lsl #1
|
||||
emit_al(as, 0x18000b0 | (rm << 16) | (rd << 12) | ASM_ARM_REG_R8); // strh rd, [rm, r8]
|
||||
}
|
||||
|
||||
void asm_arm_strb_reg_reg_reg(asm_arm_t *as, uint rd, uint rm, uint rn) {
|
||||
// strb rd, [rm, rn]
|
||||
emit_al(as, 0x7c00000 | (rm << 16) | (rd << 12) | rn);
|
||||
}
|
||||
|
||||
void asm_arm_bcc_label(asm_arm_t *as, int cond, uint label) {
|
||||
assert(label < as->base.max_num_labels);
|
||||
mp_uint_t dest = as->base.label_offsets[label];
|
||||
mp_int_t rel = dest - as->base.code_offset;
|
||||
rel -= 8; // account for instruction prefetch, PC is 8 bytes ahead of this instruction
|
||||
rel >>= 2; // in ARM mode the branch target is 32-bit aligned, so the 2 LSB are omitted
|
||||
|
||||
if (SIGNED_FIT24(rel)) {
|
||||
emit(as, cond | 0xa000000 | (rel & 0xffffff));
|
||||
} else {
|
||||
printf("asm_arm_bcc: branch does not fit in 24 bits\n");
|
||||
}
|
||||
}
|
||||
|
||||
void asm_arm_b_label(asm_arm_t *as, uint label) {
|
||||
asm_arm_bcc_label(as, ASM_ARM_CC_AL, label);
|
||||
}
|
||||
|
||||
void asm_arm_bl_ind(asm_arm_t *as, uint fun_id, uint reg_temp) {
|
||||
// The table offset should fit into the ldr instruction
|
||||
assert(fun_id < (0x1000 / 4));
|
||||
emit_al(as, asm_arm_op_mov_reg(ASM_ARM_REG_LR, ASM_ARM_REG_PC)); // mov lr, pc
|
||||
emit_al(as, 0x597f000 | (fun_id << 2)); // ldr pc, [r7, #fun_id*4]
|
||||
}
|
||||
|
||||
void asm_arm_bx_reg(asm_arm_t *as, uint reg_src) {
|
||||
emit_al(as, 0x012fff10 | reg_src);
|
||||
}
|
||||
|
||||
#endif // MICROPY_EMIT_ARM
|
||||
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014 Fabian Vogt
|
||||
* Copyright (c) 2013, 2014 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef MICROPY_INCLUDED_PY_ASMARM_H
|
||||
#define MICROPY_INCLUDED_PY_ASMARM_H
|
||||
|
||||
#include "py/misc.h"
|
||||
#include "py/asmbase.h"
|
||||
|
||||
#define ASM_ARM_REG_R0 (0)
|
||||
#define ASM_ARM_REG_R1 (1)
|
||||
#define ASM_ARM_REG_R2 (2)
|
||||
#define ASM_ARM_REG_R3 (3)
|
||||
#define ASM_ARM_REG_R4 (4)
|
||||
#define ASM_ARM_REG_R5 (5)
|
||||
#define ASM_ARM_REG_R6 (6)
|
||||
#define ASM_ARM_REG_R7 (7)
|
||||
#define ASM_ARM_REG_R8 (8)
|
||||
#define ASM_ARM_REG_R9 (9)
|
||||
#define ASM_ARM_REG_R10 (10)
|
||||
#define ASM_ARM_REG_R11 (11)
|
||||
#define ASM_ARM_REG_R12 (12)
|
||||
#define ASM_ARM_REG_R13 (13)
|
||||
#define ASM_ARM_REG_R14 (14)
|
||||
#define ASM_ARM_REG_R15 (15)
|
||||
#define ASM_ARM_REG_SP (ASM_ARM_REG_R13)
|
||||
#define ASM_ARM_REG_LR (ASM_ARM_REG_R14)
|
||||
#define ASM_ARM_REG_PC (ASM_ARM_REG_R15)
|
||||
|
||||
#define ASM_ARM_CC_EQ (0x0 << 28)
|
||||
#define ASM_ARM_CC_NE (0x1 << 28)
|
||||
#define ASM_ARM_CC_CS (0x2 << 28)
|
||||
#define ASM_ARM_CC_CC (0x3 << 28)
|
||||
#define ASM_ARM_CC_MI (0x4 << 28)
|
||||
#define ASM_ARM_CC_PL (0x5 << 28)
|
||||
#define ASM_ARM_CC_VS (0x6 << 28)
|
||||
#define ASM_ARM_CC_VC (0x7 << 28)
|
||||
#define ASM_ARM_CC_HI (0x8 << 28)
|
||||
#define ASM_ARM_CC_LS (0x9 << 28)
|
||||
#define ASM_ARM_CC_GE (0xa << 28)
|
||||
#define ASM_ARM_CC_LT (0xb << 28)
|
||||
#define ASM_ARM_CC_GT (0xc << 28)
|
||||
#define ASM_ARM_CC_LE (0xd << 28)
|
||||
#define ASM_ARM_CC_AL (0xe << 28)
|
||||
|
||||
typedef struct _asm_arm_t {
|
||||
mp_asm_base_t base;
|
||||
uint push_reglist;
|
||||
uint stack_adjust;
|
||||
} asm_arm_t;
|
||||
|
||||
void asm_arm_end_pass(asm_arm_t *as);
|
||||
|
||||
void asm_arm_entry(asm_arm_t *as, int num_locals);
|
||||
void asm_arm_exit(asm_arm_t *as);
|
||||
|
||||
void asm_arm_bkpt(asm_arm_t *as);
|
||||
|
||||
// mov
|
||||
void asm_arm_mov_reg_reg(asm_arm_t *as, uint reg_dest, uint reg_src);
|
||||
size_t asm_arm_mov_reg_i32(asm_arm_t *as, uint rd, int imm);
|
||||
void asm_arm_mov_reg_i32_optimised(asm_arm_t *as, uint rd, int imm);
|
||||
void asm_arm_mov_local_reg(asm_arm_t *as, int local_num, uint rd);
|
||||
void asm_arm_mov_reg_local(asm_arm_t *as, uint rd, int local_num);
|
||||
void asm_arm_setcc_reg(asm_arm_t *as, uint rd, uint cond);
|
||||
|
||||
// compare
|
||||
void asm_arm_cmp_reg_i8(asm_arm_t *as, uint rd, int imm);
|
||||
void asm_arm_cmp_reg_reg(asm_arm_t *as, uint rd, uint rn);
|
||||
|
||||
// arithmetic
|
||||
void asm_arm_add_reg_reg_reg(asm_arm_t *as, uint rd, uint rn, uint rm);
|
||||
void asm_arm_sub_reg_reg_reg(asm_arm_t *as, uint rd, uint rn, uint rm);
|
||||
void asm_arm_mul_reg_reg_reg(asm_arm_t *as, uint rd, uint rn, uint rm);
|
||||
void asm_arm_and_reg_reg_reg(asm_arm_t *as, uint rd, uint rn, uint rm);
|
||||
void asm_arm_eor_reg_reg_reg(asm_arm_t *as, uint rd, uint rn, uint rm);
|
||||
void asm_arm_orr_reg_reg_reg(asm_arm_t *as, uint rd, uint rn, uint rm);
|
||||
void asm_arm_mov_reg_local_addr(asm_arm_t *as, uint rd, int local_num);
|
||||
void asm_arm_mov_reg_pcrel(asm_arm_t *as, uint reg_dest, uint label);
|
||||
void asm_arm_lsl_reg_reg(asm_arm_t *as, uint rd, uint rs);
|
||||
void asm_arm_lsr_reg_reg(asm_arm_t *as, uint rd, uint rs);
|
||||
void asm_arm_asr_reg_reg(asm_arm_t *as, uint rd, uint rs);
|
||||
|
||||
// memory
|
||||
void asm_arm_ldr_reg_reg(asm_arm_t *as, uint rd, uint rn, uint byte_offset);
|
||||
void asm_arm_ldrh_reg_reg(asm_arm_t *as, uint rd, uint rn);
|
||||
void asm_arm_ldrb_reg_reg(asm_arm_t *as, uint rd, uint rn);
|
||||
void asm_arm_str_reg_reg(asm_arm_t *as, uint rd, uint rm, uint byte_offset);
|
||||
void asm_arm_strh_reg_reg(asm_arm_t *as, uint rd, uint rm);
|
||||
void asm_arm_strb_reg_reg(asm_arm_t *as, uint rd, uint rm);
|
||||
// store to array
|
||||
void asm_arm_str_reg_reg_reg(asm_arm_t *as, uint rd, uint rm, uint rn);
|
||||
void asm_arm_strh_reg_reg_reg(asm_arm_t *as, uint rd, uint rm, uint rn);
|
||||
void asm_arm_strb_reg_reg_reg(asm_arm_t *as, uint rd, uint rm, uint rn);
|
||||
|
||||
// stack
|
||||
void asm_arm_push(asm_arm_t *as, uint reglist);
|
||||
void asm_arm_pop(asm_arm_t *as, uint reglist);
|
||||
|
||||
// control flow
|
||||
void asm_arm_bcc_label(asm_arm_t *as, int cond, uint label);
|
||||
void asm_arm_b_label(asm_arm_t *as, uint label);
|
||||
void asm_arm_bl_ind(asm_arm_t *as, uint fun_id, uint reg_temp);
|
||||
void asm_arm_bx_reg(asm_arm_t *as, uint reg_src);
|
||||
|
||||
// Holds a pointer to mp_fun_table
|
||||
#define ASM_ARM_REG_FUN_TABLE ASM_ARM_REG_R7
|
||||
|
||||
#if GENERIC_ASM_API
|
||||
|
||||
// The following macros provide a (mostly) arch-independent API to
|
||||
// generate native code, and are used by the native emitter.
|
||||
|
||||
#define ASM_WORD_SIZE (4)
|
||||
|
||||
#define REG_RET ASM_ARM_REG_R0
|
||||
#define REG_ARG_1 ASM_ARM_REG_R0
|
||||
#define REG_ARG_2 ASM_ARM_REG_R1
|
||||
#define REG_ARG_3 ASM_ARM_REG_R2
|
||||
#define REG_ARG_4 ASM_ARM_REG_R3
|
||||
|
||||
#define REG_TEMP0 ASM_ARM_REG_R0
|
||||
#define REG_TEMP1 ASM_ARM_REG_R1
|
||||
#define REG_TEMP2 ASM_ARM_REG_R2
|
||||
|
||||
#define REG_LOCAL_1 ASM_ARM_REG_R4
|
||||
#define REG_LOCAL_2 ASM_ARM_REG_R5
|
||||
#define REG_LOCAL_3 ASM_ARM_REG_R6
|
||||
#define REG_LOCAL_NUM (3)
|
||||
|
||||
// Holds a pointer to mp_fun_table
|
||||
#define REG_FUN_TABLE ASM_ARM_REG_FUN_TABLE
|
||||
|
||||
#define ASM_T asm_arm_t
|
||||
#define ASM_END_PASS asm_arm_end_pass
|
||||
#define ASM_ENTRY asm_arm_entry
|
||||
#define ASM_EXIT asm_arm_exit
|
||||
|
||||
#define ASM_JUMP asm_arm_b_label
|
||||
#define ASM_JUMP_IF_REG_ZERO(as, reg, label, bool_test) \
|
||||
do { \
|
||||
asm_arm_cmp_reg_i8(as, reg, 0); \
|
||||
asm_arm_bcc_label(as, ASM_ARM_CC_EQ, label); \
|
||||
} while (0)
|
||||
#define ASM_JUMP_IF_REG_NONZERO(as, reg, label, bool_test) \
|
||||
do { \
|
||||
asm_arm_cmp_reg_i8(as, reg, 0); \
|
||||
asm_arm_bcc_label(as, ASM_ARM_CC_NE, label); \
|
||||
} while (0)
|
||||
#define ASM_JUMP_IF_REG_EQ(as, reg1, reg2, label) \
|
||||
do { \
|
||||
asm_arm_cmp_reg_reg(as, reg1, reg2); \
|
||||
asm_arm_bcc_label(as, ASM_ARM_CC_EQ, label); \
|
||||
} while (0)
|
||||
#define ASM_JUMP_REG(as, reg) asm_arm_bx_reg((as), (reg))
|
||||
#define ASM_CALL_IND(as, idx) asm_arm_bl_ind(as, idx, ASM_ARM_REG_R3)
|
||||
|
||||
#define ASM_MOV_LOCAL_REG(as, local_num, reg_src) asm_arm_mov_local_reg((as), (local_num), (reg_src))
|
||||
#define ASM_MOV_REG_IMM(as, reg_dest, imm) asm_arm_mov_reg_i32_optimised((as), (reg_dest), (imm))
|
||||
#define ASM_MOV_REG_IMM_FIX_U16(as, reg_dest, imm) asm_arm_mov_reg_i32((as), (reg_dest), (imm))
|
||||
#define ASM_MOV_REG_IMM_FIX_WORD(as, reg_dest, imm) asm_arm_mov_reg_i32((as), (reg_dest), (imm))
|
||||
#define ASM_MOV_REG_LOCAL(as, reg_dest, local_num) asm_arm_mov_reg_local((as), (reg_dest), (local_num))
|
||||
#define ASM_MOV_REG_REG(as, reg_dest, reg_src) asm_arm_mov_reg_reg((as), (reg_dest), (reg_src))
|
||||
#define ASM_MOV_REG_LOCAL_ADDR(as, reg_dest, local_num) asm_arm_mov_reg_local_addr((as), (reg_dest), (local_num))
|
||||
#define ASM_MOV_REG_PCREL(as, reg_dest, label) asm_arm_mov_reg_pcrel((as), (reg_dest), (label))
|
||||
|
||||
#define ASM_LSL_REG_REG(as, reg_dest, reg_shift) asm_arm_lsl_reg_reg((as), (reg_dest), (reg_shift))
|
||||
#define ASM_LSR_REG_REG(as, reg_dest, reg_shift) asm_arm_lsr_reg_reg((as), (reg_dest), (reg_shift))
|
||||
#define ASM_ASR_REG_REG(as, reg_dest, reg_shift) asm_arm_asr_reg_reg((as), (reg_dest), (reg_shift))
|
||||
#define ASM_OR_REG_REG(as, reg_dest, reg_src) asm_arm_orr_reg_reg_reg((as), (reg_dest), (reg_dest), (reg_src))
|
||||
#define ASM_XOR_REG_REG(as, reg_dest, reg_src) asm_arm_eor_reg_reg_reg((as), (reg_dest), (reg_dest), (reg_src))
|
||||
#define ASM_AND_REG_REG(as, reg_dest, reg_src) asm_arm_and_reg_reg_reg((as), (reg_dest), (reg_dest), (reg_src))
|
||||
#define ASM_ADD_REG_REG(as, reg_dest, reg_src) asm_arm_add_reg_reg_reg((as), (reg_dest), (reg_dest), (reg_src))
|
||||
#define ASM_SUB_REG_REG(as, reg_dest, reg_src) asm_arm_sub_reg_reg_reg((as), (reg_dest), (reg_dest), (reg_src))
|
||||
#define ASM_MUL_REG_REG(as, reg_dest, reg_src) asm_arm_mul_reg_reg_reg((as), (reg_dest), (reg_dest), (reg_src))
|
||||
|
||||
#define ASM_LOAD_REG_REG(as, reg_dest, reg_base) asm_arm_ldr_reg_reg((as), (reg_dest), (reg_base), 0)
|
||||
#define ASM_LOAD_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_arm_ldr_reg_reg((as), (reg_dest), (reg_base), 4 * (word_offset))
|
||||
#define ASM_LOAD8_REG_REG(as, reg_dest, reg_base) asm_arm_ldrb_reg_reg((as), (reg_dest), (reg_base))
|
||||
#define ASM_LOAD16_REG_REG(as, reg_dest, reg_base) asm_arm_ldrh_reg_reg((as), (reg_dest), (reg_base))
|
||||
#define ASM_LOAD32_REG_REG(as, reg_dest, reg_base) asm_arm_ldr_reg_reg((as), (reg_dest), (reg_base), 0)
|
||||
|
||||
#define ASM_STORE_REG_REG(as, reg_value, reg_base) asm_arm_str_reg_reg((as), (reg_value), (reg_base), 0)
|
||||
#define ASM_STORE_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_arm_str_reg_reg((as), (reg_dest), (reg_base), 4 * (word_offset))
|
||||
#define ASM_STORE8_REG_REG(as, reg_value, reg_base) asm_arm_strb_reg_reg((as), (reg_value), (reg_base))
|
||||
#define ASM_STORE16_REG_REG(as, reg_value, reg_base) asm_arm_strh_reg_reg((as), (reg_value), (reg_base))
|
||||
#define ASM_STORE32_REG_REG(as, reg_value, reg_base) asm_arm_str_reg_reg((as), (reg_value), (reg_base), 0)
|
||||
|
||||
#endif // GENERIC_ASM_API
|
||||
|
||||
#endif // MICROPY_INCLUDED_PY_ASMARM_H
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2016 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "py/obj.h"
|
||||
#include "py/misc.h"
|
||||
#include "py/asmbase.h"
|
||||
|
||||
#if MICROPY_EMIT_MACHINE_CODE
|
||||
|
||||
void mp_asm_base_init(mp_asm_base_t *as, size_t max_num_labels) {
|
||||
as->max_num_labels = max_num_labels;
|
||||
as->label_offsets = m_new(size_t, max_num_labels);
|
||||
}
|
||||
|
||||
void mp_asm_base_deinit(mp_asm_base_t *as, bool free_code) {
|
||||
if (free_code) {
|
||||
MP_PLAT_FREE_EXEC(as->code_base, as->code_size);
|
||||
}
|
||||
m_del(size_t, as->label_offsets, as->max_num_labels);
|
||||
}
|
||||
|
||||
void mp_asm_base_start_pass(mp_asm_base_t *as, int pass) {
|
||||
if (pass < MP_ASM_PASS_EMIT) {
|
||||
// Reset labels so we can detect backwards jumps (and verify unique assignment)
|
||||
memset(as->label_offsets, -1, as->max_num_labels * sizeof(size_t));
|
||||
} else {
|
||||
// allocating executable RAM is platform specific
|
||||
MP_PLAT_ALLOC_EXEC(as->code_offset, (void **)&as->code_base, &as->code_size);
|
||||
assert(as->code_base != NULL);
|
||||
}
|
||||
as->pass = pass;
|
||||
as->code_offset = 0;
|
||||
}
|
||||
|
||||
// all functions must go through this one to emit bytes
|
||||
// if as->pass < MP_ASM_PASS_EMIT, then this function just counts the number
|
||||
// of bytes needed and returns NULL, and callers should not store any data
|
||||
uint8_t *mp_asm_base_get_cur_to_write_bytes(mp_asm_base_t *as, size_t num_bytes_to_write) {
|
||||
uint8_t *c = NULL;
|
||||
if (as->pass == MP_ASM_PASS_EMIT) {
|
||||
assert(as->code_offset + num_bytes_to_write <= as->code_size);
|
||||
c = as->code_base + as->code_offset;
|
||||
}
|
||||
as->code_offset += num_bytes_to_write;
|
||||
return c;
|
||||
}
|
||||
|
||||
void mp_asm_base_label_assign(mp_asm_base_t *as, size_t label) {
|
||||
assert(label < as->max_num_labels);
|
||||
if (as->pass < MP_ASM_PASS_EMIT) {
|
||||
// assign label offset
|
||||
assert(as->label_offsets[label] == (size_t)-1);
|
||||
as->label_offsets[label] = as->code_offset;
|
||||
} else {
|
||||
// ensure label offset has not changed from PASS_COMPUTE to PASS_EMIT
|
||||
assert(as->label_offsets[label] == as->code_offset);
|
||||
}
|
||||
}
|
||||
|
||||
// align must be a multiple of 2
|
||||
void mp_asm_base_align(mp_asm_base_t *as, unsigned int align) {
|
||||
as->code_offset = (as->code_offset + align - 1) & (~(align - 1));
|
||||
}
|
||||
|
||||
// this function assumes a little endian machine
|
||||
void mp_asm_base_data(mp_asm_base_t *as, unsigned int bytesize, uintptr_t val) {
|
||||
uint8_t *c = mp_asm_base_get_cur_to_write_bytes(as, bytesize);
|
||||
if (c != NULL) {
|
||||
for (unsigned int i = 0; i < bytesize; i++) {
|
||||
*c++ = val;
|
||||
val >>= 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // MICROPY_EMIT_MACHINE_CODE
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2016 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef MICROPY_INCLUDED_PY_ASMBASE_H
|
||||
#define MICROPY_INCLUDED_PY_ASMBASE_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define MP_ASM_PASS_COMPUTE (1)
|
||||
#define MP_ASM_PASS_EMIT (2)
|
||||
|
||||
typedef struct _mp_asm_base_t {
|
||||
int pass;
|
||||
size_t code_offset;
|
||||
size_t code_size;
|
||||
uint8_t *code_base;
|
||||
|
||||
size_t max_num_labels;
|
||||
size_t *label_offsets;
|
||||
} mp_asm_base_t;
|
||||
|
||||
void mp_asm_base_init(mp_asm_base_t *as, size_t max_num_labels);
|
||||
void mp_asm_base_deinit(mp_asm_base_t *as, bool free_code);
|
||||
void mp_asm_base_start_pass(mp_asm_base_t *as, int pass);
|
||||
uint8_t *mp_asm_base_get_cur_to_write_bytes(mp_asm_base_t *as, size_t num_bytes_to_write);
|
||||
void mp_asm_base_label_assign(mp_asm_base_t *as, size_t label);
|
||||
void mp_asm_base_align(mp_asm_base_t *as, unsigned int align);
|
||||
void mp_asm_base_data(mp_asm_base_t *as, unsigned int bytesize, uintptr_t val);
|
||||
|
||||
static inline size_t mp_asm_base_get_code_pos(mp_asm_base_t *as) {
|
||||
return as->code_offset;
|
||||
}
|
||||
|
||||
static inline size_t mp_asm_base_get_code_size(mp_asm_base_t *as) {
|
||||
return as->code_size;
|
||||
}
|
||||
|
||||
static inline void *mp_asm_base_get_code(mp_asm_base_t *as) {
|
||||
#if defined(MP_PLAT_COMMIT_EXEC)
|
||||
return MP_PLAT_COMMIT_EXEC(as->code_base, as->code_size, NULL);
|
||||
#else
|
||||
return as->code_base;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // MICROPY_INCLUDED_PY_ASMBASE_H
|
||||
@@ -0,0 +1,413 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013, 2014 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "py/mpconfig.h"
|
||||
|
||||
// wrapper around everything in this file
|
||||
#if MICROPY_EMIT_THUMB || MICROPY_EMIT_INLINE_THUMB
|
||||
|
||||
#include "py/mpstate.h"
|
||||
#include "py/persistentcode.h"
|
||||
#include "py/mphal.h"
|
||||
#include "py/asmthumb.h"
|
||||
|
||||
#define UNSIGNED_FIT5(x) ((uint32_t)(x) < 32)
|
||||
#define UNSIGNED_FIT7(x) ((uint32_t)(x) < 128)
|
||||
#define UNSIGNED_FIT8(x) (((x) & 0xffffff00) == 0)
|
||||
#define UNSIGNED_FIT16(x) (((x) & 0xffff0000) == 0)
|
||||
#define SIGNED_FIT8(x) (((x) & 0xffffff80) == 0) || (((x) & 0xffffff80) == 0xffffff80)
|
||||
#define SIGNED_FIT9(x) (((x) & 0xffffff00) == 0) || (((x) & 0xffffff00) == 0xffffff00)
|
||||
#define SIGNED_FIT12(x) (((x) & 0xfffff800) == 0) || (((x) & 0xfffff800) == 0xfffff800)
|
||||
#define SIGNED_FIT23(x) (((x) & 0xffc00000) == 0) || (((x) & 0xffc00000) == 0xffc00000)
|
||||
|
||||
// Note: these actually take an imm12 but the high-bit is not encoded here
|
||||
#define OP_ADD_W_RRI_HI(reg_src) (0xf200 | (reg_src))
|
||||
#define OP_ADD_W_RRI_LO(reg_dest, imm11) ((imm11 << 4 & 0x7000) | reg_dest << 8 | (imm11 & 0xff))
|
||||
#define OP_SUB_W_RRI_HI(reg_src) (0xf2a0 | (reg_src))
|
||||
#define OP_SUB_W_RRI_LO(reg_dest, imm11) ((imm11 << 4 & 0x7000) | reg_dest << 8 | (imm11 & 0xff))
|
||||
|
||||
#define OP_LDR_W_HI(reg_base) (0xf8d0 | (reg_base))
|
||||
#define OP_LDR_W_LO(reg_dest, imm12) ((reg_dest) << 12 | (imm12))
|
||||
|
||||
static inline byte *asm_thumb_get_cur_to_write_bytes(asm_thumb_t *as, int n) {
|
||||
return mp_asm_base_get_cur_to_write_bytes(&as->base, n);
|
||||
}
|
||||
|
||||
void asm_thumb_end_pass(asm_thumb_t *as) {
|
||||
(void)as;
|
||||
// could check labels are resolved...
|
||||
|
||||
#if __ICACHE_PRESENT == 1
|
||||
if (as->base.pass == MP_ASM_PASS_EMIT) {
|
||||
// flush D-cache, so the code emitted is stored in memory
|
||||
MP_HAL_CLEAN_DCACHE(as->base.code_base, as->base.code_size);
|
||||
// invalidate I-cache
|
||||
SCB_InvalidateICache();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
STATIC void asm_thumb_write_byte_1(asm_thumb_t *as, byte b1) {
|
||||
byte *c = asm_thumb_get_cur_to_write_bytes(as, 1);
|
||||
c[0] = b1;
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
#define IMM32_L0(x) ((x) & 0xff)
|
||||
#define IMM32_L1(x) (((x) >> 8) & 0xff)
|
||||
#define IMM32_L2(x) (((x) >> 16) & 0xff)
|
||||
#define IMM32_L3(x) (((x) >> 24) & 0xff)
|
||||
|
||||
STATIC void asm_thumb_write_word32(asm_thumb_t *as, int w32) {
|
||||
byte *c = asm_thumb_get_cur_to_write_bytes(as, 4);
|
||||
c[0] = IMM32_L0(w32);
|
||||
c[1] = IMM32_L1(w32);
|
||||
c[2] = IMM32_L2(w32);
|
||||
c[3] = IMM32_L3(w32);
|
||||
}
|
||||
*/
|
||||
|
||||
// rlolist is a bit map indicating desired lo-registers
|
||||
#define OP_PUSH_RLIST(rlolist) (0xb400 | (rlolist))
|
||||
#define OP_PUSH_RLIST_LR(rlolist) (0xb400 | 0x0100 | (rlolist))
|
||||
#define OP_POP_RLIST(rlolist) (0xbc00 | (rlolist))
|
||||
#define OP_POP_RLIST_PC(rlolist) (0xbc00 | 0x0100 | (rlolist))
|
||||
|
||||
// The number of words must fit in 7 unsigned bits
|
||||
#define OP_ADD_SP(num_words) (0xb000 | (num_words))
|
||||
#define OP_SUB_SP(num_words) (0xb080 | (num_words))
|
||||
|
||||
// locals:
|
||||
// - stored on the stack in ascending order
|
||||
// - numbered 0 through num_locals-1
|
||||
// - SP points to first local
|
||||
//
|
||||
// | SP
|
||||
// v
|
||||
// l0 l1 l2 ... l(n-1)
|
||||
// ^ ^
|
||||
// | low address | high address in RAM
|
||||
|
||||
void asm_thumb_entry(asm_thumb_t *as, int num_locals) {
|
||||
assert(num_locals >= 0);
|
||||
|
||||
// If this Thumb machine code is run from ARM state then add a prelude
|
||||
// to switch to Thumb state for the duration of the function.
|
||||
#if MICROPY_DYNAMIC_COMPILER || MICROPY_EMIT_ARM || (defined(__arm__) && !defined(__thumb2__))
|
||||
#if MICROPY_DYNAMIC_COMPILER
|
||||
if (mp_dynamic_compiler.native_arch == MP_NATIVE_ARCH_ARMV6)
|
||||
#endif
|
||||
{
|
||||
asm_thumb_op32(as, 0x4010, 0xe92d); // push {r4, lr}
|
||||
asm_thumb_op32(as, 0xe009, 0xe28f); // add lr, pc, 8 + 1
|
||||
asm_thumb_op32(as, 0xff3e, 0xe12f); // blx lr
|
||||
asm_thumb_op32(as, 0x4010, 0xe8bd); // pop {r4, lr}
|
||||
asm_thumb_op32(as, 0xff1e, 0xe12f); // bx lr
|
||||
}
|
||||
#endif
|
||||
|
||||
// work out what to push and how many extra spaces to reserve on stack
|
||||
// so that we have enough for all locals and it's aligned an 8-byte boundary
|
||||
// we push extra regs (r1, r2, r3) to help do the stack adjustment
|
||||
// we probably should just always subtract from sp, since this would be more efficient
|
||||
// for push rlist, lowest numbered register at the lowest address
|
||||
uint reglist;
|
||||
uint stack_adjust;
|
||||
// don't pop r0 because it's used for return value
|
||||
switch (num_locals) {
|
||||
case 0:
|
||||
reglist = 0xf2;
|
||||
stack_adjust = 0;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
reglist = 0xf2;
|
||||
stack_adjust = 0;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
reglist = 0xfe;
|
||||
stack_adjust = 0;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
reglist = 0xfe;
|
||||
stack_adjust = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
reglist = 0xfe;
|
||||
stack_adjust = ((num_locals - 3) + 1) & (~1);
|
||||
break;
|
||||
}
|
||||
asm_thumb_op16(as, OP_PUSH_RLIST_LR(reglist));
|
||||
if (stack_adjust > 0) {
|
||||
if (UNSIGNED_FIT7(stack_adjust)) {
|
||||
asm_thumb_op16(as, OP_SUB_SP(stack_adjust));
|
||||
} else {
|
||||
asm_thumb_op32(as, OP_SUB_W_RRI_HI(ASM_THUMB_REG_SP), OP_SUB_W_RRI_LO(ASM_THUMB_REG_SP, stack_adjust * 4));
|
||||
}
|
||||
}
|
||||
as->push_reglist = reglist;
|
||||
as->stack_adjust = stack_adjust;
|
||||
}
|
||||
|
||||
void asm_thumb_exit(asm_thumb_t *as) {
|
||||
if (as->stack_adjust > 0) {
|
||||
if (UNSIGNED_FIT7(as->stack_adjust)) {
|
||||
asm_thumb_op16(as, OP_ADD_SP(as->stack_adjust));
|
||||
} else {
|
||||
asm_thumb_op32(as, OP_ADD_W_RRI_HI(ASM_THUMB_REG_SP), OP_ADD_W_RRI_LO(ASM_THUMB_REG_SP, as->stack_adjust * 4));
|
||||
}
|
||||
}
|
||||
asm_thumb_op16(as, OP_POP_RLIST_PC(as->push_reglist));
|
||||
}
|
||||
|
||||
STATIC mp_uint_t get_label_dest(asm_thumb_t *as, uint label) {
|
||||
assert(label < as->base.max_num_labels);
|
||||
return as->base.label_offsets[label];
|
||||
}
|
||||
|
||||
void asm_thumb_op16(asm_thumb_t *as, uint op) {
|
||||
byte *c = asm_thumb_get_cur_to_write_bytes(as, 2);
|
||||
if (c != NULL) {
|
||||
// little endian
|
||||
c[0] = op;
|
||||
c[1] = op >> 8;
|
||||
}
|
||||
}
|
||||
|
||||
void asm_thumb_op32(asm_thumb_t *as, uint op1, uint op2) {
|
||||
byte *c = asm_thumb_get_cur_to_write_bytes(as, 4);
|
||||
if (c != NULL) {
|
||||
// little endian, op1 then op2
|
||||
c[0] = op1;
|
||||
c[1] = op1 >> 8;
|
||||
c[2] = op2;
|
||||
c[3] = op2 >> 8;
|
||||
}
|
||||
}
|
||||
|
||||
#define OP_FORMAT_4(op, rlo_dest, rlo_src) ((op) | ((rlo_src) << 3) | (rlo_dest))
|
||||
|
||||
void asm_thumb_format_4(asm_thumb_t *as, uint op, uint rlo_dest, uint rlo_src) {
|
||||
assert(rlo_dest < ASM_THUMB_REG_R8);
|
||||
assert(rlo_src < ASM_THUMB_REG_R8);
|
||||
asm_thumb_op16(as, OP_FORMAT_4(op, rlo_dest, rlo_src));
|
||||
}
|
||||
|
||||
void asm_thumb_mov_reg_reg(asm_thumb_t *as, uint reg_dest, uint reg_src) {
|
||||
uint op_lo;
|
||||
if (reg_src < 8) {
|
||||
op_lo = reg_src << 3;
|
||||
} else {
|
||||
op_lo = 0x40 | ((reg_src - 8) << 3);
|
||||
}
|
||||
if (reg_dest < 8) {
|
||||
op_lo |= reg_dest;
|
||||
} else {
|
||||
op_lo |= 0x80 | (reg_dest - 8);
|
||||
}
|
||||
// mov reg_dest, reg_src
|
||||
asm_thumb_op16(as, 0x4600 | op_lo);
|
||||
}
|
||||
|
||||
// if loading lo half with movw, the i16 value will be zero extended into the r32 register!
|
||||
size_t asm_thumb_mov_reg_i16(asm_thumb_t *as, uint mov_op, uint reg_dest, int i16_src) {
|
||||
assert(reg_dest < ASM_THUMB_REG_R15);
|
||||
size_t loc = mp_asm_base_get_code_pos(&as->base);
|
||||
// mov[wt] reg_dest, #i16_src
|
||||
asm_thumb_op32(as, mov_op | ((i16_src >> 1) & 0x0400) | ((i16_src >> 12) & 0xf), ((i16_src << 4) & 0x7000) | (reg_dest << 8) | (i16_src & 0xff));
|
||||
return loc;
|
||||
}
|
||||
|
||||
#define OP_B_N(byte_offset) (0xe000 | (((byte_offset) >> 1) & 0x07ff))
|
||||
|
||||
bool asm_thumb_b_n_label(asm_thumb_t *as, uint label) {
|
||||
mp_uint_t dest = get_label_dest(as, label);
|
||||
mp_int_t rel = dest - as->base.code_offset;
|
||||
rel -= 4; // account for instruction prefetch, PC is 4 bytes ahead of this instruction
|
||||
asm_thumb_op16(as, OP_B_N(rel));
|
||||
return as->base.pass != MP_ASM_PASS_EMIT || SIGNED_FIT12(rel);
|
||||
}
|
||||
|
||||
#define OP_BCC_N(cond, byte_offset) (0xd000 | ((cond) << 8) | (((byte_offset) >> 1) & 0x00ff))
|
||||
|
||||
// all these bit arithmetics need coverage testing!
|
||||
#define OP_BCC_W_HI(cond, byte_offset) (0xf000 | ((cond) << 6) | (((byte_offset) >> 10) & 0x0400) | (((byte_offset) >> 14) & 0x003f))
|
||||
#define OP_BCC_W_LO(byte_offset) (0x8000 | ((byte_offset) & 0x2000) | (((byte_offset) >> 1) & 0x0fff))
|
||||
|
||||
bool asm_thumb_bcc_nw_label(asm_thumb_t *as, int cond, uint label, bool wide) {
|
||||
mp_uint_t dest = get_label_dest(as, label);
|
||||
mp_int_t rel = dest - as->base.code_offset;
|
||||
rel -= 4; // account for instruction prefetch, PC is 4 bytes ahead of this instruction
|
||||
if (!wide) {
|
||||
asm_thumb_op16(as, OP_BCC_N(cond, rel));
|
||||
return as->base.pass != MP_ASM_PASS_EMIT || SIGNED_FIT9(rel);
|
||||
} else {
|
||||
asm_thumb_op32(as, OP_BCC_W_HI(cond, rel), OP_BCC_W_LO(rel));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
#define OP_BL_HI(byte_offset) (0xf000 | (((byte_offset) >> 12) & 0x07ff))
|
||||
#define OP_BL_LO(byte_offset) (0xf800 | (((byte_offset) >> 1) & 0x07ff))
|
||||
|
||||
bool asm_thumb_bl_label(asm_thumb_t *as, uint label) {
|
||||
mp_uint_t dest = get_label_dest(as, label);
|
||||
mp_int_t rel = dest - as->base.code_offset;
|
||||
rel -= 4; // account for instruction prefetch, PC is 4 bytes ahead of this instruction
|
||||
asm_thumb_op32(as, OP_BL_HI(rel), OP_BL_LO(rel));
|
||||
return as->base.pass != MP_ASM_PASS_EMIT || SIGNED_FIT23(rel);
|
||||
}
|
||||
|
||||
size_t asm_thumb_mov_reg_i32(asm_thumb_t *as, uint reg_dest, mp_uint_t i32) {
|
||||
// movw, movt does it in 8 bytes
|
||||
// ldr [pc, #], dw does it in 6 bytes, but we might not reach to end of code for dw
|
||||
|
||||
size_t loc = mp_asm_base_get_code_pos(&as->base);
|
||||
|
||||
asm_thumb_mov_reg_i16(as, ASM_THUMB_OP_MOVW, reg_dest, i32);
|
||||
asm_thumb_mov_reg_i16(as, ASM_THUMB_OP_MOVT, reg_dest, i32 >> 16);
|
||||
|
||||
return loc;
|
||||
}
|
||||
|
||||
void asm_thumb_mov_reg_i32_optimised(asm_thumb_t *as, uint reg_dest, int i32) {
|
||||
if (reg_dest < 8 && UNSIGNED_FIT8(i32)) {
|
||||
asm_thumb_mov_rlo_i8(as, reg_dest, i32);
|
||||
} else if (UNSIGNED_FIT16(i32)) {
|
||||
asm_thumb_mov_reg_i16(as, ASM_THUMB_OP_MOVW, reg_dest, i32);
|
||||
} else {
|
||||
asm_thumb_mov_reg_i32(as, reg_dest, i32);
|
||||
}
|
||||
}
|
||||
|
||||
#define OP_STR_TO_SP_OFFSET(rlo_dest, word_offset) (0x9000 | ((rlo_dest) << 8) | ((word_offset) & 0x00ff))
|
||||
#define OP_LDR_FROM_SP_OFFSET(rlo_dest, word_offset) (0x9800 | ((rlo_dest) << 8) | ((word_offset) & 0x00ff))
|
||||
|
||||
void asm_thumb_mov_local_reg(asm_thumb_t *as, int local_num, uint rlo_src) {
|
||||
assert(rlo_src < ASM_THUMB_REG_R8);
|
||||
int word_offset = local_num;
|
||||
assert(as->base.pass < MP_ASM_PASS_EMIT || word_offset >= 0);
|
||||
asm_thumb_op16(as, OP_STR_TO_SP_OFFSET(rlo_src, word_offset));
|
||||
}
|
||||
|
||||
void asm_thumb_mov_reg_local(asm_thumb_t *as, uint rlo_dest, int local_num) {
|
||||
assert(rlo_dest < ASM_THUMB_REG_R8);
|
||||
int word_offset = local_num;
|
||||
assert(as->base.pass < MP_ASM_PASS_EMIT || word_offset >= 0);
|
||||
asm_thumb_op16(as, OP_LDR_FROM_SP_OFFSET(rlo_dest, word_offset));
|
||||
}
|
||||
|
||||
#define OP_ADD_REG_SP_OFFSET(rlo_dest, word_offset) (0xa800 | ((rlo_dest) << 8) | ((word_offset) & 0x00ff))
|
||||
|
||||
void asm_thumb_mov_reg_local_addr(asm_thumb_t *as, uint rlo_dest, int local_num) {
|
||||
assert(rlo_dest < ASM_THUMB_REG_R8);
|
||||
int word_offset = local_num;
|
||||
assert(as->base.pass < MP_ASM_PASS_EMIT || word_offset >= 0);
|
||||
asm_thumb_op16(as, OP_ADD_REG_SP_OFFSET(rlo_dest, word_offset));
|
||||
}
|
||||
|
||||
void asm_thumb_mov_reg_pcrel(asm_thumb_t *as, uint rlo_dest, uint label) {
|
||||
mp_uint_t dest = get_label_dest(as, label);
|
||||
mp_int_t rel = dest - as->base.code_offset;
|
||||
rel -= 4 + 4; // adjust for mov_reg_i16 and then PC+4 prefetch of add_reg_reg
|
||||
rel |= 1; // to stay in Thumb state when jumping to this address
|
||||
asm_thumb_mov_reg_i16(as, ASM_THUMB_OP_MOVW, rlo_dest, rel); // 4 bytes
|
||||
asm_thumb_add_reg_reg(as, rlo_dest, ASM_THUMB_REG_R15); // 2 bytes
|
||||
}
|
||||
|
||||
static inline void asm_thumb_ldr_reg_reg_i12(asm_thumb_t *as, uint reg_dest, uint reg_base, uint word_offset) {
|
||||
asm_thumb_op32(as, OP_LDR_W_HI(reg_base), OP_LDR_W_LO(reg_dest, word_offset * 4));
|
||||
}
|
||||
|
||||
void asm_thumb_ldr_reg_reg_i12_optimised(asm_thumb_t *as, uint reg_dest, uint reg_base, uint word_offset) {
|
||||
if (reg_dest < ASM_THUMB_REG_R8 && reg_base < ASM_THUMB_REG_R8 && UNSIGNED_FIT5(word_offset)) {
|
||||
asm_thumb_ldr_rlo_rlo_i5(as, reg_dest, reg_base, word_offset);
|
||||
} else {
|
||||
asm_thumb_ldr_reg_reg_i12(as, reg_dest, reg_base, word_offset);
|
||||
}
|
||||
}
|
||||
|
||||
// this could be wrong, because it should have a range of +/- 16MiB...
|
||||
#define OP_BW_HI(byte_offset) (0xf000 | (((byte_offset) >> 12) & 0x07ff))
|
||||
#define OP_BW_LO(byte_offset) (0xb800 | (((byte_offset) >> 1) & 0x07ff))
|
||||
|
||||
void asm_thumb_b_label(asm_thumb_t *as, uint label) {
|
||||
mp_uint_t dest = get_label_dest(as, label);
|
||||
mp_int_t rel = dest - as->base.code_offset;
|
||||
rel -= 4; // account for instruction prefetch, PC is 4 bytes ahead of this instruction
|
||||
if (dest != (mp_uint_t)-1 && rel <= -4) {
|
||||
// is a backwards jump, so we know the size of the jump on the first pass
|
||||
// calculate rel assuming 12 bit relative jump
|
||||
if (SIGNED_FIT12(rel)) {
|
||||
asm_thumb_op16(as, OP_B_N(rel));
|
||||
} else {
|
||||
goto large_jump;
|
||||
}
|
||||
} else {
|
||||
// is a forwards jump, so need to assume it's large
|
||||
large_jump:
|
||||
asm_thumb_op32(as, OP_BW_HI(rel), OP_BW_LO(rel));
|
||||
}
|
||||
}
|
||||
|
||||
void asm_thumb_bcc_label(asm_thumb_t *as, int cond, uint label) {
|
||||
mp_uint_t dest = get_label_dest(as, label);
|
||||
mp_int_t rel = dest - as->base.code_offset;
|
||||
rel -= 4; // account for instruction prefetch, PC is 4 bytes ahead of this instruction
|
||||
if (dest != (mp_uint_t)-1 && rel <= -4) {
|
||||
// is a backwards jump, so we know the size of the jump on the first pass
|
||||
// calculate rel assuming 9 bit relative jump
|
||||
if (SIGNED_FIT9(rel)) {
|
||||
asm_thumb_op16(as, OP_BCC_N(cond, rel));
|
||||
} else {
|
||||
goto large_jump;
|
||||
}
|
||||
} else {
|
||||
// is a forwards jump, so need to assume it's large
|
||||
large_jump:
|
||||
asm_thumb_op32(as, OP_BCC_W_HI(cond, rel), OP_BCC_W_LO(rel));
|
||||
}
|
||||
}
|
||||
|
||||
#define OP_BLX(reg) (0x4780 | ((reg) << 3))
|
||||
#define OP_SVC(arg) (0xdf00 | (arg))
|
||||
|
||||
void asm_thumb_bl_ind(asm_thumb_t *as, uint fun_id, uint reg_temp) {
|
||||
// Load ptr to function from table, indexed by fun_id, then call it
|
||||
asm_thumb_ldr_reg_reg_i12_optimised(as, reg_temp, ASM_THUMB_REG_FUN_TABLE, fun_id);
|
||||
asm_thumb_op16(as, OP_BLX(reg_temp));
|
||||
}
|
||||
|
||||
#endif // MICROPY_EMIT_THUMB || MICROPY_EMIT_INLINE_THUMB
|
||||
@@ -0,0 +1,378 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013, 2014 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef MICROPY_INCLUDED_PY_ASMTHUMB_H
|
||||
#define MICROPY_INCLUDED_PY_ASMTHUMB_H
|
||||
|
||||
#include <assert.h>
|
||||
#include "py/misc.h"
|
||||
#include "py/asmbase.h"
|
||||
|
||||
#define ASM_THUMB_REG_R0 (0)
|
||||
#define ASM_THUMB_REG_R1 (1)
|
||||
#define ASM_THUMB_REG_R2 (2)
|
||||
#define ASM_THUMB_REG_R3 (3)
|
||||
#define ASM_THUMB_REG_R4 (4)
|
||||
#define ASM_THUMB_REG_R5 (5)
|
||||
#define ASM_THUMB_REG_R6 (6)
|
||||
#define ASM_THUMB_REG_R7 (7)
|
||||
#define ASM_THUMB_REG_R8 (8)
|
||||
#define ASM_THUMB_REG_R9 (9)
|
||||
#define ASM_THUMB_REG_R10 (10)
|
||||
#define ASM_THUMB_REG_R11 (11)
|
||||
#define ASM_THUMB_REG_R12 (12)
|
||||
#define ASM_THUMB_REG_R13 (13)
|
||||
#define ASM_THUMB_REG_R14 (14)
|
||||
#define ASM_THUMB_REG_R15 (15)
|
||||
#define ASM_THUMB_REG_SP (ASM_THUMB_REG_R13)
|
||||
#define ASM_THUMB_REG_LR (REG_R14)
|
||||
|
||||
#define ASM_THUMB_CC_EQ (0x0)
|
||||
#define ASM_THUMB_CC_NE (0x1)
|
||||
#define ASM_THUMB_CC_CS (0x2)
|
||||
#define ASM_THUMB_CC_CC (0x3)
|
||||
#define ASM_THUMB_CC_MI (0x4)
|
||||
#define ASM_THUMB_CC_PL (0x5)
|
||||
#define ASM_THUMB_CC_VS (0x6)
|
||||
#define ASM_THUMB_CC_VC (0x7)
|
||||
#define ASM_THUMB_CC_HI (0x8)
|
||||
#define ASM_THUMB_CC_LS (0x9)
|
||||
#define ASM_THUMB_CC_GE (0xa)
|
||||
#define ASM_THUMB_CC_LT (0xb)
|
||||
#define ASM_THUMB_CC_GT (0xc)
|
||||
#define ASM_THUMB_CC_LE (0xd)
|
||||
|
||||
typedef struct _asm_thumb_t {
|
||||
mp_asm_base_t base;
|
||||
uint32_t push_reglist;
|
||||
uint32_t stack_adjust;
|
||||
} asm_thumb_t;
|
||||
|
||||
void asm_thumb_end_pass(asm_thumb_t *as);
|
||||
|
||||
void asm_thumb_entry(asm_thumb_t *as, int num_locals);
|
||||
void asm_thumb_exit(asm_thumb_t *as);
|
||||
|
||||
// argument order follows ARM, in general dest is first
|
||||
// note there is a difference between movw and mov.w, and many others!
|
||||
|
||||
#define ASM_THUMB_OP_IT (0xbf00)
|
||||
#define ASM_THUMB_OP_ITE_EQ (0xbf0c)
|
||||
#define ASM_THUMB_OP_ITE_NE (0xbf14)
|
||||
#define ASM_THUMB_OP_ITE_CS (0xbf2c)
|
||||
#define ASM_THUMB_OP_ITE_CC (0xbf34)
|
||||
#define ASM_THUMB_OP_ITE_MI (0xbf4c)
|
||||
#define ASM_THUMB_OP_ITE_PL (0xbf54)
|
||||
#define ASM_THUMB_OP_ITE_VS (0xbf6c)
|
||||
#define ASM_THUMB_OP_ITE_VC (0xbf74)
|
||||
#define ASM_THUMB_OP_ITE_HI (0xbf8c)
|
||||
#define ASM_THUMB_OP_ITE_LS (0xbf94)
|
||||
#define ASM_THUMB_OP_ITE_GE (0xbfac)
|
||||
#define ASM_THUMB_OP_ITE_LT (0xbfb4)
|
||||
#define ASM_THUMB_OP_ITE_GT (0xbfcc)
|
||||
#define ASM_THUMB_OP_ITE_LE (0xbfd4)
|
||||
|
||||
#define ASM_THUMB_OP_NOP (0xbf00)
|
||||
#define ASM_THUMB_OP_WFI (0xbf30)
|
||||
#define ASM_THUMB_OP_CPSID_I (0xb672) // cpsid i, disable irq
|
||||
#define ASM_THUMB_OP_CPSIE_I (0xb662) // cpsie i, enable irq
|
||||
|
||||
void asm_thumb_op16(asm_thumb_t *as, uint op);
|
||||
void asm_thumb_op32(asm_thumb_t *as, uint op1, uint op2);
|
||||
|
||||
static inline void asm_thumb_it_cc(asm_thumb_t *as, uint cc, uint mask) {
|
||||
asm_thumb_op16(as, ASM_THUMB_OP_IT | (cc << 4) | mask);
|
||||
}
|
||||
|
||||
// FORMAT 1: move shifted register
|
||||
|
||||
#define ASM_THUMB_FORMAT_1_LSL (0x0000)
|
||||
#define ASM_THUMB_FORMAT_1_LSR (0x0800)
|
||||
#define ASM_THUMB_FORMAT_1_ASR (0x1000)
|
||||
|
||||
#define ASM_THUMB_FORMAT_1_ENCODE(op, rlo_dest, rlo_src, offset) \
|
||||
((op) | ((offset) << 6) | ((rlo_src) << 3) | (rlo_dest))
|
||||
|
||||
static inline void asm_thumb_format_1(asm_thumb_t *as, uint op, uint rlo_dest, uint rlo_src, uint offset) {
|
||||
assert(rlo_dest < ASM_THUMB_REG_R8);
|
||||
assert(rlo_src < ASM_THUMB_REG_R8);
|
||||
asm_thumb_op16(as, ASM_THUMB_FORMAT_1_ENCODE(op, rlo_dest, rlo_src, offset));
|
||||
}
|
||||
|
||||
// FORMAT 2: add/subtract
|
||||
|
||||
#define ASM_THUMB_FORMAT_2_ADD (0x1800)
|
||||
#define ASM_THUMB_FORMAT_2_SUB (0x1a00)
|
||||
#define ASM_THUMB_FORMAT_2_REG_OPERAND (0x0000)
|
||||
#define ASM_THUMB_FORMAT_2_IMM_OPERAND (0x0400)
|
||||
|
||||
#define ASM_THUMB_FORMAT_2_ENCODE(op, rlo_dest, rlo_src, src_b) \
|
||||
((op) | ((src_b) << 6) | ((rlo_src) << 3) | (rlo_dest))
|
||||
|
||||
static inline void asm_thumb_format_2(asm_thumb_t *as, uint op, uint rlo_dest, uint rlo_src, int src_b) {
|
||||
assert(rlo_dest < ASM_THUMB_REG_R8);
|
||||
assert(rlo_src < ASM_THUMB_REG_R8);
|
||||
asm_thumb_op16(as, ASM_THUMB_FORMAT_2_ENCODE(op, rlo_dest, rlo_src, src_b));
|
||||
}
|
||||
|
||||
static inline void asm_thumb_add_rlo_rlo_rlo(asm_thumb_t *as, uint rlo_dest, uint rlo_src_a, uint rlo_src_b) {
|
||||
asm_thumb_format_2(as, ASM_THUMB_FORMAT_2_ADD | ASM_THUMB_FORMAT_2_REG_OPERAND, rlo_dest, rlo_src_a, rlo_src_b);
|
||||
}
|
||||
static inline void asm_thumb_add_rlo_rlo_i3(asm_thumb_t *as, uint rlo_dest, uint rlo_src_a, int i3_src) {
|
||||
asm_thumb_format_2(as, ASM_THUMB_FORMAT_2_ADD | ASM_THUMB_FORMAT_2_IMM_OPERAND, rlo_dest, rlo_src_a, i3_src);
|
||||
}
|
||||
static inline void asm_thumb_sub_rlo_rlo_rlo(asm_thumb_t *as, uint rlo_dest, uint rlo_src_a, uint rlo_src_b) {
|
||||
asm_thumb_format_2(as, ASM_THUMB_FORMAT_2_SUB | ASM_THUMB_FORMAT_2_REG_OPERAND, rlo_dest, rlo_src_a, rlo_src_b);
|
||||
}
|
||||
static inline void asm_thumb_sub_rlo_rlo_i3(asm_thumb_t *as, uint rlo_dest, uint rlo_src_a, int i3_src) {
|
||||
asm_thumb_format_2(as, ASM_THUMB_FORMAT_2_SUB | ASM_THUMB_FORMAT_2_IMM_OPERAND, rlo_dest, rlo_src_a, i3_src);
|
||||
}
|
||||
|
||||
// FORMAT 3: move/compare/add/subtract immediate
|
||||
// These instructions all do zero extension of the i8 value
|
||||
|
||||
#define ASM_THUMB_FORMAT_3_MOV (0x2000)
|
||||
#define ASM_THUMB_FORMAT_3_CMP (0x2800)
|
||||
#define ASM_THUMB_FORMAT_3_ADD (0x3000)
|
||||
#define ASM_THUMB_FORMAT_3_SUB (0x3800)
|
||||
|
||||
#define ASM_THUMB_FORMAT_3_ENCODE(op, rlo, i8) ((op) | ((rlo) << 8) | (i8))
|
||||
|
||||
static inline void asm_thumb_format_3(asm_thumb_t *as, uint op, uint rlo, int i8) {
|
||||
assert(rlo < ASM_THUMB_REG_R8);
|
||||
asm_thumb_op16(as, ASM_THUMB_FORMAT_3_ENCODE(op, rlo, i8));
|
||||
}
|
||||
|
||||
static inline void asm_thumb_mov_rlo_i8(asm_thumb_t *as, uint rlo, int i8) {
|
||||
asm_thumb_format_3(as, ASM_THUMB_FORMAT_3_MOV, rlo, i8);
|
||||
}
|
||||
static inline void asm_thumb_cmp_rlo_i8(asm_thumb_t *as, uint rlo, int i8) {
|
||||
asm_thumb_format_3(as, ASM_THUMB_FORMAT_3_CMP, rlo, i8);
|
||||
}
|
||||
static inline void asm_thumb_add_rlo_i8(asm_thumb_t *as, uint rlo, int i8) {
|
||||
asm_thumb_format_3(as, ASM_THUMB_FORMAT_3_ADD, rlo, i8);
|
||||
}
|
||||
static inline void asm_thumb_sub_rlo_i8(asm_thumb_t *as, uint rlo, int i8) {
|
||||
asm_thumb_format_3(as, ASM_THUMB_FORMAT_3_SUB, rlo, i8);
|
||||
}
|
||||
|
||||
// FORMAT 4: ALU operations
|
||||
|
||||
#define ASM_THUMB_FORMAT_4_AND (0x4000)
|
||||
#define ASM_THUMB_FORMAT_4_EOR (0x4040)
|
||||
#define ASM_THUMB_FORMAT_4_LSL (0x4080)
|
||||
#define ASM_THUMB_FORMAT_4_LSR (0x40c0)
|
||||
#define ASM_THUMB_FORMAT_4_ASR (0x4100)
|
||||
#define ASM_THUMB_FORMAT_4_ADC (0x4140)
|
||||
#define ASM_THUMB_FORMAT_4_SBC (0x4180)
|
||||
#define ASM_THUMB_FORMAT_4_ROR (0x41c0)
|
||||
#define ASM_THUMB_FORMAT_4_TST (0x4200)
|
||||
#define ASM_THUMB_FORMAT_4_NEG (0x4240)
|
||||
#define ASM_THUMB_FORMAT_4_CMP (0x4280)
|
||||
#define ASM_THUMB_FORMAT_4_CMN (0x42c0)
|
||||
#define ASM_THUMB_FORMAT_4_ORR (0x4300)
|
||||
#define ASM_THUMB_FORMAT_4_MUL (0x4340)
|
||||
#define ASM_THUMB_FORMAT_4_BIC (0x4380)
|
||||
#define ASM_THUMB_FORMAT_4_MVN (0x43c0)
|
||||
|
||||
void asm_thumb_format_4(asm_thumb_t *as, uint op, uint rlo_dest, uint rlo_src);
|
||||
|
||||
static inline void asm_thumb_cmp_rlo_rlo(asm_thumb_t *as, uint rlo_dest, uint rlo_src) {
|
||||
asm_thumb_format_4(as, ASM_THUMB_FORMAT_4_CMP, rlo_dest, rlo_src);
|
||||
}
|
||||
|
||||
// FORMAT 5: hi register operations (add, cmp, mov, bx)
|
||||
// For add/cmp/mov, at least one of the args must be a high register
|
||||
|
||||
#define ASM_THUMB_FORMAT_5_ADD (0x4400)
|
||||
#define ASM_THUMB_FORMAT_5_BX (0x4700)
|
||||
|
||||
#define ASM_THUMB_FORMAT_5_ENCODE(op, r_dest, r_src) \
|
||||
((op) | ((r_dest) << 4 & 0x0080) | ((r_src) << 3) | ((r_dest) & 0x0007))
|
||||
|
||||
static inline void asm_thumb_format_5(asm_thumb_t *as, uint op, uint r_dest, uint r_src) {
|
||||
asm_thumb_op16(as, ASM_THUMB_FORMAT_5_ENCODE(op, r_dest, r_src));
|
||||
}
|
||||
|
||||
static inline void asm_thumb_add_reg_reg(asm_thumb_t *as, uint r_dest, uint r_src) {
|
||||
asm_thumb_format_5(as, ASM_THUMB_FORMAT_5_ADD, r_dest, r_src);
|
||||
}
|
||||
static inline void asm_thumb_bx_reg(asm_thumb_t *as, uint r_src) {
|
||||
asm_thumb_format_5(as, ASM_THUMB_FORMAT_5_BX, 0, r_src);
|
||||
}
|
||||
|
||||
// FORMAT 9: load/store with immediate offset
|
||||
// For word transfers the offset must be aligned, and >>2
|
||||
|
||||
// FORMAT 10: load/store halfword
|
||||
// The offset must be aligned, and >>1
|
||||
// The load is zero extended into the register
|
||||
|
||||
#define ASM_THUMB_FORMAT_9_STR (0x6000)
|
||||
#define ASM_THUMB_FORMAT_9_LDR (0x6800)
|
||||
#define ASM_THUMB_FORMAT_9_WORD_TRANSFER (0x0000)
|
||||
#define ASM_THUMB_FORMAT_9_BYTE_TRANSFER (0x1000)
|
||||
|
||||
#define ASM_THUMB_FORMAT_10_STRH (0x8000)
|
||||
#define ASM_THUMB_FORMAT_10_LDRH (0x8800)
|
||||
|
||||
#define ASM_THUMB_FORMAT_9_10_ENCODE(op, rlo_dest, rlo_base, offset) \
|
||||
((op) | (((offset) << 6) & 0x07c0) | ((rlo_base) << 3) | (rlo_dest))
|
||||
|
||||
static inline void asm_thumb_format_9_10(asm_thumb_t *as, uint op, uint rlo_dest, uint rlo_base, uint offset) {
|
||||
asm_thumb_op16(as, ASM_THUMB_FORMAT_9_10_ENCODE(op, rlo_dest, rlo_base, offset));
|
||||
}
|
||||
|
||||
static inline void asm_thumb_str_rlo_rlo_i5(asm_thumb_t *as, uint rlo_src, uint rlo_base, uint word_offset) {
|
||||
asm_thumb_format_9_10(as, ASM_THUMB_FORMAT_9_STR | ASM_THUMB_FORMAT_9_WORD_TRANSFER, rlo_src, rlo_base, word_offset);
|
||||
}
|
||||
static inline void asm_thumb_strb_rlo_rlo_i5(asm_thumb_t *as, uint rlo_src, uint rlo_base, uint byte_offset) {
|
||||
asm_thumb_format_9_10(as, ASM_THUMB_FORMAT_9_STR | ASM_THUMB_FORMAT_9_BYTE_TRANSFER, rlo_src, rlo_base, byte_offset);
|
||||
}
|
||||
static inline void asm_thumb_strh_rlo_rlo_i5(asm_thumb_t *as, uint rlo_src, uint rlo_base, uint byte_offset) {
|
||||
asm_thumb_format_9_10(as, ASM_THUMB_FORMAT_10_STRH, rlo_src, rlo_base, byte_offset);
|
||||
}
|
||||
static inline void asm_thumb_ldr_rlo_rlo_i5(asm_thumb_t *as, uint rlo_dest, uint rlo_base, uint word_offset) {
|
||||
asm_thumb_format_9_10(as, ASM_THUMB_FORMAT_9_LDR | ASM_THUMB_FORMAT_9_WORD_TRANSFER, rlo_dest, rlo_base, word_offset);
|
||||
}
|
||||
static inline void asm_thumb_ldrb_rlo_rlo_i5(asm_thumb_t *as, uint rlo_dest, uint rlo_base, uint byte_offset) {
|
||||
asm_thumb_format_9_10(as, ASM_THUMB_FORMAT_9_LDR | ASM_THUMB_FORMAT_9_BYTE_TRANSFER, rlo_dest, rlo_base, byte_offset);
|
||||
}
|
||||
static inline void asm_thumb_ldrh_rlo_rlo_i5(asm_thumb_t *as, uint rlo_dest, uint rlo_base, uint byte_offset) {
|
||||
asm_thumb_format_9_10(as, ASM_THUMB_FORMAT_10_LDRH, rlo_dest, rlo_base, byte_offset);
|
||||
}
|
||||
|
||||
// TODO convert these to above format style
|
||||
|
||||
#define ASM_THUMB_OP_MOVW (0xf240)
|
||||
#define ASM_THUMB_OP_MOVT (0xf2c0)
|
||||
|
||||
void asm_thumb_mov_reg_reg(asm_thumb_t *as, uint reg_dest, uint reg_src);
|
||||
size_t asm_thumb_mov_reg_i16(asm_thumb_t *as, uint mov_op, uint reg_dest, int i16_src);
|
||||
|
||||
// these return true if the destination is in range, false otherwise
|
||||
bool asm_thumb_b_n_label(asm_thumb_t *as, uint label);
|
||||
bool asm_thumb_bcc_nw_label(asm_thumb_t *as, int cond, uint label, bool wide);
|
||||
bool asm_thumb_bl_label(asm_thumb_t *as, uint label);
|
||||
|
||||
size_t asm_thumb_mov_reg_i32(asm_thumb_t *as, uint reg_dest, mp_uint_t i32_src); // convenience
|
||||
void asm_thumb_mov_reg_i32_optimised(asm_thumb_t *as, uint reg_dest, int i32_src); // convenience
|
||||
void asm_thumb_mov_local_reg(asm_thumb_t *as, int local_num_dest, uint rlo_src); // convenience
|
||||
void asm_thumb_mov_reg_local(asm_thumb_t *as, uint rlo_dest, int local_num); // convenience
|
||||
void asm_thumb_mov_reg_local_addr(asm_thumb_t *as, uint rlo_dest, int local_num); // convenience
|
||||
void asm_thumb_mov_reg_pcrel(asm_thumb_t *as, uint rlo_dest, uint label);
|
||||
|
||||
void asm_thumb_ldr_reg_reg_i12_optimised(asm_thumb_t *as, uint reg_dest, uint reg_base, uint byte_offset); // convenience
|
||||
|
||||
void asm_thumb_b_label(asm_thumb_t *as, uint label); // convenience: picks narrow or wide branch
|
||||
void asm_thumb_bcc_label(asm_thumb_t *as, int cc, uint label); // convenience: picks narrow or wide branch
|
||||
void asm_thumb_bl_ind(asm_thumb_t *as, uint fun_id, uint reg_temp); // convenience
|
||||
|
||||
// Holds a pointer to mp_fun_table
|
||||
#define ASM_THUMB_REG_FUN_TABLE ASM_THUMB_REG_R7
|
||||
|
||||
#if GENERIC_ASM_API
|
||||
|
||||
// The following macros provide a (mostly) arch-independent API to
|
||||
// generate native code, and are used by the native emitter.
|
||||
|
||||
#define ASM_WORD_SIZE (4)
|
||||
|
||||
#define REG_RET ASM_THUMB_REG_R0
|
||||
#define REG_ARG_1 ASM_THUMB_REG_R0
|
||||
#define REG_ARG_2 ASM_THUMB_REG_R1
|
||||
#define REG_ARG_3 ASM_THUMB_REG_R2
|
||||
#define REG_ARG_4 ASM_THUMB_REG_R3
|
||||
// rest of args go on stack
|
||||
|
||||
#define REG_TEMP0 ASM_THUMB_REG_R0
|
||||
#define REG_TEMP1 ASM_THUMB_REG_R1
|
||||
#define REG_TEMP2 ASM_THUMB_REG_R2
|
||||
|
||||
#define REG_LOCAL_1 ASM_THUMB_REG_R4
|
||||
#define REG_LOCAL_2 ASM_THUMB_REG_R5
|
||||
#define REG_LOCAL_3 ASM_THUMB_REG_R6
|
||||
#define REG_LOCAL_NUM (3)
|
||||
|
||||
#define REG_FUN_TABLE ASM_THUMB_REG_FUN_TABLE
|
||||
|
||||
#define ASM_T asm_thumb_t
|
||||
#define ASM_END_PASS asm_thumb_end_pass
|
||||
#define ASM_ENTRY asm_thumb_entry
|
||||
#define ASM_EXIT asm_thumb_exit
|
||||
|
||||
#define ASM_JUMP asm_thumb_b_label
|
||||
#define ASM_JUMP_IF_REG_ZERO(as, reg, label, bool_test) \
|
||||
do { \
|
||||
asm_thumb_cmp_rlo_i8(as, reg, 0); \
|
||||
asm_thumb_bcc_label(as, ASM_THUMB_CC_EQ, label); \
|
||||
} while (0)
|
||||
#define ASM_JUMP_IF_REG_NONZERO(as, reg, label, bool_test) \
|
||||
do { \
|
||||
asm_thumb_cmp_rlo_i8(as, reg, 0); \
|
||||
asm_thumb_bcc_label(as, ASM_THUMB_CC_NE, label); \
|
||||
} while (0)
|
||||
#define ASM_JUMP_IF_REG_EQ(as, reg1, reg2, label) \
|
||||
do { \
|
||||
asm_thumb_cmp_rlo_rlo(as, reg1, reg2); \
|
||||
asm_thumb_bcc_label(as, ASM_THUMB_CC_EQ, label); \
|
||||
} while (0)
|
||||
#define ASM_JUMP_REG(as, reg) asm_thumb_bx_reg((as), (reg))
|
||||
#define ASM_CALL_IND(as, idx) asm_thumb_bl_ind(as, idx, ASM_THUMB_REG_R3)
|
||||
|
||||
#define ASM_MOV_LOCAL_REG(as, local_num, reg) asm_thumb_mov_local_reg((as), (local_num), (reg))
|
||||
#define ASM_MOV_REG_IMM(as, reg_dest, imm) asm_thumb_mov_reg_i32_optimised((as), (reg_dest), (imm))
|
||||
#define ASM_MOV_REG_IMM_FIX_U16(as, reg_dest, imm) asm_thumb_mov_reg_i16((as), ASM_THUMB_OP_MOVW, (reg_dest), (imm))
|
||||
#define ASM_MOV_REG_IMM_FIX_WORD(as, reg_dest, imm) asm_thumb_mov_reg_i32((as), (reg_dest), (imm))
|
||||
#define ASM_MOV_REG_LOCAL(as, reg_dest, local_num) asm_thumb_mov_reg_local((as), (reg_dest), (local_num))
|
||||
#define ASM_MOV_REG_REG(as, reg_dest, reg_src) asm_thumb_mov_reg_reg((as), (reg_dest), (reg_src))
|
||||
#define ASM_MOV_REG_LOCAL_ADDR(as, reg_dest, local_num) asm_thumb_mov_reg_local_addr((as), (reg_dest), (local_num))
|
||||
#define ASM_MOV_REG_PCREL(as, rlo_dest, label) asm_thumb_mov_reg_pcrel((as), (rlo_dest), (label))
|
||||
|
||||
#define ASM_LSL_REG_REG(as, reg_dest, reg_shift) asm_thumb_format_4((as), ASM_THUMB_FORMAT_4_LSL, (reg_dest), (reg_shift))
|
||||
#define ASM_LSR_REG_REG(as, reg_dest, reg_shift) asm_thumb_format_4((as), ASM_THUMB_FORMAT_4_LSR, (reg_dest), (reg_shift))
|
||||
#define ASM_ASR_REG_REG(as, reg_dest, reg_shift) asm_thumb_format_4((as), ASM_THUMB_FORMAT_4_ASR, (reg_dest), (reg_shift))
|
||||
#define ASM_OR_REG_REG(as, reg_dest, reg_src) asm_thumb_format_4((as), ASM_THUMB_FORMAT_4_ORR, (reg_dest), (reg_src))
|
||||
#define ASM_XOR_REG_REG(as, reg_dest, reg_src) asm_thumb_format_4((as), ASM_THUMB_FORMAT_4_EOR, (reg_dest), (reg_src))
|
||||
#define ASM_AND_REG_REG(as, reg_dest, reg_src) asm_thumb_format_4((as), ASM_THUMB_FORMAT_4_AND, (reg_dest), (reg_src))
|
||||
#define ASM_ADD_REG_REG(as, reg_dest, reg_src) asm_thumb_add_rlo_rlo_rlo((as), (reg_dest), (reg_dest), (reg_src))
|
||||
#define ASM_SUB_REG_REG(as, reg_dest, reg_src) asm_thumb_sub_rlo_rlo_rlo((as), (reg_dest), (reg_dest), (reg_src))
|
||||
#define ASM_MUL_REG_REG(as, reg_dest, reg_src) asm_thumb_format_4((as), ASM_THUMB_FORMAT_4_MUL, (reg_dest), (reg_src))
|
||||
|
||||
#define ASM_LOAD_REG_REG(as, reg_dest, reg_base) asm_thumb_ldr_rlo_rlo_i5((as), (reg_dest), (reg_base), 0)
|
||||
#define ASM_LOAD_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_thumb_ldr_reg_reg_i12_optimised((as), (reg_dest), (reg_base), (word_offset))
|
||||
#define ASM_LOAD8_REG_REG(as, reg_dest, reg_base) asm_thumb_ldrb_rlo_rlo_i5((as), (reg_dest), (reg_base), 0)
|
||||
#define ASM_LOAD16_REG_REG(as, reg_dest, reg_base) asm_thumb_ldrh_rlo_rlo_i5((as), (reg_dest), (reg_base), 0)
|
||||
#define ASM_LOAD32_REG_REG(as, reg_dest, reg_base) asm_thumb_ldr_rlo_rlo_i5((as), (reg_dest), (reg_base), 0)
|
||||
|
||||
#define ASM_STORE_REG_REG(as, reg_src, reg_base) asm_thumb_str_rlo_rlo_i5((as), (reg_src), (reg_base), 0)
|
||||
#define ASM_STORE_REG_REG_OFFSET(as, reg_src, reg_base, word_offset) asm_thumb_str_rlo_rlo_i5((as), (reg_src), (reg_base), (word_offset))
|
||||
#define ASM_STORE8_REG_REG(as, reg_src, reg_base) asm_thumb_strb_rlo_rlo_i5((as), (reg_src), (reg_base), 0)
|
||||
#define ASM_STORE16_REG_REG(as, reg_src, reg_base) asm_thumb_strh_rlo_rlo_i5((as), (reg_src), (reg_base), 0)
|
||||
#define ASM_STORE32_REG_REG(as, reg_src, reg_base) asm_thumb_str_rlo_rlo_i5((as), (reg_src), (reg_base), 0)
|
||||
|
||||
#endif // GENERIC_ASM_API
|
||||
|
||||
#endif // MICROPY_INCLUDED_PY_ASMTHUMB_H
|
||||
@@ -0,0 +1,637 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013, 2014 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "py/mpconfig.h"
|
||||
|
||||
// wrapper around everything in this file
|
||||
#if MICROPY_EMIT_X64
|
||||
|
||||
#include "py/asmx64.h"
|
||||
|
||||
/* all offsets are measured in multiples of 8 bytes */
|
||||
#define WORD_SIZE (8)
|
||||
|
||||
#define OPCODE_NOP (0x90)
|
||||
#define OPCODE_PUSH_R64 (0x50) /* +rq */
|
||||
#define OPCODE_PUSH_I64 (0x68)
|
||||
#define OPCODE_PUSH_M64 (0xff) /* /6 */
|
||||
#define OPCODE_POP_R64 (0x58) /* +rq */
|
||||
#define OPCODE_RET (0xc3)
|
||||
#define OPCODE_MOV_I8_TO_R8 (0xb0) /* +rb */
|
||||
#define OPCODE_MOV_I64_TO_R64 (0xb8) /* +rq */
|
||||
#define OPCODE_MOV_I32_TO_RM32 (0xc7)
|
||||
#define OPCODE_MOV_R8_TO_RM8 (0x88) /* /r */
|
||||
#define OPCODE_MOV_R64_TO_RM64 (0x89) /* /r */
|
||||
#define OPCODE_MOV_RM64_TO_R64 (0x8b) /* /r */
|
||||
#define OPCODE_MOVZX_RM8_TO_R64 (0xb6) /* 0x0f 0xb6/r */
|
||||
#define OPCODE_MOVZX_RM16_TO_R64 (0xb7) /* 0x0f 0xb7/r */
|
||||
#define OPCODE_LEA_MEM_TO_R64 (0x8d) /* /r */
|
||||
#define OPCODE_AND_R64_TO_RM64 (0x21) /* /r */
|
||||
#define OPCODE_OR_R64_TO_RM64 (0x09) /* /r */
|
||||
#define OPCODE_XOR_R64_TO_RM64 (0x31) /* /r */
|
||||
#define OPCODE_ADD_R64_TO_RM64 (0x01) /* /r */
|
||||
#define OPCODE_ADD_I32_TO_RM32 (0x81) /* /0 */
|
||||
#define OPCODE_ADD_I8_TO_RM32 (0x83) /* /0 */
|
||||
#define OPCODE_SUB_R64_FROM_RM64 (0x29)
|
||||
#define OPCODE_SUB_I32_FROM_RM64 (0x81) /* /5 */
|
||||
#define OPCODE_SUB_I8_FROM_RM64 (0x83) /* /5 */
|
||||
// #define OPCODE_SHL_RM32_BY_I8 (0xc1) /* /4 */
|
||||
// #define OPCODE_SHR_RM32_BY_I8 (0xc1) /* /5 */
|
||||
// #define OPCODE_SAR_RM32_BY_I8 (0xc1) /* /7 */
|
||||
#define OPCODE_SHL_RM64_CL (0xd3) /* /4 */
|
||||
#define OPCODE_SHR_RM64_CL (0xd3) /* /5 */
|
||||
#define OPCODE_SAR_RM64_CL (0xd3) /* /7 */
|
||||
// #define OPCODE_CMP_I32_WITH_RM32 (0x81) /* /7 */
|
||||
// #define OPCODE_CMP_I8_WITH_RM32 (0x83) /* /7 */
|
||||
#define OPCODE_CMP_R64_WITH_RM64 (0x39) /* /r */
|
||||
// #define OPCODE_CMP_RM32_WITH_R32 (0x3b)
|
||||
#define OPCODE_TEST_R8_WITH_RM8 (0x84) /* /r */
|
||||
#define OPCODE_TEST_R64_WITH_RM64 (0x85) /* /r */
|
||||
#define OPCODE_JMP_REL8 (0xeb)
|
||||
#define OPCODE_JMP_REL32 (0xe9)
|
||||
#define OPCODE_JMP_RM64 (0xff) /* /4 */
|
||||
#define OPCODE_JCC_REL8 (0x70) /* | jcc type */
|
||||
#define OPCODE_JCC_REL32_A (0x0f)
|
||||
#define OPCODE_JCC_REL32_B (0x80) /* | jcc type */
|
||||
#define OPCODE_SETCC_RM8_A (0x0f)
|
||||
#define OPCODE_SETCC_RM8_B (0x90) /* | jcc type, /0 */
|
||||
#define OPCODE_CALL_REL32 (0xe8)
|
||||
#define OPCODE_CALL_RM32 (0xff) /* /2 */
|
||||
#define OPCODE_LEAVE (0xc9)
|
||||
|
||||
#define MODRM_R64(x) (((x) & 0x7) << 3)
|
||||
#define MODRM_RM_DISP0 (0x00)
|
||||
#define MODRM_RM_DISP8 (0x40)
|
||||
#define MODRM_RM_DISP32 (0x80)
|
||||
#define MODRM_RM_REG (0xc0)
|
||||
#define MODRM_RM_R64(x) ((x) & 0x7)
|
||||
|
||||
#define OP_SIZE_PREFIX (0x66)
|
||||
|
||||
#define REX_PREFIX (0x40)
|
||||
#define REX_W (0x08) // width
|
||||
#define REX_R (0x04) // register
|
||||
#define REX_X (0x02) // index
|
||||
#define REX_B (0x01) // base
|
||||
#define REX_W_FROM_R64(r64) ((r64) >> 0 & 0x08)
|
||||
#define REX_R_FROM_R64(r64) ((r64) >> 1 & 0x04)
|
||||
#define REX_X_FROM_R64(r64) ((r64) >> 2 & 0x02)
|
||||
#define REX_B_FROM_R64(r64) ((r64) >> 3 & 0x01)
|
||||
|
||||
#define IMM32_L0(x) ((x) & 0xff)
|
||||
#define IMM32_L1(x) (((x) >> 8) & 0xff)
|
||||
#define IMM32_L2(x) (((x) >> 16) & 0xff)
|
||||
#define IMM32_L3(x) (((x) >> 24) & 0xff)
|
||||
#define IMM64_L4(x) (((x) >> 32) & 0xff)
|
||||
#define IMM64_L5(x) (((x) >> 40) & 0xff)
|
||||
#define IMM64_L6(x) (((x) >> 48) & 0xff)
|
||||
#define IMM64_L7(x) (((x) >> 56) & 0xff)
|
||||
|
||||
#define UNSIGNED_FIT8(x) (((x) & 0xffffffffffffff00) == 0)
|
||||
#define UNSIGNED_FIT32(x) (((x) & 0xffffffff00000000) == 0)
|
||||
#define SIGNED_FIT8(x) (((x) & 0xffffff80) == 0) || (((x) & 0xffffff80) == 0xffffff80)
|
||||
|
||||
static inline byte *asm_x64_get_cur_to_write_bytes(asm_x64_t *as, int n) {
|
||||
return mp_asm_base_get_cur_to_write_bytes(&as->base, n);
|
||||
}
|
||||
|
||||
STATIC void asm_x64_write_byte_1(asm_x64_t *as, byte b1) {
|
||||
byte *c = asm_x64_get_cur_to_write_bytes(as, 1);
|
||||
if (c != NULL) {
|
||||
c[0] = b1;
|
||||
}
|
||||
}
|
||||
|
||||
STATIC void asm_x64_write_byte_2(asm_x64_t *as, byte b1, byte b2) {
|
||||
byte *c = asm_x64_get_cur_to_write_bytes(as, 2);
|
||||
if (c != NULL) {
|
||||
c[0] = b1;
|
||||
c[1] = b2;
|
||||
}
|
||||
}
|
||||
|
||||
STATIC void asm_x64_write_byte_3(asm_x64_t *as, byte b1, byte b2, byte b3) {
|
||||
byte *c = asm_x64_get_cur_to_write_bytes(as, 3);
|
||||
if (c != NULL) {
|
||||
c[0] = b1;
|
||||
c[1] = b2;
|
||||
c[2] = b3;
|
||||
}
|
||||
}
|
||||
|
||||
STATIC void asm_x64_write_word32(asm_x64_t *as, int w32) {
|
||||
byte *c = asm_x64_get_cur_to_write_bytes(as, 4);
|
||||
if (c != NULL) {
|
||||
c[0] = IMM32_L0(w32);
|
||||
c[1] = IMM32_L1(w32);
|
||||
c[2] = IMM32_L2(w32);
|
||||
c[3] = IMM32_L3(w32);
|
||||
}
|
||||
}
|
||||
|
||||
STATIC void asm_x64_write_word64(asm_x64_t *as, int64_t w64) {
|
||||
byte *c = asm_x64_get_cur_to_write_bytes(as, 8);
|
||||
if (c != NULL) {
|
||||
c[0] = IMM32_L0(w64);
|
||||
c[1] = IMM32_L1(w64);
|
||||
c[2] = IMM32_L2(w64);
|
||||
c[3] = IMM32_L3(w64);
|
||||
c[4] = IMM64_L4(w64);
|
||||
c[5] = IMM64_L5(w64);
|
||||
c[6] = IMM64_L6(w64);
|
||||
c[7] = IMM64_L7(w64);
|
||||
}
|
||||
}
|
||||
|
||||
/* unused
|
||||
STATIC void asm_x64_write_word32_to(asm_x64_t *as, int offset, int w32) {
|
||||
byte* c;
|
||||
assert(offset + 4 <= as->code_size);
|
||||
c = as->code_base + offset;
|
||||
c[0] = IMM32_L0(w32);
|
||||
c[1] = IMM32_L1(w32);
|
||||
c[2] = IMM32_L2(w32);
|
||||
c[3] = IMM32_L3(w32);
|
||||
}
|
||||
*/
|
||||
|
||||
STATIC void asm_x64_write_r64_disp(asm_x64_t *as, int r64, int disp_r64, int disp_offset) {
|
||||
uint8_t rm_disp;
|
||||
if (disp_offset == 0 && (disp_r64 & 7) != ASM_X64_REG_RBP) {
|
||||
rm_disp = MODRM_RM_DISP0;
|
||||
} else if (SIGNED_FIT8(disp_offset)) {
|
||||
rm_disp = MODRM_RM_DISP8;
|
||||
} else {
|
||||
rm_disp = MODRM_RM_DISP32;
|
||||
}
|
||||
asm_x64_write_byte_1(as, MODRM_R64(r64) | rm_disp | MODRM_RM_R64(disp_r64));
|
||||
if ((disp_r64 & 7) == ASM_X64_REG_RSP) {
|
||||
// Special case for rsp and r12, they need a SIB byte
|
||||
asm_x64_write_byte_1(as, 0x24);
|
||||
}
|
||||
if (rm_disp == MODRM_RM_DISP8) {
|
||||
asm_x64_write_byte_1(as, IMM32_L0(disp_offset));
|
||||
} else if (rm_disp == MODRM_RM_DISP32) {
|
||||
asm_x64_write_word32(as, disp_offset);
|
||||
}
|
||||
}
|
||||
|
||||
STATIC void asm_x64_generic_r64_r64(asm_x64_t *as, int dest_r64, int src_r64, int op) {
|
||||
asm_x64_write_byte_3(as, REX_PREFIX | REX_W | REX_R_FROM_R64(src_r64) | REX_B_FROM_R64(dest_r64), op, MODRM_R64(src_r64) | MODRM_RM_REG | MODRM_RM_R64(dest_r64));
|
||||
}
|
||||
|
||||
void asm_x64_nop(asm_x64_t *as) {
|
||||
asm_x64_write_byte_1(as, OPCODE_NOP);
|
||||
}
|
||||
|
||||
void asm_x64_push_r64(asm_x64_t *as, int src_r64) {
|
||||
if (src_r64 < 8) {
|
||||
asm_x64_write_byte_1(as, OPCODE_PUSH_R64 | src_r64);
|
||||
} else {
|
||||
asm_x64_write_byte_2(as, REX_PREFIX | REX_B, OPCODE_PUSH_R64 | (src_r64 & 7));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
void asm_x64_push_i32(asm_x64_t *as, int src_i32) {
|
||||
asm_x64_write_byte_1(as, OPCODE_PUSH_I64);
|
||||
asm_x64_write_word32(as, src_i32); // will be sign extended to 64 bits
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
void asm_x64_push_disp(asm_x64_t *as, int src_r64, int src_offset) {
|
||||
assert(src_r64 < 8);
|
||||
asm_x64_write_byte_1(as, OPCODE_PUSH_M64);
|
||||
asm_x64_write_r64_disp(as, 6, src_r64, src_offset);
|
||||
}
|
||||
*/
|
||||
|
||||
void asm_x64_pop_r64(asm_x64_t *as, int dest_r64) {
|
||||
if (dest_r64 < 8) {
|
||||
asm_x64_write_byte_1(as, OPCODE_POP_R64 | dest_r64);
|
||||
} else {
|
||||
asm_x64_write_byte_2(as, REX_PREFIX | REX_B, OPCODE_POP_R64 | (dest_r64 & 7));
|
||||
}
|
||||
}
|
||||
|
||||
STATIC void asm_x64_ret(asm_x64_t *as) {
|
||||
asm_x64_write_byte_1(as, OPCODE_RET);
|
||||
}
|
||||
|
||||
void asm_x64_mov_r64_r64(asm_x64_t *as, int dest_r64, int src_r64) {
|
||||
asm_x64_generic_r64_r64(as, dest_r64, src_r64, OPCODE_MOV_R64_TO_RM64);
|
||||
}
|
||||
|
||||
void asm_x64_mov_r8_to_mem8(asm_x64_t *as, int src_r64, int dest_r64, int dest_disp) {
|
||||
if (src_r64 < 8 && dest_r64 < 8) {
|
||||
asm_x64_write_byte_1(as, OPCODE_MOV_R8_TO_RM8);
|
||||
} else {
|
||||
asm_x64_write_byte_2(as, REX_PREFIX | REX_R_FROM_R64(src_r64) | REX_B_FROM_R64(dest_r64), OPCODE_MOV_R8_TO_RM8);
|
||||
}
|
||||
asm_x64_write_r64_disp(as, src_r64, dest_r64, dest_disp);
|
||||
}
|
||||
|
||||
void asm_x64_mov_r16_to_mem16(asm_x64_t *as, int src_r64, int dest_r64, int dest_disp) {
|
||||
if (src_r64 < 8 && dest_r64 < 8) {
|
||||
asm_x64_write_byte_2(as, OP_SIZE_PREFIX, OPCODE_MOV_R64_TO_RM64);
|
||||
} else {
|
||||
asm_x64_write_byte_3(as, OP_SIZE_PREFIX, REX_PREFIX | REX_R_FROM_R64(src_r64) | REX_B_FROM_R64(dest_r64), OPCODE_MOV_R64_TO_RM64);
|
||||
}
|
||||
asm_x64_write_r64_disp(as, src_r64, dest_r64, dest_disp);
|
||||
}
|
||||
|
||||
void asm_x64_mov_r32_to_mem32(asm_x64_t *as, int src_r64, int dest_r64, int dest_disp) {
|
||||
if (src_r64 < 8 && dest_r64 < 8) {
|
||||
asm_x64_write_byte_1(as, OPCODE_MOV_R64_TO_RM64);
|
||||
} else {
|
||||
asm_x64_write_byte_2(as, REX_PREFIX | REX_R_FROM_R64(src_r64) | REX_B_FROM_R64(dest_r64), OPCODE_MOV_R64_TO_RM64);
|
||||
}
|
||||
asm_x64_write_r64_disp(as, src_r64, dest_r64, dest_disp);
|
||||
}
|
||||
|
||||
void asm_x64_mov_r64_to_mem64(asm_x64_t *as, int src_r64, int dest_r64, int dest_disp) {
|
||||
// use REX prefix for 64 bit operation
|
||||
asm_x64_write_byte_2(as, REX_PREFIX | REX_W | REX_R_FROM_R64(src_r64) | REX_B_FROM_R64(dest_r64), OPCODE_MOV_R64_TO_RM64);
|
||||
asm_x64_write_r64_disp(as, src_r64, dest_r64, dest_disp);
|
||||
}
|
||||
|
||||
void asm_x64_mov_mem8_to_r64zx(asm_x64_t *as, int src_r64, int src_disp, int dest_r64) {
|
||||
assert(src_r64 < 8);
|
||||
if (dest_r64 < 8) {
|
||||
asm_x64_write_byte_2(as, 0x0f, OPCODE_MOVZX_RM8_TO_R64);
|
||||
} else {
|
||||
asm_x64_write_byte_3(as, REX_PREFIX | REX_R, 0x0f, OPCODE_MOVZX_RM8_TO_R64);
|
||||
}
|
||||
asm_x64_write_r64_disp(as, dest_r64, src_r64, src_disp);
|
||||
}
|
||||
|
||||
void asm_x64_mov_mem16_to_r64zx(asm_x64_t *as, int src_r64, int src_disp, int dest_r64) {
|
||||
assert(src_r64 < 8);
|
||||
if (dest_r64 < 8) {
|
||||
asm_x64_write_byte_2(as, 0x0f, OPCODE_MOVZX_RM16_TO_R64);
|
||||
} else {
|
||||
asm_x64_write_byte_3(as, REX_PREFIX | REX_R, 0x0f, OPCODE_MOVZX_RM16_TO_R64);
|
||||
}
|
||||
asm_x64_write_r64_disp(as, dest_r64, src_r64, src_disp);
|
||||
}
|
||||
|
||||
void asm_x64_mov_mem32_to_r64zx(asm_x64_t *as, int src_r64, int src_disp, int dest_r64) {
|
||||
assert(src_r64 < 8);
|
||||
if (dest_r64 < 8) {
|
||||
asm_x64_write_byte_1(as, OPCODE_MOV_RM64_TO_R64);
|
||||
} else {
|
||||
asm_x64_write_byte_2(as, REX_PREFIX | REX_R, OPCODE_MOV_RM64_TO_R64);
|
||||
}
|
||||
asm_x64_write_r64_disp(as, dest_r64, src_r64, src_disp);
|
||||
}
|
||||
|
||||
void asm_x64_mov_mem64_to_r64(asm_x64_t *as, int src_r64, int src_disp, int dest_r64) {
|
||||
// use REX prefix for 64 bit operation
|
||||
asm_x64_write_byte_2(as, REX_PREFIX | REX_W | REX_R_FROM_R64(dest_r64) | REX_B_FROM_R64(src_r64), OPCODE_MOV_RM64_TO_R64);
|
||||
asm_x64_write_r64_disp(as, dest_r64, src_r64, src_disp);
|
||||
}
|
||||
|
||||
STATIC void asm_x64_lea_disp_to_r64(asm_x64_t *as, int src_r64, int src_disp, int dest_r64) {
|
||||
// use REX prefix for 64 bit operation
|
||||
assert(src_r64 < 8);
|
||||
assert(dest_r64 < 8);
|
||||
asm_x64_write_byte_2(as, REX_PREFIX | REX_W, OPCODE_LEA_MEM_TO_R64);
|
||||
asm_x64_write_r64_disp(as, dest_r64, src_r64, src_disp);
|
||||
}
|
||||
|
||||
/*
|
||||
void asm_x64_mov_i8_to_r8(asm_x64_t *as, int src_i8, int dest_r64) {
|
||||
assert(dest_r64 < 8);
|
||||
asm_x64_write_byte_2(as, OPCODE_MOV_I8_TO_R8 | dest_r64, src_i8);
|
||||
}
|
||||
*/
|
||||
|
||||
size_t asm_x64_mov_i32_to_r64(asm_x64_t *as, int src_i32, int dest_r64) {
|
||||
// cpu defaults to i32 to r64, with zero extension
|
||||
if (dest_r64 < 8) {
|
||||
asm_x64_write_byte_1(as, OPCODE_MOV_I64_TO_R64 | dest_r64);
|
||||
} else {
|
||||
asm_x64_write_byte_2(as, REX_PREFIX | REX_B, OPCODE_MOV_I64_TO_R64 | (dest_r64 & 7));
|
||||
}
|
||||
size_t loc = mp_asm_base_get_code_pos(&as->base);
|
||||
asm_x64_write_word32(as, src_i32);
|
||||
return loc;
|
||||
}
|
||||
|
||||
void asm_x64_mov_i64_to_r64(asm_x64_t *as, int64_t src_i64, int dest_r64) {
|
||||
// cpu defaults to i32 to r64
|
||||
// to mov i64 to r64 need to use REX prefix
|
||||
asm_x64_write_byte_2(as,
|
||||
REX_PREFIX | REX_W | (dest_r64 < 8 ? 0 : REX_B),
|
||||
OPCODE_MOV_I64_TO_R64 | (dest_r64 & 7));
|
||||
asm_x64_write_word64(as, src_i64);
|
||||
}
|
||||
|
||||
void asm_x64_mov_i64_to_r64_optimised(asm_x64_t *as, int64_t src_i64, int dest_r64) {
|
||||
// TODO use movzx, movsx if possible
|
||||
if (UNSIGNED_FIT32(src_i64)) {
|
||||
// 5 bytes
|
||||
asm_x64_mov_i32_to_r64(as, src_i64 & 0xffffffff, dest_r64);
|
||||
} else {
|
||||
// 10 bytes
|
||||
asm_x64_mov_i64_to_r64(as, src_i64, dest_r64);
|
||||
}
|
||||
}
|
||||
|
||||
void asm_x64_and_r64_r64(asm_x64_t *as, int dest_r64, int src_r64) {
|
||||
asm_x64_generic_r64_r64(as, dest_r64, src_r64, OPCODE_AND_R64_TO_RM64);
|
||||
}
|
||||
|
||||
void asm_x64_or_r64_r64(asm_x64_t *as, int dest_r64, int src_r64) {
|
||||
asm_x64_generic_r64_r64(as, dest_r64, src_r64, OPCODE_OR_R64_TO_RM64);
|
||||
}
|
||||
|
||||
void asm_x64_xor_r64_r64(asm_x64_t *as, int dest_r64, int src_r64) {
|
||||
asm_x64_generic_r64_r64(as, dest_r64, src_r64, OPCODE_XOR_R64_TO_RM64);
|
||||
}
|
||||
|
||||
void asm_x64_shl_r64_cl(asm_x64_t *as, int dest_r64) {
|
||||
asm_x64_generic_r64_r64(as, dest_r64, 4, OPCODE_SHL_RM64_CL);
|
||||
}
|
||||
|
||||
void asm_x64_shr_r64_cl(asm_x64_t *as, int dest_r64) {
|
||||
asm_x64_generic_r64_r64(as, dest_r64, 5, OPCODE_SHR_RM64_CL);
|
||||
}
|
||||
|
||||
void asm_x64_sar_r64_cl(asm_x64_t *as, int dest_r64) {
|
||||
asm_x64_generic_r64_r64(as, dest_r64, 7, OPCODE_SAR_RM64_CL);
|
||||
}
|
||||
|
||||
void asm_x64_add_r64_r64(asm_x64_t *as, int dest_r64, int src_r64) {
|
||||
asm_x64_generic_r64_r64(as, dest_r64, src_r64, OPCODE_ADD_R64_TO_RM64);
|
||||
}
|
||||
|
||||
void asm_x64_sub_r64_r64(asm_x64_t *as, int dest_r64, int src_r64) {
|
||||
asm_x64_generic_r64_r64(as, dest_r64, src_r64, OPCODE_SUB_R64_FROM_RM64);
|
||||
}
|
||||
|
||||
void asm_x64_mul_r64_r64(asm_x64_t *as, int dest_r64, int src_r64) {
|
||||
// imul reg64, reg/mem64 -- 0x0f 0xaf /r
|
||||
asm_x64_write_byte_1(as, REX_PREFIX | REX_W | REX_R_FROM_R64(dest_r64) | REX_B_FROM_R64(src_r64));
|
||||
asm_x64_write_byte_3(as, 0x0f, 0xaf, MODRM_R64(dest_r64) | MODRM_RM_REG | MODRM_RM_R64(src_r64));
|
||||
}
|
||||
|
||||
/*
|
||||
void asm_x64_sub_i32_from_r32(asm_x64_t *as, int src_i32, int dest_r32) {
|
||||
if (SIGNED_FIT8(src_i32)) {
|
||||
// defaults to 32 bit operation
|
||||
asm_x64_write_byte_2(as, OPCODE_SUB_I8_FROM_RM64, MODRM_R64(5) | MODRM_RM_REG | MODRM_RM_R64(dest_r32));
|
||||
asm_x64_write_byte_1(as, src_i32 & 0xff);
|
||||
} else {
|
||||
// defaults to 32 bit operation
|
||||
asm_x64_write_byte_2(as, OPCODE_SUB_I32_FROM_RM64, MODRM_R64(5) | MODRM_RM_REG | MODRM_RM_R64(dest_r32));
|
||||
asm_x64_write_word32(as, src_i32);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
STATIC void asm_x64_sub_r64_i32(asm_x64_t *as, int dest_r64, int src_i32) {
|
||||
assert(dest_r64 < 8);
|
||||
if (SIGNED_FIT8(src_i32)) {
|
||||
// use REX prefix for 64 bit operation
|
||||
asm_x64_write_byte_3(as, REX_PREFIX | REX_W, OPCODE_SUB_I8_FROM_RM64, MODRM_R64(5) | MODRM_RM_REG | MODRM_RM_R64(dest_r64));
|
||||
asm_x64_write_byte_1(as, src_i32 & 0xff);
|
||||
} else {
|
||||
// use REX prefix for 64 bit operation
|
||||
asm_x64_write_byte_3(as, REX_PREFIX | REX_W, OPCODE_SUB_I32_FROM_RM64, MODRM_R64(5) | MODRM_RM_REG | MODRM_RM_R64(dest_r64));
|
||||
asm_x64_write_word32(as, src_i32);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
void asm_x64_shl_r32_by_imm(asm_x64_t *as, int r32, int imm) {
|
||||
asm_x64_write_byte_2(as, OPCODE_SHL_RM32_BY_I8, MODRM_R64(4) | MODRM_RM_REG | MODRM_RM_R64(r32));
|
||||
asm_x64_write_byte_1(as, imm);
|
||||
}
|
||||
|
||||
void asm_x64_shr_r32_by_imm(asm_x64_t *as, int r32, int imm) {
|
||||
asm_x64_write_byte_2(as, OPCODE_SHR_RM32_BY_I8, MODRM_R64(5) | MODRM_RM_REG | MODRM_RM_R64(r32));
|
||||
asm_x64_write_byte_1(as, imm);
|
||||
}
|
||||
|
||||
void asm_x64_sar_r32_by_imm(asm_x64_t *as, int r32, int imm) {
|
||||
asm_x64_write_byte_2(as, OPCODE_SAR_RM32_BY_I8, MODRM_R64(7) | MODRM_RM_REG | MODRM_RM_R64(r32));
|
||||
asm_x64_write_byte_1(as, imm);
|
||||
}
|
||||
*/
|
||||
|
||||
void asm_x64_cmp_r64_with_r64(asm_x64_t *as, int src_r64_a, int src_r64_b) {
|
||||
asm_x64_generic_r64_r64(as, src_r64_b, src_r64_a, OPCODE_CMP_R64_WITH_RM64);
|
||||
}
|
||||
|
||||
/*
|
||||
void asm_x64_cmp_i32_with_r32(asm_x64_t *as, int src_i32, int src_r32) {
|
||||
if (SIGNED_FIT8(src_i32)) {
|
||||
asm_x64_write_byte_2(as, OPCODE_CMP_I8_WITH_RM32, MODRM_R64(7) | MODRM_RM_REG | MODRM_RM_R64(src_r32));
|
||||
asm_x64_write_byte_1(as, src_i32 & 0xff);
|
||||
} else {
|
||||
asm_x64_write_byte_2(as, OPCODE_CMP_I32_WITH_RM32, MODRM_R64(7) | MODRM_RM_REG | MODRM_RM_R64(src_r32));
|
||||
asm_x64_write_word32(as, src_i32);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
void asm_x64_test_r8_with_r8(asm_x64_t *as, int src_r64_a, int src_r64_b) {
|
||||
assert(src_r64_a < 8);
|
||||
assert(src_r64_b < 8);
|
||||
asm_x64_write_byte_2(as, OPCODE_TEST_R8_WITH_RM8, MODRM_R64(src_r64_a) | MODRM_RM_REG | MODRM_RM_R64(src_r64_b));
|
||||
}
|
||||
|
||||
void asm_x64_test_r64_with_r64(asm_x64_t *as, int src_r64_a, int src_r64_b) {
|
||||
asm_x64_generic_r64_r64(as, src_r64_b, src_r64_a, OPCODE_TEST_R64_WITH_RM64);
|
||||
}
|
||||
|
||||
void asm_x64_setcc_r8(asm_x64_t *as, int jcc_type, int dest_r8) {
|
||||
assert(dest_r8 < 8);
|
||||
asm_x64_write_byte_3(as, OPCODE_SETCC_RM8_A, OPCODE_SETCC_RM8_B | jcc_type, MODRM_R64(0) | MODRM_RM_REG | MODRM_RM_R64(dest_r8));
|
||||
}
|
||||
|
||||
void asm_x64_jmp_reg(asm_x64_t *as, int src_r64) {
|
||||
assert(src_r64 < 8);
|
||||
asm_x64_write_byte_2(as, OPCODE_JMP_RM64, MODRM_R64(4) | MODRM_RM_REG | MODRM_RM_R64(src_r64));
|
||||
}
|
||||
|
||||
STATIC mp_uint_t get_label_dest(asm_x64_t *as, mp_uint_t label) {
|
||||
assert(label < as->base.max_num_labels);
|
||||
return as->base.label_offsets[label];
|
||||
}
|
||||
|
||||
void asm_x64_jmp_label(asm_x64_t *as, mp_uint_t label) {
|
||||
mp_uint_t dest = get_label_dest(as, label);
|
||||
mp_int_t rel = dest - as->base.code_offset;
|
||||
if (dest != (mp_uint_t)-1 && rel < 0) {
|
||||
// is a backwards jump, so we know the size of the jump on the first pass
|
||||
// calculate rel assuming 8 bit relative jump
|
||||
rel -= 2;
|
||||
if (SIGNED_FIT8(rel)) {
|
||||
asm_x64_write_byte_2(as, OPCODE_JMP_REL8, rel & 0xff);
|
||||
} else {
|
||||
rel += 2;
|
||||
goto large_jump;
|
||||
}
|
||||
} else {
|
||||
// is a forwards jump, so need to assume it's large
|
||||
large_jump:
|
||||
rel -= 5;
|
||||
asm_x64_write_byte_1(as, OPCODE_JMP_REL32);
|
||||
asm_x64_write_word32(as, rel);
|
||||
}
|
||||
}
|
||||
|
||||
void asm_x64_jcc_label(asm_x64_t *as, int jcc_type, mp_uint_t label) {
|
||||
mp_uint_t dest = get_label_dest(as, label);
|
||||
mp_int_t rel = dest - as->base.code_offset;
|
||||
if (dest != (mp_uint_t)-1 && rel < 0) {
|
||||
// is a backwards jump, so we know the size of the jump on the first pass
|
||||
// calculate rel assuming 8 bit relative jump
|
||||
rel -= 2;
|
||||
if (SIGNED_FIT8(rel)) {
|
||||
asm_x64_write_byte_2(as, OPCODE_JCC_REL8 | jcc_type, rel & 0xff);
|
||||
} else {
|
||||
rel += 2;
|
||||
goto large_jump;
|
||||
}
|
||||
} else {
|
||||
// is a forwards jump, so need to assume it's large
|
||||
large_jump:
|
||||
rel -= 6;
|
||||
asm_x64_write_byte_2(as, OPCODE_JCC_REL32_A, OPCODE_JCC_REL32_B | jcc_type);
|
||||
asm_x64_write_word32(as, rel);
|
||||
}
|
||||
}
|
||||
|
||||
void asm_x64_entry(asm_x64_t *as, int num_locals) {
|
||||
assert(num_locals >= 0);
|
||||
asm_x64_push_r64(as, ASM_X64_REG_RBP);
|
||||
asm_x64_push_r64(as, ASM_X64_REG_RBX);
|
||||
asm_x64_push_r64(as, ASM_X64_REG_R12);
|
||||
asm_x64_push_r64(as, ASM_X64_REG_R13);
|
||||
num_locals |= 1; // make it odd so stack is aligned on 16 byte boundary
|
||||
asm_x64_sub_r64_i32(as, ASM_X64_REG_RSP, num_locals * WORD_SIZE);
|
||||
as->num_locals = num_locals;
|
||||
}
|
||||
|
||||
void asm_x64_exit(asm_x64_t *as) {
|
||||
asm_x64_sub_r64_i32(as, ASM_X64_REG_RSP, -as->num_locals * WORD_SIZE);
|
||||
asm_x64_pop_r64(as, ASM_X64_REG_R13);
|
||||
asm_x64_pop_r64(as, ASM_X64_REG_R12);
|
||||
asm_x64_pop_r64(as, ASM_X64_REG_RBX);
|
||||
asm_x64_pop_r64(as, ASM_X64_REG_RBP);
|
||||
asm_x64_ret(as);
|
||||
}
|
||||
|
||||
// locals:
|
||||
// - stored on the stack in ascending order
|
||||
// - numbered 0 through as->num_locals-1
|
||||
// - RSP points to the first local
|
||||
//
|
||||
// | RSP
|
||||
// v
|
||||
// l0 l1 l2 ... l(n-1)
|
||||
// ^ ^
|
||||
// | low address | high address in RAM
|
||||
//
|
||||
STATIC int asm_x64_local_offset_from_rsp(asm_x64_t *as, int local_num) {
|
||||
(void)as;
|
||||
// Stack is full descending, RSP points to local0
|
||||
return local_num * WORD_SIZE;
|
||||
}
|
||||
|
||||
void asm_x64_mov_local_to_r64(asm_x64_t *as, int src_local_num, int dest_r64) {
|
||||
asm_x64_mov_mem64_to_r64(as, ASM_X64_REG_RSP, asm_x64_local_offset_from_rsp(as, src_local_num), dest_r64);
|
||||
}
|
||||
|
||||
void asm_x64_mov_r64_to_local(asm_x64_t *as, int src_r64, int dest_local_num) {
|
||||
asm_x64_mov_r64_to_mem64(as, src_r64, ASM_X64_REG_RSP, asm_x64_local_offset_from_rsp(as, dest_local_num));
|
||||
}
|
||||
|
||||
void asm_x64_mov_local_addr_to_r64(asm_x64_t *as, int local_num, int dest_r64) {
|
||||
int offset = asm_x64_local_offset_from_rsp(as, local_num);
|
||||
if (offset == 0) {
|
||||
asm_x64_mov_r64_r64(as, dest_r64, ASM_X64_REG_RSP);
|
||||
} else {
|
||||
asm_x64_lea_disp_to_r64(as, ASM_X64_REG_RSP, offset, dest_r64);
|
||||
}
|
||||
}
|
||||
|
||||
void asm_x64_mov_reg_pcrel(asm_x64_t *as, int dest_r64, mp_uint_t label) {
|
||||
mp_uint_t dest = get_label_dest(as, label);
|
||||
mp_int_t rel = dest - (as->base.code_offset + 7);
|
||||
asm_x64_write_byte_3(as, REX_PREFIX | REX_W | REX_R_FROM_R64(dest_r64), OPCODE_LEA_MEM_TO_R64, MODRM_R64(dest_r64) | MODRM_RM_R64(5));
|
||||
asm_x64_write_word32(as, rel);
|
||||
}
|
||||
|
||||
/*
|
||||
void asm_x64_push_local(asm_x64_t *as, int local_num) {
|
||||
asm_x64_push_disp(as, ASM_X64_REG_RSP, asm_x64_local_offset_from_rsp(as, local_num));
|
||||
}
|
||||
|
||||
void asm_x64_push_local_addr(asm_x64_t *as, int local_num, int temp_r64) {
|
||||
asm_x64_mov_r64_r64(as, temp_r64, ASM_X64_REG_RSP);
|
||||
asm_x64_add_i32_to_r32(as, asm_x64_local_offset_from_rsp(as, local_num), temp_r64);
|
||||
asm_x64_push_r64(as, temp_r64);
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
can't use these because code might be relocated when resized
|
||||
|
||||
void asm_x64_call(asm_x64_t *as, void* func) {
|
||||
asm_x64_sub_i32_from_r32(as, 8, ASM_X64_REG_RSP);
|
||||
asm_x64_write_byte_1(as, OPCODE_CALL_REL32);
|
||||
asm_x64_write_word32(as, func - (void*)(as->code_cur + 4));
|
||||
asm_x64_mov_r64_r64(as, ASM_X64_REG_RSP, ASM_X64_REG_RBP);
|
||||
}
|
||||
|
||||
void asm_x64_call_i1(asm_x64_t *as, void* func, int i1) {
|
||||
asm_x64_sub_i32_from_r32(as, 8, ASM_X64_REG_RSP);
|
||||
asm_x64_sub_i32_from_r32(as, 12, ASM_X64_REG_RSP);
|
||||
asm_x64_push_i32(as, i1);
|
||||
asm_x64_write_byte_1(as, OPCODE_CALL_REL32);
|
||||
asm_x64_write_word32(as, func - (void*)(as->code_cur + 4));
|
||||
asm_x64_add_i32_to_r32(as, 16, ASM_X64_REG_RSP);
|
||||
asm_x64_mov_r64_r64(as, ASM_X64_REG_RSP, ASM_X64_REG_RBP);
|
||||
}
|
||||
*/
|
||||
|
||||
void asm_x64_call_ind(asm_x64_t *as, size_t fun_id, int temp_r64) {
|
||||
assert(temp_r64 < 8);
|
||||
asm_x64_mov_mem64_to_r64(as, ASM_X64_REG_FUN_TABLE, fun_id * WORD_SIZE, temp_r64);
|
||||
asm_x64_write_byte_2(as, OPCODE_CALL_RM32, MODRM_R64(2) | MODRM_RM_REG | MODRM_RM_R64(temp_r64));
|
||||
}
|
||||
|
||||
#endif // MICROPY_EMIT_X64
|
||||
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013, 2014 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef MICROPY_INCLUDED_PY_ASMX64_H
|
||||
#define MICROPY_INCLUDED_PY_ASMX64_H
|
||||
|
||||
#include "py/mpconfig.h"
|
||||
#include "py/misc.h"
|
||||
#include "py/asmbase.h"
|
||||
|
||||
// AMD64 calling convention is:
|
||||
// - args pass in: RDI, RSI, RDX, RCX, R08, R09
|
||||
// - return value in RAX
|
||||
// - stack must be aligned on a 16-byte boundary before all calls
|
||||
// - RAX, RCX, RDX, RSI, RDI, R08, R09, R10, R11 are caller-save
|
||||
// - RBX, RBP, R12, R13, R14, R15 are callee-save
|
||||
|
||||
// In the functions below, argument order follows x86 docs and generally
|
||||
// the destination is the first argument.
|
||||
// NOTE: this is a change from the old convention used in this file and
|
||||
// some functions still use the old (reverse) convention.
|
||||
|
||||
#define ASM_X64_REG_RAX (0)
|
||||
#define ASM_X64_REG_RCX (1)
|
||||
#define ASM_X64_REG_RDX (2)
|
||||
#define ASM_X64_REG_RBX (3)
|
||||
#define ASM_X64_REG_RSP (4)
|
||||
#define ASM_X64_REG_RBP (5)
|
||||
#define ASM_X64_REG_RSI (6)
|
||||
#define ASM_X64_REG_RDI (7)
|
||||
#define ASM_X64_REG_R08 (8)
|
||||
#define ASM_X64_REG_R09 (9)
|
||||
#define ASM_X64_REG_R10 (10)
|
||||
#define ASM_X64_REG_R11 (11)
|
||||
#define ASM_X64_REG_R12 (12)
|
||||
#define ASM_X64_REG_R13 (13)
|
||||
#define ASM_X64_REG_R14 (14)
|
||||
#define ASM_X64_REG_R15 (15)
|
||||
|
||||
// condition codes, used for jcc and setcc (despite their j-name!)
|
||||
#define ASM_X64_CC_JB (0x2) // below, unsigned
|
||||
#define ASM_X64_CC_JAE (0x3) // above or equal, unsigned
|
||||
#define ASM_X64_CC_JZ (0x4)
|
||||
#define ASM_X64_CC_JE (0x4)
|
||||
#define ASM_X64_CC_JNZ (0x5)
|
||||
#define ASM_X64_CC_JNE (0x5)
|
||||
#define ASM_X64_CC_JBE (0x6) // below or equal, unsigned
|
||||
#define ASM_X64_CC_JA (0x7) // above, unsigned
|
||||
#define ASM_X64_CC_JL (0xc) // less, signed
|
||||
#define ASM_X64_CC_JGE (0xd) // greater or equal, signed
|
||||
#define ASM_X64_CC_JLE (0xe) // less or equal, signed
|
||||
#define ASM_X64_CC_JG (0xf) // greater, signed
|
||||
|
||||
typedef struct _asm_x64_t {
|
||||
mp_asm_base_t base;
|
||||
int num_locals;
|
||||
} asm_x64_t;
|
||||
|
||||
static inline void asm_x64_end_pass(asm_x64_t *as) {
|
||||
(void)as;
|
||||
}
|
||||
|
||||
void asm_x64_nop(asm_x64_t *as);
|
||||
void asm_x64_push_r64(asm_x64_t *as, int src_r64);
|
||||
void asm_x64_pop_r64(asm_x64_t *as, int dest_r64);
|
||||
void asm_x64_mov_r64_r64(asm_x64_t *as, int dest_r64, int src_r64);
|
||||
size_t asm_x64_mov_i32_to_r64(asm_x64_t *as, int src_i32, int dest_r64);
|
||||
void asm_x64_mov_i64_to_r64(asm_x64_t *as, int64_t src_i64, int dest_r64);
|
||||
void asm_x64_mov_i64_to_r64_optimised(asm_x64_t *as, int64_t src_i64, int dest_r64);
|
||||
void asm_x64_mov_r8_to_mem8(asm_x64_t *as, int src_r64, int dest_r64, int dest_disp);
|
||||
void asm_x64_mov_r16_to_mem16(asm_x64_t *as, int src_r64, int dest_r64, int dest_disp);
|
||||
void asm_x64_mov_r32_to_mem32(asm_x64_t *as, int src_r64, int dest_r64, int dest_disp);
|
||||
void asm_x64_mov_r64_to_mem64(asm_x64_t *as, int src_r64, int dest_r64, int dest_disp);
|
||||
void asm_x64_mov_mem8_to_r64zx(asm_x64_t *as, int src_r64, int src_disp, int dest_r64);
|
||||
void asm_x64_mov_mem16_to_r64zx(asm_x64_t *as, int src_r64, int src_disp, int dest_r64);
|
||||
void asm_x64_mov_mem32_to_r64zx(asm_x64_t *as, int src_r64, int src_disp, int dest_r64);
|
||||
void asm_x64_mov_mem64_to_r64(asm_x64_t *as, int src_r64, int src_disp, int dest_r64);
|
||||
void asm_x64_and_r64_r64(asm_x64_t *as, int dest_r64, int src_r64);
|
||||
void asm_x64_or_r64_r64(asm_x64_t *as, int dest_r64, int src_r64);
|
||||
void asm_x64_xor_r64_r64(asm_x64_t *as, int dest_r64, int src_r64);
|
||||
void asm_x64_shl_r64_cl(asm_x64_t *as, int dest_r64);
|
||||
void asm_x64_shr_r64_cl(asm_x64_t *as, int dest_r64);
|
||||
void asm_x64_sar_r64_cl(asm_x64_t *as, int dest_r64);
|
||||
void asm_x64_add_r64_r64(asm_x64_t *as, int dest_r64, int src_r64);
|
||||
void asm_x64_sub_r64_r64(asm_x64_t *as, int dest_r64, int src_r64);
|
||||
void asm_x64_mul_r64_r64(asm_x64_t *as, int dest_r64, int src_r64);
|
||||
void asm_x64_cmp_r64_with_r64(asm_x64_t *as, int src_r64_a, int src_r64_b);
|
||||
void asm_x64_test_r8_with_r8(asm_x64_t *as, int src_r64_a, int src_r64_b);
|
||||
void asm_x64_test_r64_with_r64(asm_x64_t *as, int src_r64_a, int src_r64_b);
|
||||
void asm_x64_setcc_r8(asm_x64_t *as, int jcc_type, int dest_r8);
|
||||
void asm_x64_jmp_reg(asm_x64_t *as, int src_r64);
|
||||
void asm_x64_jmp_label(asm_x64_t *as, mp_uint_t label);
|
||||
void asm_x64_jcc_label(asm_x64_t *as, int jcc_type, mp_uint_t label);
|
||||
void asm_x64_entry(asm_x64_t *as, int num_locals);
|
||||
void asm_x64_exit(asm_x64_t *as);
|
||||
void asm_x64_mov_local_to_r64(asm_x64_t *as, int src_local_num, int dest_r64);
|
||||
void asm_x64_mov_r64_to_local(asm_x64_t *as, int src_r64, int dest_local_num);
|
||||
void asm_x64_mov_local_addr_to_r64(asm_x64_t *as, int local_num, int dest_r64);
|
||||
void asm_x64_mov_reg_pcrel(asm_x64_t *as, int dest_r64, mp_uint_t label);
|
||||
void asm_x64_call_ind(asm_x64_t *as, size_t fun_id, int temp_r32);
|
||||
|
||||
// Holds a pointer to mp_fun_table
|
||||
#define ASM_X64_REG_FUN_TABLE ASM_X64_REG_RBP
|
||||
|
||||
#if GENERIC_ASM_API
|
||||
|
||||
// The following macros provide a (mostly) arch-independent API to
|
||||
// generate native code, and are used by the native emitter.
|
||||
|
||||
#define ASM_WORD_SIZE (8)
|
||||
|
||||
#define REG_RET ASM_X64_REG_RAX
|
||||
#define REG_ARG_1 ASM_X64_REG_RDI
|
||||
#define REG_ARG_2 ASM_X64_REG_RSI
|
||||
#define REG_ARG_3 ASM_X64_REG_RDX
|
||||
#define REG_ARG_4 ASM_X64_REG_RCX
|
||||
#define REG_ARG_5 ASM_X64_REG_R08
|
||||
|
||||
// caller-save
|
||||
#define REG_TEMP0 ASM_X64_REG_RAX
|
||||
#define REG_TEMP1 ASM_X64_REG_RDI
|
||||
#define REG_TEMP2 ASM_X64_REG_RSI
|
||||
|
||||
// callee-save
|
||||
#define REG_LOCAL_1 ASM_X64_REG_RBX
|
||||
#define REG_LOCAL_2 ASM_X64_REG_R12
|
||||
#define REG_LOCAL_3 ASM_X64_REG_R13
|
||||
#define REG_LOCAL_NUM (3)
|
||||
|
||||
// Holds a pointer to mp_fun_table
|
||||
#define REG_FUN_TABLE ASM_X64_REG_FUN_TABLE
|
||||
|
||||
#define ASM_T asm_x64_t
|
||||
#define ASM_END_PASS asm_x64_end_pass
|
||||
#define ASM_ENTRY asm_x64_entry
|
||||
#define ASM_EXIT asm_x64_exit
|
||||
|
||||
#define ASM_JUMP asm_x64_jmp_label
|
||||
#define ASM_JUMP_IF_REG_ZERO(as, reg, label, bool_test) \
|
||||
do { \
|
||||
if (bool_test) { \
|
||||
asm_x64_test_r8_with_r8((as), (reg), (reg)); \
|
||||
} else { \
|
||||
asm_x64_test_r64_with_r64((as), (reg), (reg)); \
|
||||
} \
|
||||
asm_x64_jcc_label(as, ASM_X64_CC_JZ, label); \
|
||||
} while (0)
|
||||
#define ASM_JUMP_IF_REG_NONZERO(as, reg, label, bool_test) \
|
||||
do { \
|
||||
if (bool_test) { \
|
||||
asm_x64_test_r8_with_r8((as), (reg), (reg)); \
|
||||
} else { \
|
||||
asm_x64_test_r64_with_r64((as), (reg), (reg)); \
|
||||
} \
|
||||
asm_x64_jcc_label(as, ASM_X64_CC_JNZ, label); \
|
||||
} while (0)
|
||||
#define ASM_JUMP_IF_REG_EQ(as, reg1, reg2, label) \
|
||||
do { \
|
||||
asm_x64_cmp_r64_with_r64(as, reg1, reg2); \
|
||||
asm_x64_jcc_label(as, ASM_X64_CC_JE, label); \
|
||||
} while (0)
|
||||
#define ASM_JUMP_REG(as, reg) asm_x64_jmp_reg((as), (reg))
|
||||
#define ASM_CALL_IND(as, idx) asm_x64_call_ind(as, idx, ASM_X64_REG_RAX)
|
||||
|
||||
#define ASM_MOV_LOCAL_REG(as, local_num, reg_src) asm_x64_mov_r64_to_local((as), (reg_src), (local_num))
|
||||
#define ASM_MOV_REG_IMM(as, reg_dest, imm) asm_x64_mov_i64_to_r64_optimised((as), (imm), (reg_dest))
|
||||
#define ASM_MOV_REG_IMM_FIX_U16(as, reg_dest, imm) asm_x64_mov_i32_to_r64((as), (imm), (reg_dest))
|
||||
#define ASM_MOV_REG_IMM_FIX_WORD(as, reg_dest, imm) asm_x64_mov_i32_to_r64((as), (imm), (reg_dest))
|
||||
#define ASM_MOV_REG_LOCAL(as, reg_dest, local_num) asm_x64_mov_local_to_r64((as), (local_num), (reg_dest))
|
||||
#define ASM_MOV_REG_REG(as, reg_dest, reg_src) asm_x64_mov_r64_r64((as), (reg_dest), (reg_src))
|
||||
#define ASM_MOV_REG_LOCAL_ADDR(as, reg_dest, local_num) asm_x64_mov_local_addr_to_r64((as), (local_num), (reg_dest))
|
||||
#define ASM_MOV_REG_PCREL(as, reg_dest, label) asm_x64_mov_reg_pcrel((as), (reg_dest), (label))
|
||||
|
||||
#define ASM_LSL_REG(as, reg) asm_x64_shl_r64_cl((as), (reg))
|
||||
#define ASM_LSR_REG(as, reg) asm_x64_shr_r64_cl((as), (reg))
|
||||
#define ASM_ASR_REG(as, reg) asm_x64_sar_r64_cl((as), (reg))
|
||||
#define ASM_OR_REG_REG(as, reg_dest, reg_src) asm_x64_or_r64_r64((as), (reg_dest), (reg_src))
|
||||
#define ASM_XOR_REG_REG(as, reg_dest, reg_src) asm_x64_xor_r64_r64((as), (reg_dest), (reg_src))
|
||||
#define ASM_AND_REG_REG(as, reg_dest, reg_src) asm_x64_and_r64_r64((as), (reg_dest), (reg_src))
|
||||
#define ASM_ADD_REG_REG(as, reg_dest, reg_src) asm_x64_add_r64_r64((as), (reg_dest), (reg_src))
|
||||
#define ASM_SUB_REG_REG(as, reg_dest, reg_src) asm_x64_sub_r64_r64((as), (reg_dest), (reg_src))
|
||||
#define ASM_MUL_REG_REG(as, reg_dest, reg_src) asm_x64_mul_r64_r64((as), (reg_dest), (reg_src))
|
||||
|
||||
#define ASM_LOAD_REG_REG(as, reg_dest, reg_base) asm_x64_mov_mem64_to_r64((as), (reg_base), 0, (reg_dest))
|
||||
#define ASM_LOAD_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_x64_mov_mem64_to_r64((as), (reg_base), 8 * (word_offset), (reg_dest))
|
||||
#define ASM_LOAD8_REG_REG(as, reg_dest, reg_base) asm_x64_mov_mem8_to_r64zx((as), (reg_base), 0, (reg_dest))
|
||||
#define ASM_LOAD16_REG_REG(as, reg_dest, reg_base) asm_x64_mov_mem16_to_r64zx((as), (reg_base), 0, (reg_dest))
|
||||
#define ASM_LOAD32_REG_REG(as, reg_dest, reg_base) asm_x64_mov_mem32_to_r64zx((as), (reg_base), 0, (reg_dest))
|
||||
|
||||
#define ASM_STORE_REG_REG(as, reg_src, reg_base) asm_x64_mov_r64_to_mem64((as), (reg_src), (reg_base), 0)
|
||||
#define ASM_STORE_REG_REG_OFFSET(as, reg_src, reg_base, word_offset) asm_x64_mov_r64_to_mem64((as), (reg_src), (reg_base), 8 * (word_offset))
|
||||
#define ASM_STORE8_REG_REG(as, reg_src, reg_base) asm_x64_mov_r8_to_mem8((as), (reg_src), (reg_base), 0)
|
||||
#define ASM_STORE16_REG_REG(as, reg_src, reg_base) asm_x64_mov_r16_to_mem16((as), (reg_src), (reg_base), 0)
|
||||
#define ASM_STORE32_REG_REG(as, reg_src, reg_base) asm_x64_mov_r32_to_mem32((as), (reg_src), (reg_base), 0)
|
||||
|
||||
#endif // GENERIC_ASM_API
|
||||
|
||||
#endif // MICROPY_INCLUDED_PY_ASMX64_H
|
||||
@@ -0,0 +1,535 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "py/mpconfig.h"
|
||||
|
||||
// wrapper around everything in this file
|
||||
#if MICROPY_EMIT_X86
|
||||
|
||||
#include "py/asmx86.h"
|
||||
|
||||
/* all offsets are measured in multiples of 4 bytes */
|
||||
#define WORD_SIZE (4)
|
||||
|
||||
#define OPCODE_NOP (0x90)
|
||||
#define OPCODE_PUSH_R32 (0x50)
|
||||
// #define OPCODE_PUSH_I32 (0x68)
|
||||
// #define OPCODE_PUSH_M32 (0xff) /* /6 */
|
||||
#define OPCODE_POP_R32 (0x58)
|
||||
#define OPCODE_RET (0xc3)
|
||||
// #define OPCODE_MOV_I8_TO_R8 (0xb0) /* +rb */
|
||||
#define OPCODE_MOV_I32_TO_R32 (0xb8)
|
||||
// #define OPCODE_MOV_I32_TO_RM32 (0xc7)
|
||||
#define OPCODE_MOV_R8_TO_RM8 (0x88) /* /r */
|
||||
#define OPCODE_MOV_R32_TO_RM32 (0x89) /* /r */
|
||||
#define OPCODE_MOV_RM32_TO_R32 (0x8b) /* /r */
|
||||
#define OPCODE_MOVZX_RM8_TO_R32 (0xb6) /* 0x0f 0xb6/r */
|
||||
#define OPCODE_MOVZX_RM16_TO_R32 (0xb7) /* 0x0f 0xb7/r */
|
||||
#define OPCODE_LEA_MEM_TO_R32 (0x8d) /* /r */
|
||||
#define OPCODE_AND_R32_TO_RM32 (0x21) /* /r */
|
||||
#define OPCODE_OR_R32_TO_RM32 (0x09) /* /r */
|
||||
#define OPCODE_XOR_R32_TO_RM32 (0x31) /* /r */
|
||||
#define OPCODE_ADD_R32_TO_RM32 (0x01)
|
||||
#define OPCODE_ADD_I32_TO_RM32 (0x81) /* /0 */
|
||||
#define OPCODE_ADD_I8_TO_RM32 (0x83) /* /0 */
|
||||
#define OPCODE_SUB_R32_FROM_RM32 (0x29)
|
||||
#define OPCODE_SUB_I32_FROM_RM32 (0x81) /* /5 */
|
||||
#define OPCODE_SUB_I8_FROM_RM32 (0x83) /* /5 */
|
||||
// #define OPCODE_SHL_RM32_BY_I8 (0xc1) /* /4 */
|
||||
// #define OPCODE_SHR_RM32_BY_I8 (0xc1) /* /5 */
|
||||
// #define OPCODE_SAR_RM32_BY_I8 (0xc1) /* /7 */
|
||||
#define OPCODE_SHL_RM32_CL (0xd3) /* /4 */
|
||||
#define OPCODE_SHR_RM32_CL (0xd3) /* /5 */
|
||||
#define OPCODE_SAR_RM32_CL (0xd3) /* /7 */
|
||||
// #define OPCODE_CMP_I32_WITH_RM32 (0x81) /* /7 */
|
||||
// #define OPCODE_CMP_I8_WITH_RM32 (0x83) /* /7 */
|
||||
#define OPCODE_CMP_R32_WITH_RM32 (0x39)
|
||||
// #define OPCODE_CMP_RM32_WITH_R32 (0x3b)
|
||||
#define OPCODE_TEST_R8_WITH_RM8 (0x84) /* /r */
|
||||
#define OPCODE_TEST_R32_WITH_RM32 (0x85) /* /r */
|
||||
#define OPCODE_JMP_REL8 (0xeb)
|
||||
#define OPCODE_JMP_REL32 (0xe9)
|
||||
#define OPCODE_JMP_RM32 (0xff) /* /4 */
|
||||
#define OPCODE_JCC_REL8 (0x70) /* | jcc type */
|
||||
#define OPCODE_JCC_REL32_A (0x0f)
|
||||
#define OPCODE_JCC_REL32_B (0x80) /* | jcc type */
|
||||
#define OPCODE_SETCC_RM8_A (0x0f)
|
||||
#define OPCODE_SETCC_RM8_B (0x90) /* | jcc type, /0 */
|
||||
#define OPCODE_CALL_REL32 (0xe8)
|
||||
#define OPCODE_CALL_RM32 (0xff) /* /2 */
|
||||
#define OPCODE_LEAVE (0xc9)
|
||||
|
||||
#define MODRM_R32(x) ((x) << 3)
|
||||
#define MODRM_RM_DISP0 (0x00)
|
||||
#define MODRM_RM_DISP8 (0x40)
|
||||
#define MODRM_RM_DISP32 (0x80)
|
||||
#define MODRM_RM_REG (0xc0)
|
||||
#define MODRM_RM_R32(x) (x)
|
||||
|
||||
#define OP_SIZE_PREFIX (0x66)
|
||||
|
||||
#define IMM32_L0(x) ((x) & 0xff)
|
||||
#define IMM32_L1(x) (((x) >> 8) & 0xff)
|
||||
#define IMM32_L2(x) (((x) >> 16) & 0xff)
|
||||
#define IMM32_L3(x) (((x) >> 24) & 0xff)
|
||||
|
||||
#define SIGNED_FIT8(x) (((x) & 0xffffff80) == 0) || (((x) & 0xffffff80) == 0xffffff80)
|
||||
|
||||
STATIC void asm_x86_write_byte_1(asm_x86_t *as, byte b1) {
|
||||
byte *c = mp_asm_base_get_cur_to_write_bytes(&as->base, 1);
|
||||
if (c != NULL) {
|
||||
c[0] = b1;
|
||||
}
|
||||
}
|
||||
|
||||
STATIC void asm_x86_write_byte_2(asm_x86_t *as, byte b1, byte b2) {
|
||||
byte *c = mp_asm_base_get_cur_to_write_bytes(&as->base, 2);
|
||||
if (c != NULL) {
|
||||
c[0] = b1;
|
||||
c[1] = b2;
|
||||
}
|
||||
}
|
||||
|
||||
STATIC void asm_x86_write_byte_3(asm_x86_t *as, byte b1, byte b2, byte b3) {
|
||||
byte *c = mp_asm_base_get_cur_to_write_bytes(&as->base, 3);
|
||||
if (c != NULL) {
|
||||
c[0] = b1;
|
||||
c[1] = b2;
|
||||
c[2] = b3;
|
||||
}
|
||||
}
|
||||
|
||||
STATIC void asm_x86_write_word32(asm_x86_t *as, int w32) {
|
||||
byte *c = mp_asm_base_get_cur_to_write_bytes(&as->base, 4);
|
||||
if (c != NULL) {
|
||||
c[0] = IMM32_L0(w32);
|
||||
c[1] = IMM32_L1(w32);
|
||||
c[2] = IMM32_L2(w32);
|
||||
c[3] = IMM32_L3(w32);
|
||||
}
|
||||
}
|
||||
|
||||
STATIC void asm_x86_write_r32_disp(asm_x86_t *as, int r32, int disp_r32, int disp_offset) {
|
||||
uint8_t rm_disp;
|
||||
if (disp_offset == 0 && disp_r32 != ASM_X86_REG_EBP) {
|
||||
rm_disp = MODRM_RM_DISP0;
|
||||
} else if (SIGNED_FIT8(disp_offset)) {
|
||||
rm_disp = MODRM_RM_DISP8;
|
||||
} else {
|
||||
rm_disp = MODRM_RM_DISP32;
|
||||
}
|
||||
asm_x86_write_byte_1(as, MODRM_R32(r32) | rm_disp | MODRM_RM_R32(disp_r32));
|
||||
if (disp_r32 == ASM_X86_REG_ESP) {
|
||||
// Special case for esp, it needs a SIB byte
|
||||
asm_x86_write_byte_1(as, 0x24);
|
||||
}
|
||||
if (rm_disp == MODRM_RM_DISP8) {
|
||||
asm_x86_write_byte_1(as, IMM32_L0(disp_offset));
|
||||
} else if (rm_disp == MODRM_RM_DISP32) {
|
||||
asm_x86_write_word32(as, disp_offset);
|
||||
}
|
||||
}
|
||||
|
||||
STATIC void asm_x86_generic_r32_r32(asm_x86_t *as, int dest_r32, int src_r32, int op) {
|
||||
asm_x86_write_byte_2(as, op, MODRM_R32(src_r32) | MODRM_RM_REG | MODRM_RM_R32(dest_r32));
|
||||
}
|
||||
|
||||
#if 0
|
||||
STATIC void asm_x86_nop(asm_x86_t *as) {
|
||||
asm_x86_write_byte_1(as, OPCODE_NOP);
|
||||
}
|
||||
#endif
|
||||
|
||||
STATIC void asm_x86_push_r32(asm_x86_t *as, int src_r32) {
|
||||
asm_x86_write_byte_1(as, OPCODE_PUSH_R32 | src_r32);
|
||||
}
|
||||
|
||||
#if 0
|
||||
void asm_x86_push_i32(asm_x86_t *as, int src_i32) {
|
||||
asm_x86_write_byte_1(as, OPCODE_PUSH_I32);
|
||||
asm_x86_write_word32(as, src_i32);
|
||||
}
|
||||
|
||||
void asm_x86_push_disp(asm_x86_t *as, int src_r32, int src_offset) {
|
||||
asm_x86_write_byte_1(as, OPCODE_PUSH_M32);
|
||||
asm_x86_write_r32_disp(as, 6, src_r32, src_offset);
|
||||
}
|
||||
#endif
|
||||
|
||||
STATIC void asm_x86_pop_r32(asm_x86_t *as, int dest_r32) {
|
||||
asm_x86_write_byte_1(as, OPCODE_POP_R32 | dest_r32);
|
||||
}
|
||||
|
||||
STATIC void asm_x86_ret(asm_x86_t *as) {
|
||||
asm_x86_write_byte_1(as, OPCODE_RET);
|
||||
}
|
||||
|
||||
void asm_x86_mov_r32_r32(asm_x86_t *as, int dest_r32, int src_r32) {
|
||||
asm_x86_generic_r32_r32(as, dest_r32, src_r32, OPCODE_MOV_R32_TO_RM32);
|
||||
}
|
||||
|
||||
void asm_x86_mov_r8_to_mem8(asm_x86_t *as, int src_r32, int dest_r32, int dest_disp) {
|
||||
asm_x86_write_byte_1(as, OPCODE_MOV_R8_TO_RM8);
|
||||
asm_x86_write_r32_disp(as, src_r32, dest_r32, dest_disp);
|
||||
}
|
||||
|
||||
void asm_x86_mov_r16_to_mem16(asm_x86_t *as, int src_r32, int dest_r32, int dest_disp) {
|
||||
asm_x86_write_byte_2(as, OP_SIZE_PREFIX, OPCODE_MOV_R32_TO_RM32);
|
||||
asm_x86_write_r32_disp(as, src_r32, dest_r32, dest_disp);
|
||||
}
|
||||
|
||||
void asm_x86_mov_r32_to_mem32(asm_x86_t *as, int src_r32, int dest_r32, int dest_disp) {
|
||||
asm_x86_write_byte_1(as, OPCODE_MOV_R32_TO_RM32);
|
||||
asm_x86_write_r32_disp(as, src_r32, dest_r32, dest_disp);
|
||||
}
|
||||
|
||||
void asm_x86_mov_mem8_to_r32zx(asm_x86_t *as, int src_r32, int src_disp, int dest_r32) {
|
||||
asm_x86_write_byte_2(as, 0x0f, OPCODE_MOVZX_RM8_TO_R32);
|
||||
asm_x86_write_r32_disp(as, dest_r32, src_r32, src_disp);
|
||||
}
|
||||
|
||||
void asm_x86_mov_mem16_to_r32zx(asm_x86_t *as, int src_r32, int src_disp, int dest_r32) {
|
||||
asm_x86_write_byte_2(as, 0x0f, OPCODE_MOVZX_RM16_TO_R32);
|
||||
asm_x86_write_r32_disp(as, dest_r32, src_r32, src_disp);
|
||||
}
|
||||
|
||||
void asm_x86_mov_mem32_to_r32(asm_x86_t *as, int src_r32, int src_disp, int dest_r32) {
|
||||
asm_x86_write_byte_1(as, OPCODE_MOV_RM32_TO_R32);
|
||||
asm_x86_write_r32_disp(as, dest_r32, src_r32, src_disp);
|
||||
}
|
||||
|
||||
STATIC void asm_x86_lea_disp_to_r32(asm_x86_t *as, int src_r32, int src_disp, int dest_r32) {
|
||||
asm_x86_write_byte_1(as, OPCODE_LEA_MEM_TO_R32);
|
||||
asm_x86_write_r32_disp(as, dest_r32, src_r32, src_disp);
|
||||
}
|
||||
|
||||
#if 0
|
||||
void asm_x86_mov_i8_to_r8(asm_x86_t *as, int src_i8, int dest_r32) {
|
||||
asm_x86_write_byte_2(as, OPCODE_MOV_I8_TO_R8 | dest_r32, src_i8);
|
||||
}
|
||||
#endif
|
||||
|
||||
size_t asm_x86_mov_i32_to_r32(asm_x86_t *as, int32_t src_i32, int dest_r32) {
|
||||
asm_x86_write_byte_1(as, OPCODE_MOV_I32_TO_R32 | dest_r32);
|
||||
size_t loc = mp_asm_base_get_code_pos(&as->base);
|
||||
asm_x86_write_word32(as, src_i32);
|
||||
return loc;
|
||||
}
|
||||
|
||||
void asm_x86_and_r32_r32(asm_x86_t *as, int dest_r32, int src_r32) {
|
||||
asm_x86_generic_r32_r32(as, dest_r32, src_r32, OPCODE_AND_R32_TO_RM32);
|
||||
}
|
||||
|
||||
void asm_x86_or_r32_r32(asm_x86_t *as, int dest_r32, int src_r32) {
|
||||
asm_x86_generic_r32_r32(as, dest_r32, src_r32, OPCODE_OR_R32_TO_RM32);
|
||||
}
|
||||
|
||||
void asm_x86_xor_r32_r32(asm_x86_t *as, int dest_r32, int src_r32) {
|
||||
asm_x86_generic_r32_r32(as, dest_r32, src_r32, OPCODE_XOR_R32_TO_RM32);
|
||||
}
|
||||
|
||||
void asm_x86_shl_r32_cl(asm_x86_t *as, int dest_r32) {
|
||||
asm_x86_generic_r32_r32(as, dest_r32, 4, OPCODE_SHL_RM32_CL);
|
||||
}
|
||||
|
||||
void asm_x86_shr_r32_cl(asm_x86_t *as, int dest_r32) {
|
||||
asm_x86_generic_r32_r32(as, dest_r32, 5, OPCODE_SHR_RM32_CL);
|
||||
}
|
||||
|
||||
void asm_x86_sar_r32_cl(asm_x86_t *as, int dest_r32) {
|
||||
asm_x86_generic_r32_r32(as, dest_r32, 7, OPCODE_SAR_RM32_CL);
|
||||
}
|
||||
|
||||
void asm_x86_add_r32_r32(asm_x86_t *as, int dest_r32, int src_r32) {
|
||||
asm_x86_generic_r32_r32(as, dest_r32, src_r32, OPCODE_ADD_R32_TO_RM32);
|
||||
}
|
||||
|
||||
STATIC void asm_x86_add_i32_to_r32(asm_x86_t *as, int src_i32, int dest_r32) {
|
||||
if (SIGNED_FIT8(src_i32)) {
|
||||
asm_x86_write_byte_2(as, OPCODE_ADD_I8_TO_RM32, MODRM_R32(0) | MODRM_RM_REG | MODRM_RM_R32(dest_r32));
|
||||
asm_x86_write_byte_1(as, src_i32 & 0xff);
|
||||
} else {
|
||||
asm_x86_write_byte_2(as, OPCODE_ADD_I32_TO_RM32, MODRM_R32(0) | MODRM_RM_REG | MODRM_RM_R32(dest_r32));
|
||||
asm_x86_write_word32(as, src_i32);
|
||||
}
|
||||
}
|
||||
|
||||
void asm_x86_sub_r32_r32(asm_x86_t *as, int dest_r32, int src_r32) {
|
||||
asm_x86_generic_r32_r32(as, dest_r32, src_r32, OPCODE_SUB_R32_FROM_RM32);
|
||||
}
|
||||
|
||||
STATIC void asm_x86_sub_r32_i32(asm_x86_t *as, int dest_r32, int src_i32) {
|
||||
if (SIGNED_FIT8(src_i32)) {
|
||||
// defaults to 32 bit operation
|
||||
asm_x86_write_byte_2(as, OPCODE_SUB_I8_FROM_RM32, MODRM_R32(5) | MODRM_RM_REG | MODRM_RM_R32(dest_r32));
|
||||
asm_x86_write_byte_1(as, src_i32 & 0xff);
|
||||
} else {
|
||||
// defaults to 32 bit operation
|
||||
asm_x86_write_byte_2(as, OPCODE_SUB_I32_FROM_RM32, MODRM_R32(5) | MODRM_RM_REG | MODRM_RM_R32(dest_r32));
|
||||
asm_x86_write_word32(as, src_i32);
|
||||
}
|
||||
}
|
||||
|
||||
void asm_x86_mul_r32_r32(asm_x86_t *as, int dest_r32, int src_r32) {
|
||||
// imul reg32, reg/mem32 -- 0x0f 0xaf /r
|
||||
asm_x86_write_byte_3(as, 0x0f, 0xaf, MODRM_R32(dest_r32) | MODRM_RM_REG | MODRM_RM_R32(src_r32));
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* shifts not tested */
|
||||
void asm_x86_shl_r32_by_imm(asm_x86_t *as, int r32, int imm) {
|
||||
asm_x86_write_byte_2(as, OPCODE_SHL_RM32_BY_I8, MODRM_R32(4) | MODRM_RM_REG | MODRM_RM_R32(r32));
|
||||
asm_x86_write_byte_1(as, imm);
|
||||
}
|
||||
|
||||
void asm_x86_shr_r32_by_imm(asm_x86_t *as, int r32, int imm) {
|
||||
asm_x86_write_byte_2(as, OPCODE_SHR_RM32_BY_I8, MODRM_R32(5) | MODRM_RM_REG | MODRM_RM_R32(r32));
|
||||
asm_x86_write_byte_1(as, imm);
|
||||
}
|
||||
|
||||
void asm_x86_sar_r32_by_imm(asm_x86_t *as, int r32, int imm) {
|
||||
asm_x86_write_byte_2(as, OPCODE_SAR_RM32_BY_I8, MODRM_R32(7) | MODRM_RM_REG | MODRM_RM_R32(r32));
|
||||
asm_x86_write_byte_1(as, imm);
|
||||
}
|
||||
#endif
|
||||
|
||||
void asm_x86_cmp_r32_with_r32(asm_x86_t *as, int src_r32_a, int src_r32_b) {
|
||||
asm_x86_generic_r32_r32(as, src_r32_b, src_r32_a, OPCODE_CMP_R32_WITH_RM32);
|
||||
}
|
||||
|
||||
#if 0
|
||||
void asm_x86_cmp_i32_with_r32(asm_x86_t *as, int src_i32, int src_r32) {
|
||||
if (SIGNED_FIT8(src_i32)) {
|
||||
asm_x86_write_byte_2(as, OPCODE_CMP_I8_WITH_RM32, MODRM_R32(7) | MODRM_RM_REG | MODRM_RM_R32(src_r32));
|
||||
asm_x86_write_byte_1(as, src_i32 & 0xff);
|
||||
} else {
|
||||
asm_x86_write_byte_2(as, OPCODE_CMP_I32_WITH_RM32, MODRM_R32(7) | MODRM_RM_REG | MODRM_RM_R32(src_r32));
|
||||
asm_x86_write_word32(as, src_i32);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void asm_x86_test_r8_with_r8(asm_x86_t *as, int src_r32_a, int src_r32_b) {
|
||||
asm_x86_write_byte_2(as, OPCODE_TEST_R8_WITH_RM8, MODRM_R32(src_r32_a) | MODRM_RM_REG | MODRM_RM_R32(src_r32_b));
|
||||
}
|
||||
|
||||
void asm_x86_test_r32_with_r32(asm_x86_t *as, int src_r32_a, int src_r32_b) {
|
||||
asm_x86_generic_r32_r32(as, src_r32_b, src_r32_a, OPCODE_TEST_R32_WITH_RM32);
|
||||
}
|
||||
|
||||
void asm_x86_setcc_r8(asm_x86_t *as, mp_uint_t jcc_type, int dest_r8) {
|
||||
asm_x86_write_byte_3(as, OPCODE_SETCC_RM8_A, OPCODE_SETCC_RM8_B | jcc_type, MODRM_R32(0) | MODRM_RM_REG | MODRM_RM_R32(dest_r8));
|
||||
}
|
||||
|
||||
void asm_x86_jmp_reg(asm_x86_t *as, int src_r32) {
|
||||
asm_x86_write_byte_2(as, OPCODE_JMP_RM32, MODRM_R32(4) | MODRM_RM_REG | MODRM_RM_R32(src_r32));
|
||||
}
|
||||
|
||||
STATIC mp_uint_t get_label_dest(asm_x86_t *as, mp_uint_t label) {
|
||||
assert(label < as->base.max_num_labels);
|
||||
return as->base.label_offsets[label];
|
||||
}
|
||||
|
||||
void asm_x86_jmp_label(asm_x86_t *as, mp_uint_t label) {
|
||||
mp_uint_t dest = get_label_dest(as, label);
|
||||
mp_int_t rel = dest - as->base.code_offset;
|
||||
if (dest != (mp_uint_t)-1 && rel < 0) {
|
||||
// is a backwards jump, so we know the size of the jump on the first pass
|
||||
// calculate rel assuming 8 bit relative jump
|
||||
rel -= 2;
|
||||
if (SIGNED_FIT8(rel)) {
|
||||
asm_x86_write_byte_2(as, OPCODE_JMP_REL8, rel & 0xff);
|
||||
} else {
|
||||
rel += 2;
|
||||
goto large_jump;
|
||||
}
|
||||
} else {
|
||||
// is a forwards jump, so need to assume it's large
|
||||
large_jump:
|
||||
rel -= 5;
|
||||
asm_x86_write_byte_1(as, OPCODE_JMP_REL32);
|
||||
asm_x86_write_word32(as, rel);
|
||||
}
|
||||
}
|
||||
|
||||
void asm_x86_jcc_label(asm_x86_t *as, mp_uint_t jcc_type, mp_uint_t label) {
|
||||
mp_uint_t dest = get_label_dest(as, label);
|
||||
mp_int_t rel = dest - as->base.code_offset;
|
||||
if (dest != (mp_uint_t)-1 && rel < 0) {
|
||||
// is a backwards jump, so we know the size of the jump on the first pass
|
||||
// calculate rel assuming 8 bit relative jump
|
||||
rel -= 2;
|
||||
if (SIGNED_FIT8(rel)) {
|
||||
asm_x86_write_byte_2(as, OPCODE_JCC_REL8 | jcc_type, rel & 0xff);
|
||||
} else {
|
||||
rel += 2;
|
||||
goto large_jump;
|
||||
}
|
||||
} else {
|
||||
// is a forwards jump, so need to assume it's large
|
||||
large_jump:
|
||||
rel -= 6;
|
||||
asm_x86_write_byte_2(as, OPCODE_JCC_REL32_A, OPCODE_JCC_REL32_B | jcc_type);
|
||||
asm_x86_write_word32(as, rel);
|
||||
}
|
||||
}
|
||||
|
||||
void asm_x86_entry(asm_x86_t *as, int num_locals) {
|
||||
assert(num_locals >= 0);
|
||||
asm_x86_push_r32(as, ASM_X86_REG_EBP);
|
||||
asm_x86_push_r32(as, ASM_X86_REG_EBX);
|
||||
asm_x86_push_r32(as, ASM_X86_REG_ESI);
|
||||
asm_x86_push_r32(as, ASM_X86_REG_EDI);
|
||||
num_locals |= 3; // make it odd so stack is aligned on 16 byte boundary
|
||||
asm_x86_sub_r32_i32(as, ASM_X86_REG_ESP, num_locals * WORD_SIZE);
|
||||
as->num_locals = num_locals;
|
||||
}
|
||||
|
||||
void asm_x86_exit(asm_x86_t *as) {
|
||||
asm_x86_sub_r32_i32(as, ASM_X86_REG_ESP, -as->num_locals * WORD_SIZE);
|
||||
asm_x86_pop_r32(as, ASM_X86_REG_EDI);
|
||||
asm_x86_pop_r32(as, ASM_X86_REG_ESI);
|
||||
asm_x86_pop_r32(as, ASM_X86_REG_EBX);
|
||||
asm_x86_pop_r32(as, ASM_X86_REG_EBP);
|
||||
asm_x86_ret(as);
|
||||
}
|
||||
|
||||
STATIC int asm_x86_arg_offset_from_esp(asm_x86_t *as, size_t arg_num) {
|
||||
// Above esp are: locals, 4 saved registers, return eip, arguments
|
||||
return (as->num_locals + 4 + 1 + arg_num) * WORD_SIZE;
|
||||
}
|
||||
|
||||
#if 0
|
||||
void asm_x86_push_arg(asm_x86_t *as, int src_arg_num) {
|
||||
asm_x86_push_disp(as, ASM_X86_REG_ESP, asm_x86_arg_offset_from_esp(as, src_arg_num));
|
||||
}
|
||||
#endif
|
||||
|
||||
void asm_x86_mov_arg_to_r32(asm_x86_t *as, int src_arg_num, int dest_r32) {
|
||||
asm_x86_mov_mem32_to_r32(as, ASM_X86_REG_ESP, asm_x86_arg_offset_from_esp(as, src_arg_num), dest_r32);
|
||||
}
|
||||
|
||||
#if 0
|
||||
void asm_x86_mov_r32_to_arg(asm_x86_t *as, int src_r32, int dest_arg_num) {
|
||||
asm_x86_mov_r32_to_mem32(as, src_r32, ASM_X86_REG_ESP, asm_x86_arg_offset_from_esp(as, dest_arg_num));
|
||||
}
|
||||
#endif
|
||||
|
||||
// locals:
|
||||
// - stored on the stack in ascending order
|
||||
// - numbered 0 through as->num_locals-1
|
||||
// - ESP points to the first local
|
||||
//
|
||||
// | ESP
|
||||
// v
|
||||
// l0 l1 l2 ... l(n-1)
|
||||
// ^ ^
|
||||
// | low address | high address in RAM
|
||||
//
|
||||
STATIC int asm_x86_local_offset_from_esp(asm_x86_t *as, int local_num) {
|
||||
(void)as;
|
||||
// Stack is full descending, ESP points to local0
|
||||
return local_num * WORD_SIZE;
|
||||
}
|
||||
|
||||
void asm_x86_mov_local_to_r32(asm_x86_t *as, int src_local_num, int dest_r32) {
|
||||
asm_x86_mov_mem32_to_r32(as, ASM_X86_REG_ESP, asm_x86_local_offset_from_esp(as, src_local_num), dest_r32);
|
||||
}
|
||||
|
||||
void asm_x86_mov_r32_to_local(asm_x86_t *as, int src_r32, int dest_local_num) {
|
||||
asm_x86_mov_r32_to_mem32(as, src_r32, ASM_X86_REG_ESP, asm_x86_local_offset_from_esp(as, dest_local_num));
|
||||
}
|
||||
|
||||
void asm_x86_mov_local_addr_to_r32(asm_x86_t *as, int local_num, int dest_r32) {
|
||||
int offset = asm_x86_local_offset_from_esp(as, local_num);
|
||||
if (offset == 0) {
|
||||
asm_x86_mov_r32_r32(as, dest_r32, ASM_X86_REG_ESP);
|
||||
} else {
|
||||
asm_x86_lea_disp_to_r32(as, ASM_X86_REG_ESP, offset, dest_r32);
|
||||
}
|
||||
}
|
||||
|
||||
void asm_x86_mov_reg_pcrel(asm_x86_t *as, int dest_r32, mp_uint_t label) {
|
||||
asm_x86_write_byte_1(as, OPCODE_CALL_REL32);
|
||||
asm_x86_write_word32(as, 0);
|
||||
mp_uint_t dest = get_label_dest(as, label);
|
||||
mp_int_t rel = dest - as->base.code_offset;
|
||||
asm_x86_pop_r32(as, dest_r32);
|
||||
// PC rel is usually a forward reference, so need to assume it's large
|
||||
asm_x86_write_byte_2(as, OPCODE_ADD_I32_TO_RM32, MODRM_R32(0) | MODRM_RM_REG | MODRM_RM_R32(dest_r32));
|
||||
asm_x86_write_word32(as, rel);
|
||||
}
|
||||
|
||||
#if 0
|
||||
void asm_x86_push_local(asm_x86_t *as, int local_num) {
|
||||
asm_x86_push_disp(as, ASM_X86_REG_ESP, asm_x86_local_offset_from_esp(as, local_num));
|
||||
}
|
||||
|
||||
void asm_x86_push_local_addr(asm_x86_t *as, int local_num, int temp_r32) {
|
||||
asm_x86_mov_r32_r32(as, temp_r32, ASM_X86_REG_ESP);
|
||||
asm_x86_add_i32_to_r32(as, asm_x86_local_offset_from_esp(as, local_num), temp_r32);
|
||||
asm_x86_push_r32(as, temp_r32);
|
||||
}
|
||||
#endif
|
||||
|
||||
void asm_x86_call_ind(asm_x86_t *as, size_t fun_id, mp_uint_t n_args, int temp_r32) {
|
||||
assert(n_args <= 4);
|
||||
|
||||
// Align stack on 16-byte boundary during the call
|
||||
unsigned int align = ((n_args + 3) & ~3) - n_args;
|
||||
if (align) {
|
||||
asm_x86_sub_r32_i32(as, ASM_X86_REG_ESP, align * WORD_SIZE);
|
||||
}
|
||||
|
||||
if (n_args > 3) {
|
||||
asm_x86_push_r32(as, ASM_X86_REG_ARG_4);
|
||||
}
|
||||
if (n_args > 2) {
|
||||
asm_x86_push_r32(as, ASM_X86_REG_ARG_3);
|
||||
}
|
||||
if (n_args > 1) {
|
||||
asm_x86_push_r32(as, ASM_X86_REG_ARG_2);
|
||||
}
|
||||
if (n_args > 0) {
|
||||
asm_x86_push_r32(as, ASM_X86_REG_ARG_1);
|
||||
}
|
||||
|
||||
// Load the pointer to the function and make the call
|
||||
asm_x86_mov_mem32_to_r32(as, ASM_X86_REG_FUN_TABLE, fun_id * WORD_SIZE, temp_r32);
|
||||
asm_x86_write_byte_2(as, OPCODE_CALL_RM32, MODRM_R32(2) | MODRM_RM_REG | MODRM_RM_R32(temp_r32));
|
||||
|
||||
// the caller must clean up the stack
|
||||
if (n_args > 0) {
|
||||
asm_x86_add_i32_to_r32(as, (n_args + align) * WORD_SIZE, ASM_X86_REG_ESP);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // MICROPY_EMIT_X86
|
||||
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef MICROPY_INCLUDED_PY_ASMX86_H
|
||||
#define MICROPY_INCLUDED_PY_ASMX86_H
|
||||
|
||||
#include "py/mpconfig.h"
|
||||
#include "py/misc.h"
|
||||
#include "py/asmbase.h"
|
||||
|
||||
// x86 cdecl calling convention is:
|
||||
// - args passed on the stack in reverse order
|
||||
// - return value in EAX
|
||||
// - caller cleans up the stack after a call
|
||||
// - stack must be aligned to 16-byte boundary before all calls
|
||||
// - EAX, ECX, EDX are caller-save
|
||||
// - EBX, ESI, EDI, EBP, ESP, EIP are callee-save
|
||||
|
||||
// In the functions below, argument order follows x86 docs and generally
|
||||
// the destination is the first argument.
|
||||
// NOTE: this is a change from the old convention used in this file and
|
||||
// some functions still use the old (reverse) convention.
|
||||
|
||||
#define ASM_X86_REG_EAX (0)
|
||||
#define ASM_X86_REG_ECX (1)
|
||||
#define ASM_X86_REG_EDX (2)
|
||||
#define ASM_X86_REG_EBX (3)
|
||||
#define ASM_X86_REG_ESP (4)
|
||||
#define ASM_X86_REG_EBP (5)
|
||||
#define ASM_X86_REG_ESI (6)
|
||||
#define ASM_X86_REG_EDI (7)
|
||||
|
||||
// x86 passes values on the stack, but the emitter is register based, so we need
|
||||
// to define registers that can temporarily hold the function arguments. They
|
||||
// need to be defined here so that asm_x86_call_ind can push them onto the stack
|
||||
// before the call.
|
||||
#define ASM_X86_REG_ARG_1 ASM_X86_REG_EAX
|
||||
#define ASM_X86_REG_ARG_2 ASM_X86_REG_ECX
|
||||
#define ASM_X86_REG_ARG_3 ASM_X86_REG_EDX
|
||||
#define ASM_X86_REG_ARG_4 ASM_X86_REG_EBX
|
||||
|
||||
// condition codes, used for jcc and setcc (despite their j-name!)
|
||||
#define ASM_X86_CC_JB (0x2) // below, unsigned
|
||||
#define ASM_X86_CC_JAE (0x3) // above or equal, unsigned
|
||||
#define ASM_X86_CC_JZ (0x4)
|
||||
#define ASM_X86_CC_JE (0x4)
|
||||
#define ASM_X86_CC_JNZ (0x5)
|
||||
#define ASM_X86_CC_JNE (0x5)
|
||||
#define ASM_X86_CC_JBE (0x6) // below or equal, unsigned
|
||||
#define ASM_X86_CC_JA (0x7) // above, unsigned
|
||||
#define ASM_X86_CC_JL (0xc) // less, signed
|
||||
#define ASM_X86_CC_JGE (0xd) // greater or equal, signed
|
||||
#define ASM_X86_CC_JLE (0xe) // less or equal, signed
|
||||
#define ASM_X86_CC_JG (0xf) // greater, signed
|
||||
|
||||
typedef struct _asm_x86_t {
|
||||
mp_asm_base_t base;
|
||||
int num_locals;
|
||||
} asm_x86_t;
|
||||
|
||||
static inline void asm_x86_end_pass(asm_x86_t *as) {
|
||||
(void)as;
|
||||
}
|
||||
|
||||
void asm_x86_mov_r32_r32(asm_x86_t *as, int dest_r32, int src_r32);
|
||||
size_t asm_x86_mov_i32_to_r32(asm_x86_t *as, int32_t src_i32, int dest_r32);
|
||||
void asm_x86_mov_r8_to_mem8(asm_x86_t *as, int src_r32, int dest_r32, int dest_disp);
|
||||
void asm_x86_mov_r16_to_mem16(asm_x86_t *as, int src_r32, int dest_r32, int dest_disp);
|
||||
void asm_x86_mov_r32_to_mem32(asm_x86_t *as, int src_r32, int dest_r32, int dest_disp);
|
||||
void asm_x86_mov_mem8_to_r32zx(asm_x86_t *as, int src_r32, int src_disp, int dest_r32);
|
||||
void asm_x86_mov_mem16_to_r32zx(asm_x86_t *as, int src_r32, int src_disp, int dest_r32);
|
||||
void asm_x86_mov_mem32_to_r32(asm_x86_t *as, int src_r32, int src_disp, int dest_r32);
|
||||
void asm_x86_and_r32_r32(asm_x86_t *as, int dest_r32, int src_r32);
|
||||
void asm_x86_or_r32_r32(asm_x86_t *as, int dest_r32, int src_r32);
|
||||
void asm_x86_xor_r32_r32(asm_x86_t *as, int dest_r32, int src_r32);
|
||||
void asm_x86_shl_r32_cl(asm_x86_t *as, int dest_r32);
|
||||
void asm_x86_shr_r32_cl(asm_x86_t *as, int dest_r32);
|
||||
void asm_x86_sar_r32_cl(asm_x86_t *as, int dest_r32);
|
||||
void asm_x86_add_r32_r32(asm_x86_t *as, int dest_r32, int src_r32);
|
||||
void asm_x86_sub_r32_r32(asm_x86_t *as, int dest_r32, int src_r32);
|
||||
void asm_x86_mul_r32_r32(asm_x86_t *as, int dest_r32, int src_r32);
|
||||
void asm_x86_cmp_r32_with_r32(asm_x86_t *as, int src_r32_a, int src_r32_b);
|
||||
void asm_x86_test_r8_with_r8(asm_x86_t *as, int src_r32_a, int src_r32_b);
|
||||
void asm_x86_test_r32_with_r32(asm_x86_t *as, int src_r32_a, int src_r32_b);
|
||||
void asm_x86_setcc_r8(asm_x86_t *as, mp_uint_t jcc_type, int dest_r8);
|
||||
void asm_x86_jmp_reg(asm_x86_t *as, int src_r86);
|
||||
void asm_x86_jmp_label(asm_x86_t *as, mp_uint_t label);
|
||||
void asm_x86_jcc_label(asm_x86_t *as, mp_uint_t jcc_type, mp_uint_t label);
|
||||
void asm_x86_entry(asm_x86_t *as, int num_locals);
|
||||
void asm_x86_exit(asm_x86_t *as);
|
||||
void asm_x86_mov_arg_to_r32(asm_x86_t *as, int src_arg_num, int dest_r32);
|
||||
void asm_x86_mov_local_to_r32(asm_x86_t *as, int src_local_num, int dest_r32);
|
||||
void asm_x86_mov_r32_to_local(asm_x86_t *as, int src_r32, int dest_local_num);
|
||||
void asm_x86_mov_local_addr_to_r32(asm_x86_t *as, int local_num, int dest_r32);
|
||||
void asm_x86_mov_reg_pcrel(asm_x86_t *as, int dest_r64, mp_uint_t label);
|
||||
void asm_x86_call_ind(asm_x86_t *as, size_t fun_id, mp_uint_t n_args, int temp_r32);
|
||||
|
||||
// Holds a pointer to mp_fun_table
|
||||
#define ASM_X86_REG_FUN_TABLE ASM_X86_REG_EBP
|
||||
|
||||
#if GENERIC_ASM_API
|
||||
|
||||
// The following macros provide a (mostly) arch-independent API to
|
||||
// generate native code, and are used by the native emitter.
|
||||
|
||||
#define ASM_WORD_SIZE (4)
|
||||
|
||||
#define REG_RET ASM_X86_REG_EAX
|
||||
#define REG_ARG_1 ASM_X86_REG_ARG_1
|
||||
#define REG_ARG_2 ASM_X86_REG_ARG_2
|
||||
#define REG_ARG_3 ASM_X86_REG_ARG_3
|
||||
#define REG_ARG_4 ASM_X86_REG_ARG_4
|
||||
|
||||
// caller-save, so can be used as temporaries
|
||||
#define REG_TEMP0 ASM_X86_REG_EAX
|
||||
#define REG_TEMP1 ASM_X86_REG_ECX
|
||||
#define REG_TEMP2 ASM_X86_REG_EDX
|
||||
|
||||
// callee-save, so can be used as locals
|
||||
#define REG_LOCAL_1 ASM_X86_REG_EBX
|
||||
#define REG_LOCAL_2 ASM_X86_REG_ESI
|
||||
#define REG_LOCAL_3 ASM_X86_REG_EDI
|
||||
#define REG_LOCAL_NUM (3)
|
||||
|
||||
// Holds a pointer to mp_fun_table
|
||||
#define REG_FUN_TABLE ASM_X86_REG_FUN_TABLE
|
||||
|
||||
#define ASM_T asm_x86_t
|
||||
#define ASM_END_PASS asm_x86_end_pass
|
||||
#define ASM_ENTRY asm_x86_entry
|
||||
#define ASM_EXIT asm_x86_exit
|
||||
|
||||
#define ASM_JUMP asm_x86_jmp_label
|
||||
#define ASM_JUMP_IF_REG_ZERO(as, reg, label, bool_test) \
|
||||
do { \
|
||||
if (bool_test) { \
|
||||
asm_x86_test_r8_with_r8(as, reg, reg); \
|
||||
} else { \
|
||||
asm_x86_test_r32_with_r32(as, reg, reg); \
|
||||
} \
|
||||
asm_x86_jcc_label(as, ASM_X86_CC_JZ, label); \
|
||||
} while (0)
|
||||
#define ASM_JUMP_IF_REG_NONZERO(as, reg, label, bool_test) \
|
||||
do { \
|
||||
if (bool_test) { \
|
||||
asm_x86_test_r8_with_r8(as, reg, reg); \
|
||||
} else { \
|
||||
asm_x86_test_r32_with_r32(as, reg, reg); \
|
||||
} \
|
||||
asm_x86_jcc_label(as, ASM_X86_CC_JNZ, label); \
|
||||
} while (0)
|
||||
#define ASM_JUMP_IF_REG_EQ(as, reg1, reg2, label) \
|
||||
do { \
|
||||
asm_x86_cmp_r32_with_r32(as, reg1, reg2); \
|
||||
asm_x86_jcc_label(as, ASM_X86_CC_JE, label); \
|
||||
} while (0)
|
||||
#define ASM_JUMP_REG(as, reg) asm_x86_jmp_reg((as), (reg))
|
||||
#define ASM_CALL_IND(as, idx) asm_x86_call_ind(as, idx, mp_f_n_args[idx], ASM_X86_REG_EAX)
|
||||
|
||||
#define ASM_MOV_LOCAL_REG(as, local_num, reg_src) asm_x86_mov_r32_to_local((as), (reg_src), (local_num))
|
||||
#define ASM_MOV_REG_IMM(as, reg_dest, imm) asm_x86_mov_i32_to_r32((as), (imm), (reg_dest))
|
||||
#define ASM_MOV_REG_IMM_FIX_U16(as, reg_dest, imm) asm_x86_mov_i32_to_r32((as), (imm), (reg_dest))
|
||||
#define ASM_MOV_REG_IMM_FIX_WORD(as, reg_dest, imm) asm_x86_mov_i32_to_r32((as), (imm), (reg_dest))
|
||||
#define ASM_MOV_REG_LOCAL(as, reg_dest, local_num) asm_x86_mov_local_to_r32((as), (local_num), (reg_dest))
|
||||
#define ASM_MOV_REG_REG(as, reg_dest, reg_src) asm_x86_mov_r32_r32((as), (reg_dest), (reg_src))
|
||||
#define ASM_MOV_REG_LOCAL_ADDR(as, reg_dest, local_num) asm_x86_mov_local_addr_to_r32((as), (local_num), (reg_dest))
|
||||
#define ASM_MOV_REG_PCREL(as, reg_dest, label) asm_x86_mov_reg_pcrel((as), (reg_dest), (label))
|
||||
|
||||
#define ASM_LSL_REG(as, reg) asm_x86_shl_r32_cl((as), (reg))
|
||||
#define ASM_LSR_REG(as, reg) asm_x86_shr_r32_cl((as), (reg))
|
||||
#define ASM_ASR_REG(as, reg) asm_x86_sar_r32_cl((as), (reg))
|
||||
#define ASM_OR_REG_REG(as, reg_dest, reg_src) asm_x86_or_r32_r32((as), (reg_dest), (reg_src))
|
||||
#define ASM_XOR_REG_REG(as, reg_dest, reg_src) asm_x86_xor_r32_r32((as), (reg_dest), (reg_src))
|
||||
#define ASM_AND_REG_REG(as, reg_dest, reg_src) asm_x86_and_r32_r32((as), (reg_dest), (reg_src))
|
||||
#define ASM_ADD_REG_REG(as, reg_dest, reg_src) asm_x86_add_r32_r32((as), (reg_dest), (reg_src))
|
||||
#define ASM_SUB_REG_REG(as, reg_dest, reg_src) asm_x86_sub_r32_r32((as), (reg_dest), (reg_src))
|
||||
#define ASM_MUL_REG_REG(as, reg_dest, reg_src) asm_x86_mul_r32_r32((as), (reg_dest), (reg_src))
|
||||
|
||||
#define ASM_LOAD_REG_REG(as, reg_dest, reg_base) asm_x86_mov_mem32_to_r32((as), (reg_base), 0, (reg_dest))
|
||||
#define ASM_LOAD_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_x86_mov_mem32_to_r32((as), (reg_base), 4 * (word_offset), (reg_dest))
|
||||
#define ASM_LOAD8_REG_REG(as, reg_dest, reg_base) asm_x86_mov_mem8_to_r32zx((as), (reg_base), 0, (reg_dest))
|
||||
#define ASM_LOAD16_REG_REG(as, reg_dest, reg_base) asm_x86_mov_mem16_to_r32zx((as), (reg_base), 0, (reg_dest))
|
||||
#define ASM_LOAD32_REG_REG(as, reg_dest, reg_base) asm_x86_mov_mem32_to_r32((as), (reg_base), 0, (reg_dest))
|
||||
|
||||
#define ASM_STORE_REG_REG(as, reg_src, reg_base) asm_x86_mov_r32_to_mem32((as), (reg_src), (reg_base), 0)
|
||||
#define ASM_STORE_REG_REG_OFFSET(as, reg_src, reg_base, word_offset) asm_x86_mov_r32_to_mem32((as), (reg_src), (reg_base), 4 * (word_offset))
|
||||
#define ASM_STORE8_REG_REG(as, reg_src, reg_base) asm_x86_mov_r8_to_mem8((as), (reg_src), (reg_base), 0)
|
||||
#define ASM_STORE16_REG_REG(as, reg_src, reg_base) asm_x86_mov_r16_to_mem16((as), (reg_src), (reg_base), 0)
|
||||
#define ASM_STORE32_REG_REG(as, reg_src, reg_base) asm_x86_mov_r32_to_mem32((as), (reg_src), (reg_base), 0)
|
||||
|
||||
#endif // GENERIC_ASM_API
|
||||
|
||||
#endif // MICROPY_INCLUDED_PY_ASMX86_H
|
||||
@@ -0,0 +1,253 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2016 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "py/mpconfig.h"
|
||||
|
||||
// wrapper around everything in this file
|
||||
#if MICROPY_EMIT_XTENSA || MICROPY_EMIT_INLINE_XTENSA || MICROPY_EMIT_XTENSAWIN
|
||||
|
||||
#include "py/asmxtensa.h"
|
||||
|
||||
#define WORD_SIZE (4)
|
||||
#define SIGNED_FIT8(x) ((((x) & 0xffffff80) == 0) || (((x) & 0xffffff80) == 0xffffff80))
|
||||
#define SIGNED_FIT12(x) ((((x) & 0xfffff800) == 0) || (((x) & 0xfffff800) == 0xfffff800))
|
||||
|
||||
void asm_xtensa_end_pass(asm_xtensa_t *as) {
|
||||
as->num_const = as->cur_const;
|
||||
as->cur_const = 0;
|
||||
|
||||
#if 0
|
||||
// make a hex dump of the machine code
|
||||
if (as->base.pass == MP_ASM_PASS_EMIT) {
|
||||
uint8_t *d = as->base.code_base;
|
||||
printf("XTENSA ASM:");
|
||||
for (int i = 0; i < ((as->base.code_size + 15) & ~15); ++i) {
|
||||
if (i % 16 == 0) {
|
||||
printf("\n%08x:", (uint32_t)&d[i]);
|
||||
}
|
||||
if (i % 2 == 0) {
|
||||
printf(" ");
|
||||
}
|
||||
printf("%02x", d[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void asm_xtensa_entry(asm_xtensa_t *as, int num_locals) {
|
||||
// jump over the constants
|
||||
asm_xtensa_op_j(as, as->num_const * WORD_SIZE + 4 - 4);
|
||||
mp_asm_base_get_cur_to_write_bytes(&as->base, 1); // padding/alignment byte
|
||||
as->const_table = (uint32_t *)mp_asm_base_get_cur_to_write_bytes(&as->base, as->num_const * 4);
|
||||
|
||||
// adjust the stack-pointer to store a0, a12, a13, a14, a15 and locals, 16-byte aligned
|
||||
as->stack_adjust = (((ASM_XTENSA_NUM_REGS_SAVED + num_locals) * WORD_SIZE) + 15) & ~15;
|
||||
if (SIGNED_FIT8(-as->stack_adjust)) {
|
||||
asm_xtensa_op_addi(as, ASM_XTENSA_REG_A1, ASM_XTENSA_REG_A1, -as->stack_adjust);
|
||||
} else {
|
||||
asm_xtensa_op_movi(as, ASM_XTENSA_REG_A9, as->stack_adjust);
|
||||
asm_xtensa_op_sub(as, ASM_XTENSA_REG_A1, ASM_XTENSA_REG_A1, ASM_XTENSA_REG_A9);
|
||||
}
|
||||
|
||||
// save return value (a0) and callee-save registers (a12, a13, a14, a15)
|
||||
asm_xtensa_op_s32i_n(as, ASM_XTENSA_REG_A0, ASM_XTENSA_REG_A1, 0);
|
||||
for (int i = 1; i < ASM_XTENSA_NUM_REGS_SAVED; ++i) {
|
||||
asm_xtensa_op_s32i_n(as, ASM_XTENSA_REG_A11 + i, ASM_XTENSA_REG_A1, i);
|
||||
}
|
||||
}
|
||||
|
||||
void asm_xtensa_exit(asm_xtensa_t *as) {
|
||||
// restore registers
|
||||
for (int i = ASM_XTENSA_NUM_REGS_SAVED - 1; i >= 1; --i) {
|
||||
asm_xtensa_op_l32i_n(as, ASM_XTENSA_REG_A11 + i, ASM_XTENSA_REG_A1, i);
|
||||
}
|
||||
asm_xtensa_op_l32i_n(as, ASM_XTENSA_REG_A0, ASM_XTENSA_REG_A1, 0);
|
||||
|
||||
// restore stack-pointer and return
|
||||
if (SIGNED_FIT8(as->stack_adjust)) {
|
||||
asm_xtensa_op_addi(as, ASM_XTENSA_REG_A1, ASM_XTENSA_REG_A1, as->stack_adjust);
|
||||
} else {
|
||||
asm_xtensa_op_movi(as, ASM_XTENSA_REG_A9, as->stack_adjust);
|
||||
asm_xtensa_op_add_n(as, ASM_XTENSA_REG_A1, ASM_XTENSA_REG_A1, ASM_XTENSA_REG_A9);
|
||||
}
|
||||
|
||||
asm_xtensa_op_ret_n(as);
|
||||
}
|
||||
|
||||
void asm_xtensa_entry_win(asm_xtensa_t *as, int num_locals) {
|
||||
// jump over the constants
|
||||
asm_xtensa_op_j(as, as->num_const * WORD_SIZE + 4 - 4);
|
||||
mp_asm_base_get_cur_to_write_bytes(&as->base, 1); // padding/alignment byte
|
||||
as->const_table = (uint32_t *)mp_asm_base_get_cur_to_write_bytes(&as->base, as->num_const * 4);
|
||||
|
||||
as->stack_adjust = 32 + ((((ASM_XTENSA_NUM_REGS_SAVED_WIN + num_locals) * WORD_SIZE) + 15) & ~15);
|
||||
asm_xtensa_op_entry(as, ASM_XTENSA_REG_A1, as->stack_adjust);
|
||||
asm_xtensa_op_s32i_n(as, ASM_XTENSA_REG_A0, ASM_XTENSA_REG_A1, 0);
|
||||
}
|
||||
|
||||
void asm_xtensa_exit_win(asm_xtensa_t *as) {
|
||||
asm_xtensa_op_l32i_n(as, ASM_XTENSA_REG_A0, ASM_XTENSA_REG_A1, 0);
|
||||
asm_xtensa_op_retw_n(as);
|
||||
}
|
||||
|
||||
STATIC uint32_t get_label_dest(asm_xtensa_t *as, uint label) {
|
||||
assert(label < as->base.max_num_labels);
|
||||
return as->base.label_offsets[label];
|
||||
}
|
||||
|
||||
void asm_xtensa_op16(asm_xtensa_t *as, uint16_t op) {
|
||||
uint8_t *c = mp_asm_base_get_cur_to_write_bytes(&as->base, 2);
|
||||
if (c != NULL) {
|
||||
c[0] = op;
|
||||
c[1] = op >> 8;
|
||||
}
|
||||
}
|
||||
|
||||
void asm_xtensa_op24(asm_xtensa_t *as, uint32_t op) {
|
||||
uint8_t *c = mp_asm_base_get_cur_to_write_bytes(&as->base, 3);
|
||||
if (c != NULL) {
|
||||
c[0] = op;
|
||||
c[1] = op >> 8;
|
||||
c[2] = op >> 16;
|
||||
}
|
||||
}
|
||||
|
||||
void asm_xtensa_j_label(asm_xtensa_t *as, uint label) {
|
||||
uint32_t dest = get_label_dest(as, label);
|
||||
int32_t rel = dest - as->base.code_offset - 4;
|
||||
// we assume rel, as a signed int, fits in 18-bits
|
||||
asm_xtensa_op_j(as, rel);
|
||||
}
|
||||
|
||||
void asm_xtensa_bccz_reg_label(asm_xtensa_t *as, uint cond, uint reg, uint label) {
|
||||
uint32_t dest = get_label_dest(as, label);
|
||||
int32_t rel = dest - as->base.code_offset - 4;
|
||||
if (as->base.pass == MP_ASM_PASS_EMIT && !SIGNED_FIT12(rel)) {
|
||||
printf("ERROR: xtensa bccz out of range\n");
|
||||
}
|
||||
asm_xtensa_op_bccz(as, cond, reg, rel);
|
||||
}
|
||||
|
||||
void asm_xtensa_bcc_reg_reg_label(asm_xtensa_t *as, uint cond, uint reg1, uint reg2, uint label) {
|
||||
uint32_t dest = get_label_dest(as, label);
|
||||
int32_t rel = dest - as->base.code_offset - 4;
|
||||
if (as->base.pass == MP_ASM_PASS_EMIT && !SIGNED_FIT8(rel)) {
|
||||
printf("ERROR: xtensa bcc out of range\n");
|
||||
}
|
||||
asm_xtensa_op_bcc(as, cond, reg1, reg2, rel);
|
||||
}
|
||||
|
||||
// convenience function; reg_dest must be different from reg_src[12]
|
||||
void asm_xtensa_setcc_reg_reg_reg(asm_xtensa_t *as, uint cond, uint reg_dest, uint reg_src1, uint reg_src2) {
|
||||
asm_xtensa_op_movi_n(as, reg_dest, 1);
|
||||
asm_xtensa_op_bcc(as, cond, reg_src1, reg_src2, 1);
|
||||
asm_xtensa_op_movi_n(as, reg_dest, 0);
|
||||
}
|
||||
|
||||
size_t asm_xtensa_mov_reg_i32(asm_xtensa_t *as, uint reg_dest, uint32_t i32) {
|
||||
// load the constant
|
||||
uint32_t const_table_offset = (uint8_t *)as->const_table - as->base.code_base;
|
||||
size_t loc = const_table_offset + as->cur_const * WORD_SIZE;
|
||||
asm_xtensa_op_l32r(as, reg_dest, as->base.code_offset, loc);
|
||||
// store the constant in the table
|
||||
if (as->const_table != NULL) {
|
||||
as->const_table[as->cur_const] = i32;
|
||||
}
|
||||
++as->cur_const;
|
||||
return loc;
|
||||
}
|
||||
|
||||
void asm_xtensa_mov_reg_i32_optimised(asm_xtensa_t *as, uint reg_dest, uint32_t i32) {
|
||||
if (SIGNED_FIT12(i32)) {
|
||||
asm_xtensa_op_movi(as, reg_dest, i32);
|
||||
} else {
|
||||
asm_xtensa_mov_reg_i32(as, reg_dest, i32);
|
||||
}
|
||||
}
|
||||
|
||||
void asm_xtensa_mov_local_reg(asm_xtensa_t *as, int local_num, uint reg_src) {
|
||||
asm_xtensa_op_s32i(as, reg_src, ASM_XTENSA_REG_A1, local_num);
|
||||
}
|
||||
|
||||
void asm_xtensa_mov_reg_local(asm_xtensa_t *as, uint reg_dest, int local_num) {
|
||||
asm_xtensa_op_l32i(as, reg_dest, ASM_XTENSA_REG_A1, local_num);
|
||||
}
|
||||
|
||||
void asm_xtensa_mov_reg_local_addr(asm_xtensa_t *as, uint reg_dest, int local_num) {
|
||||
uint off = local_num * WORD_SIZE;
|
||||
if (SIGNED_FIT8(off)) {
|
||||
asm_xtensa_op_addi(as, reg_dest, ASM_XTENSA_REG_A1, off);
|
||||
} else {
|
||||
asm_xtensa_op_movi(as, reg_dest, off);
|
||||
asm_xtensa_op_add_n(as, reg_dest, reg_dest, ASM_XTENSA_REG_A1);
|
||||
}
|
||||
}
|
||||
|
||||
void asm_xtensa_mov_reg_pcrel(asm_xtensa_t *as, uint reg_dest, uint label) {
|
||||
// Get relative offset from PC
|
||||
uint32_t dest = get_label_dest(as, label);
|
||||
int32_t rel = dest - as->base.code_offset;
|
||||
rel -= 3 + 3; // account for 3 bytes of movi instruction, 3 bytes call0 adjustment
|
||||
asm_xtensa_op_movi(as, reg_dest, rel); // imm has 12-bit range
|
||||
|
||||
// Use call0 to get PC+3 into a0
|
||||
// call0 destination must be aligned on 4 bytes:
|
||||
// - code_offset&3=0: off=0, pad=1
|
||||
// - code_offset&3=1: off=0, pad=0
|
||||
// - code_offset&3=2: off=1, pad=3
|
||||
// - code_offset&3=3: off=1, pad=2
|
||||
uint32_t off = as->base.code_offset >> 1 & 1;
|
||||
uint32_t pad = (5 - as->base.code_offset) & 3;
|
||||
asm_xtensa_op_call0(as, off);
|
||||
mp_asm_base_get_cur_to_write_bytes(&as->base, pad);
|
||||
|
||||
// Add PC to relative offset
|
||||
asm_xtensa_op_add_n(as, reg_dest, reg_dest, ASM_XTENSA_REG_A0);
|
||||
}
|
||||
|
||||
void asm_xtensa_call_ind(asm_xtensa_t *as, uint idx) {
|
||||
if (idx < 16) {
|
||||
asm_xtensa_op_l32i_n(as, ASM_XTENSA_REG_A0, ASM_XTENSA_REG_FUN_TABLE, idx);
|
||||
} else {
|
||||
asm_xtensa_op_l32i(as, ASM_XTENSA_REG_A0, ASM_XTENSA_REG_FUN_TABLE, idx);
|
||||
}
|
||||
asm_xtensa_op_callx0(as, ASM_XTENSA_REG_A0);
|
||||
}
|
||||
|
||||
void asm_xtensa_call_ind_win(asm_xtensa_t *as, uint idx) {
|
||||
if (idx < 16) {
|
||||
asm_xtensa_op_l32i_n(as, ASM_XTENSA_REG_A8, ASM_XTENSA_REG_FUN_TABLE_WIN, idx);
|
||||
} else {
|
||||
asm_xtensa_op_l32i(as, ASM_XTENSA_REG_A8, ASM_XTENSA_REG_FUN_TABLE_WIN, idx);
|
||||
}
|
||||
asm_xtensa_op_callx8(as, ASM_XTENSA_REG_A8);
|
||||
}
|
||||
|
||||
#endif // MICROPY_EMIT_XTENSA || MICROPY_EMIT_INLINE_XTENSA || MICROPY_EMIT_XTENSAWIN
|
||||
@@ -0,0 +1,408 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2016 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef MICROPY_INCLUDED_PY_ASMXTENSA_H
|
||||
#define MICROPY_INCLUDED_PY_ASMXTENSA_H
|
||||
|
||||
#include "py/misc.h"
|
||||
#include "py/asmbase.h"
|
||||
|
||||
// calling conventions:
|
||||
// up to 6 args in a2-a7
|
||||
// return value in a2
|
||||
// PC stored in a0
|
||||
// stack pointer is a1, stack full descending, is aligned to 16 bytes
|
||||
// callee save: a1, a12, a13, a14, a15
|
||||
// caller save: a3
|
||||
|
||||
// With windowed registers, size 8:
|
||||
// - a0: return PC
|
||||
// - a1: stack pointer, full descending, aligned to 16 bytes
|
||||
// - a2-a7: incoming args, and essentially callee save
|
||||
// - a2: return value
|
||||
// - a8-a15: caller save temporaries
|
||||
// - a10-a15: input args to called function
|
||||
// - a10: return value of called function
|
||||
// note: a0-a7 are saved automatically via window shift of called function
|
||||
|
||||
#define ASM_XTENSA_REG_A0 (0)
|
||||
#define ASM_XTENSA_REG_A1 (1)
|
||||
#define ASM_XTENSA_REG_A2 (2)
|
||||
#define ASM_XTENSA_REG_A3 (3)
|
||||
#define ASM_XTENSA_REG_A4 (4)
|
||||
#define ASM_XTENSA_REG_A5 (5)
|
||||
#define ASM_XTENSA_REG_A6 (6)
|
||||
#define ASM_XTENSA_REG_A7 (7)
|
||||
#define ASM_XTENSA_REG_A8 (8)
|
||||
#define ASM_XTENSA_REG_A9 (9)
|
||||
#define ASM_XTENSA_REG_A10 (10)
|
||||
#define ASM_XTENSA_REG_A11 (11)
|
||||
#define ASM_XTENSA_REG_A12 (12)
|
||||
#define ASM_XTENSA_REG_A13 (13)
|
||||
#define ASM_XTENSA_REG_A14 (14)
|
||||
#define ASM_XTENSA_REG_A15 (15)
|
||||
|
||||
// for bccz
|
||||
#define ASM_XTENSA_CCZ_EQ (0)
|
||||
#define ASM_XTENSA_CCZ_NE (1)
|
||||
|
||||
// for bcc and setcc
|
||||
#define ASM_XTENSA_CC_NONE (0)
|
||||
#define ASM_XTENSA_CC_EQ (1)
|
||||
#define ASM_XTENSA_CC_LT (2)
|
||||
#define ASM_XTENSA_CC_LTU (3)
|
||||
#define ASM_XTENSA_CC_ALL (4)
|
||||
#define ASM_XTENSA_CC_BC (5)
|
||||
#define ASM_XTENSA_CC_ANY (8)
|
||||
#define ASM_XTENSA_CC_NE (9)
|
||||
#define ASM_XTENSA_CC_GE (10)
|
||||
#define ASM_XTENSA_CC_GEU (11)
|
||||
#define ASM_XTENSA_CC_NALL (12)
|
||||
#define ASM_XTENSA_CC_BS (13)
|
||||
|
||||
// macros for encoding instructions (little endian versions)
|
||||
#define ASM_XTENSA_ENCODE_RRR(op0, op1, op2, r, s, t) \
|
||||
((((uint32_t)op2) << 20) | (((uint32_t)op1) << 16) | ((r) << 12) | ((s) << 8) | ((t) << 4) | (op0))
|
||||
#define ASM_XTENSA_ENCODE_RRI4(op0, op1, r, s, t, imm4) \
|
||||
(((imm4) << 20) | ((op1) << 16) | ((r) << 12) | ((s) << 8) | ((t) << 4) | (op0))
|
||||
#define ASM_XTENSA_ENCODE_RRI8(op0, r, s, t, imm8) \
|
||||
((((uint32_t)imm8) << 16) | ((r) << 12) | ((s) << 8) | ((t) << 4) | (op0))
|
||||
#define ASM_XTENSA_ENCODE_RI16(op0, t, imm16) \
|
||||
(((imm16) << 8) | ((t) << 4) | (op0))
|
||||
#define ASM_XTENSA_ENCODE_RSR(op0, op1, op2, rs, t) \
|
||||
(((op2) << 20) | ((op1) << 16) | ((rs) << 8) | ((t) << 4) | (op0))
|
||||
#define ASM_XTENSA_ENCODE_CALL(op0, n, offset) \
|
||||
(((offset) << 6) | ((n) << 4) | (op0))
|
||||
#define ASM_XTENSA_ENCODE_CALLX(op0, op1, op2, r, s, m, n) \
|
||||
((((uint32_t)op2) << 20) | (((uint32_t)op1) << 16) | ((r) << 12) | ((s) << 8) | ((m) << 6) | ((n) << 4) | (op0))
|
||||
#define ASM_XTENSA_ENCODE_BRI8(op0, r, s, m, n, imm8) \
|
||||
(((imm8) << 16) | ((r) << 12) | ((s) << 8) | ((m) << 6) | ((n) << 4) | (op0))
|
||||
#define ASM_XTENSA_ENCODE_BRI12(op0, s, m, n, imm12) \
|
||||
(((imm12) << 12) | ((s) << 8) | ((m) << 6) | ((n) << 4) | (op0))
|
||||
#define ASM_XTENSA_ENCODE_RRRN(op0, r, s, t) \
|
||||
(((r) << 12) | ((s) << 8) | ((t) << 4) | (op0))
|
||||
#define ASM_XTENSA_ENCODE_RI7(op0, s, imm7) \
|
||||
((((imm7) & 0xf) << 12) | ((s) << 8) | ((imm7) & 0x70) | (op0))
|
||||
|
||||
// Number of registers saved on the stack upon entry to function
|
||||
#define ASM_XTENSA_NUM_REGS_SAVED (5)
|
||||
#define ASM_XTENSA_NUM_REGS_SAVED_WIN (1)
|
||||
|
||||
typedef struct _asm_xtensa_t {
|
||||
mp_asm_base_t base;
|
||||
uint32_t cur_const;
|
||||
uint32_t num_const;
|
||||
uint32_t *const_table;
|
||||
uint32_t stack_adjust;
|
||||
} asm_xtensa_t;
|
||||
|
||||
void asm_xtensa_end_pass(asm_xtensa_t *as);
|
||||
|
||||
void asm_xtensa_entry(asm_xtensa_t *as, int num_locals);
|
||||
void asm_xtensa_exit(asm_xtensa_t *as);
|
||||
|
||||
void asm_xtensa_entry_win(asm_xtensa_t *as, int num_locals);
|
||||
void asm_xtensa_exit_win(asm_xtensa_t *as);
|
||||
|
||||
void asm_xtensa_op16(asm_xtensa_t *as, uint16_t op);
|
||||
void asm_xtensa_op24(asm_xtensa_t *as, uint32_t op);
|
||||
|
||||
// raw instructions
|
||||
|
||||
static inline void asm_xtensa_op_entry(asm_xtensa_t *as, uint reg_src, int32_t num_bytes) {
|
||||
asm_xtensa_op24(as, ASM_XTENSA_ENCODE_BRI12(6, reg_src, 0, 3, (num_bytes / 8) & 0xfff));
|
||||
}
|
||||
|
||||
static inline void asm_xtensa_op_add_n(asm_xtensa_t *as, uint reg_dest, uint reg_src_a, uint reg_src_b) {
|
||||
asm_xtensa_op16(as, ASM_XTENSA_ENCODE_RRRN(10, reg_dest, reg_src_a, reg_src_b));
|
||||
}
|
||||
|
||||
static inline void asm_xtensa_op_addi(asm_xtensa_t *as, uint reg_dest, uint reg_src, int imm8) {
|
||||
asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRI8(2, 12, reg_src, reg_dest, imm8 & 0xff));
|
||||
}
|
||||
|
||||
static inline void asm_xtensa_op_and(asm_xtensa_t *as, uint reg_dest, uint reg_src_a, uint reg_src_b) {
|
||||
asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRR(0, 0, 1, reg_dest, reg_src_a, reg_src_b));
|
||||
}
|
||||
|
||||
static inline void asm_xtensa_op_bcc(asm_xtensa_t *as, uint cond, uint reg_src1, uint reg_src2, int32_t rel8) {
|
||||
asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRI8(7, cond, reg_src1, reg_src2, rel8 & 0xff));
|
||||
}
|
||||
|
||||
static inline void asm_xtensa_op_bccz(asm_xtensa_t *as, uint cond, uint reg_src, int32_t rel12) {
|
||||
asm_xtensa_op24(as, ASM_XTENSA_ENCODE_BRI12(6, reg_src, cond, 1, rel12 & 0xfff));
|
||||
}
|
||||
|
||||
static inline void asm_xtensa_op_call0(asm_xtensa_t *as, int32_t rel18) {
|
||||
asm_xtensa_op24(as, ASM_XTENSA_ENCODE_CALL(5, 0, rel18 & 0x3ffff));
|
||||
}
|
||||
|
||||
static inline void asm_xtensa_op_callx0(asm_xtensa_t *as, uint reg) {
|
||||
asm_xtensa_op24(as, ASM_XTENSA_ENCODE_CALLX(0, 0, 0, 0, reg, 3, 0));
|
||||
}
|
||||
|
||||
static inline void asm_xtensa_op_callx8(asm_xtensa_t *as, uint reg) {
|
||||
asm_xtensa_op24(as, ASM_XTENSA_ENCODE_CALLX(0, 0, 0, 0, reg, 3, 2));
|
||||
}
|
||||
|
||||
static inline void asm_xtensa_op_j(asm_xtensa_t *as, int32_t rel18) {
|
||||
asm_xtensa_op24(as, ASM_XTENSA_ENCODE_CALL(6, 0, rel18 & 0x3ffff));
|
||||
}
|
||||
|
||||
static inline void asm_xtensa_op_jx(asm_xtensa_t *as, uint reg) {
|
||||
asm_xtensa_op24(as, ASM_XTENSA_ENCODE_CALLX(0, 0, 0, 0, reg, 2, 2));
|
||||
}
|
||||
|
||||
static inline void asm_xtensa_op_l8ui(asm_xtensa_t *as, uint reg_dest, uint reg_base, uint byte_offset) {
|
||||
asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRI8(2, 0, reg_base, reg_dest, byte_offset & 0xff));
|
||||
}
|
||||
|
||||
static inline void asm_xtensa_op_l16ui(asm_xtensa_t *as, uint reg_dest, uint reg_base, uint half_word_offset) {
|
||||
asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRI8(2, 1, reg_base, reg_dest, half_word_offset & 0xff));
|
||||
}
|
||||
|
||||
static inline void asm_xtensa_op_l32i(asm_xtensa_t *as, uint reg_dest, uint reg_base, uint word_offset) {
|
||||
asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRI8(2, 2, reg_base, reg_dest, word_offset & 0xff));
|
||||
}
|
||||
|
||||
static inline void asm_xtensa_op_l32i_n(asm_xtensa_t *as, uint reg_dest, uint reg_base, uint word_offset) {
|
||||
asm_xtensa_op16(as, ASM_XTENSA_ENCODE_RRRN(8, word_offset & 0xf, reg_base, reg_dest));
|
||||
}
|
||||
|
||||
static inline void asm_xtensa_op_l32r(asm_xtensa_t *as, uint reg_dest, uint32_t op_off, uint32_t dest_off) {
|
||||
asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RI16(1, reg_dest, ((dest_off - ((op_off + 3) & ~3)) >> 2) & 0xffff));
|
||||
}
|
||||
|
||||
static inline void asm_xtensa_op_mov_n(asm_xtensa_t *as, uint reg_dest, uint reg_src) {
|
||||
asm_xtensa_op16(as, ASM_XTENSA_ENCODE_RRRN(13, 0, reg_src, reg_dest));
|
||||
}
|
||||
|
||||
static inline void asm_xtensa_op_movi(asm_xtensa_t *as, uint reg_dest, int32_t imm12) {
|
||||
asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRI8(2, 10, (imm12 >> 8) & 0xf, reg_dest, imm12 & 0xff));
|
||||
}
|
||||
|
||||
static inline void asm_xtensa_op_movi_n(asm_xtensa_t *as, uint reg_dest, int imm4) {
|
||||
asm_xtensa_op16(as, ASM_XTENSA_ENCODE_RI7(12, reg_dest, imm4));
|
||||
}
|
||||
|
||||
static inline void asm_xtensa_op_mull(asm_xtensa_t *as, uint reg_dest, uint reg_src_a, uint reg_src_b) {
|
||||
asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRR(0, 2, 8, reg_dest, reg_src_a, reg_src_b));
|
||||
}
|
||||
|
||||
static inline void asm_xtensa_op_or(asm_xtensa_t *as, uint reg_dest, uint reg_src_a, uint reg_src_b) {
|
||||
asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRR(0, 0, 2, reg_dest, reg_src_a, reg_src_b));
|
||||
}
|
||||
|
||||
static inline void asm_xtensa_op_ret_n(asm_xtensa_t *as) {
|
||||
asm_xtensa_op16(as, ASM_XTENSA_ENCODE_RRRN(13, 15, 0, 0));
|
||||
}
|
||||
|
||||
static inline void asm_xtensa_op_retw_n(asm_xtensa_t *as) {
|
||||
asm_xtensa_op16(as, ASM_XTENSA_ENCODE_RRRN(13, 15, 0, 1));
|
||||
}
|
||||
|
||||
static inline void asm_xtensa_op_s8i(asm_xtensa_t *as, uint reg_src, uint reg_base, uint byte_offset) {
|
||||
asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRI8(2, 4, reg_base, reg_src, byte_offset & 0xff));
|
||||
}
|
||||
|
||||
static inline void asm_xtensa_op_s16i(asm_xtensa_t *as, uint reg_src, uint reg_base, uint half_word_offset) {
|
||||
asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRI8(2, 5, reg_base, reg_src, half_word_offset & 0xff));
|
||||
}
|
||||
|
||||
static inline void asm_xtensa_op_s32i(asm_xtensa_t *as, uint reg_src, uint reg_base, uint word_offset) {
|
||||
asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRI8(2, 6, reg_base, reg_src, word_offset & 0xff));
|
||||
}
|
||||
|
||||
static inline void asm_xtensa_op_s32i_n(asm_xtensa_t *as, uint reg_src, uint reg_base, uint word_offset) {
|
||||
asm_xtensa_op16(as, ASM_XTENSA_ENCODE_RRRN(9, word_offset & 0xf, reg_base, reg_src));
|
||||
}
|
||||
|
||||
static inline void asm_xtensa_op_sll(asm_xtensa_t *as, uint reg_dest, uint reg_src) {
|
||||
asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRR(0, 1, 10, reg_dest, reg_src, 0));
|
||||
}
|
||||
|
||||
static inline void asm_xtensa_op_srl(asm_xtensa_t *as, uint reg_dest, uint reg_src) {
|
||||
asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRR(0, 1, 9, reg_dest, 0, reg_src));
|
||||
}
|
||||
|
||||
static inline void asm_xtensa_op_sra(asm_xtensa_t *as, uint reg_dest, uint reg_src) {
|
||||
asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRR(0, 1, 11, reg_dest, 0, reg_src));
|
||||
}
|
||||
|
||||
static inline void asm_xtensa_op_ssl(asm_xtensa_t *as, uint reg_src) {
|
||||
asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRR(0, 0, 4, 1, reg_src, 0));
|
||||
}
|
||||
|
||||
static inline void asm_xtensa_op_ssr(asm_xtensa_t *as, uint reg_src) {
|
||||
asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRR(0, 0, 4, 0, reg_src, 0));
|
||||
}
|
||||
|
||||
static inline void asm_xtensa_op_sub(asm_xtensa_t *as, uint reg_dest, uint reg_src_a, uint reg_src_b) {
|
||||
asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRR(0, 0, 12, reg_dest, reg_src_a, reg_src_b));
|
||||
}
|
||||
|
||||
static inline void asm_xtensa_op_xor(asm_xtensa_t *as, uint reg_dest, uint reg_src_a, uint reg_src_b) {
|
||||
asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRR(0, 0, 3, reg_dest, reg_src_a, reg_src_b));
|
||||
}
|
||||
|
||||
// convenience functions
|
||||
void asm_xtensa_j_label(asm_xtensa_t *as, uint label);
|
||||
void asm_xtensa_bccz_reg_label(asm_xtensa_t *as, uint cond, uint reg, uint label);
|
||||
void asm_xtensa_bcc_reg_reg_label(asm_xtensa_t *as, uint cond, uint reg1, uint reg2, uint label);
|
||||
void asm_xtensa_setcc_reg_reg_reg(asm_xtensa_t *as, uint cond, uint reg_dest, uint reg_src1, uint reg_src2);
|
||||
size_t asm_xtensa_mov_reg_i32(asm_xtensa_t *as, uint reg_dest, uint32_t i32);
|
||||
void asm_xtensa_mov_reg_i32_optimised(asm_xtensa_t *as, uint reg_dest, uint32_t i32);
|
||||
void asm_xtensa_mov_local_reg(asm_xtensa_t *as, int local_num, uint reg_src);
|
||||
void asm_xtensa_mov_reg_local(asm_xtensa_t *as, uint reg_dest, int local_num);
|
||||
void asm_xtensa_mov_reg_local_addr(asm_xtensa_t *as, uint reg_dest, int local_num);
|
||||
void asm_xtensa_mov_reg_pcrel(asm_xtensa_t *as, uint reg_dest, uint label);
|
||||
void asm_xtensa_call_ind(asm_xtensa_t *as, uint idx);
|
||||
void asm_xtensa_call_ind_win(asm_xtensa_t *as, uint idx);
|
||||
|
||||
// Holds a pointer to mp_fun_table
|
||||
#define ASM_XTENSA_REG_FUN_TABLE ASM_XTENSA_REG_A15
|
||||
#define ASM_XTENSA_REG_FUN_TABLE_WIN ASM_XTENSA_REG_A7
|
||||
|
||||
#if GENERIC_ASM_API
|
||||
|
||||
// The following macros provide a (mostly) arch-independent API to
|
||||
// generate native code, and are used by the native emitter.
|
||||
|
||||
#define ASM_WORD_SIZE (4)
|
||||
|
||||
#if !GENERIC_ASM_API_WIN
|
||||
// Configuration for non-windowed calls
|
||||
|
||||
#define REG_RET ASM_XTENSA_REG_A2
|
||||
#define REG_ARG_1 ASM_XTENSA_REG_A2
|
||||
#define REG_ARG_2 ASM_XTENSA_REG_A3
|
||||
#define REG_ARG_3 ASM_XTENSA_REG_A4
|
||||
#define REG_ARG_4 ASM_XTENSA_REG_A5
|
||||
#define REG_ARG_5 ASM_XTENSA_REG_A6
|
||||
|
||||
#define REG_TEMP0 ASM_XTENSA_REG_A2
|
||||
#define REG_TEMP1 ASM_XTENSA_REG_A3
|
||||
#define REG_TEMP2 ASM_XTENSA_REG_A4
|
||||
|
||||
#define REG_LOCAL_1 ASM_XTENSA_REG_A12
|
||||
#define REG_LOCAL_2 ASM_XTENSA_REG_A13
|
||||
#define REG_LOCAL_3 ASM_XTENSA_REG_A14
|
||||
#define REG_LOCAL_NUM (3)
|
||||
|
||||
#define ASM_NUM_REGS_SAVED ASM_XTENSA_NUM_REGS_SAVED
|
||||
#define REG_FUN_TABLE ASM_XTENSA_REG_FUN_TABLE
|
||||
|
||||
#define ASM_ENTRY(as, nlocal) asm_xtensa_entry((as), (nlocal))
|
||||
#define ASM_EXIT(as) asm_xtensa_exit((as))
|
||||
#define ASM_CALL_IND(as, idx) asm_xtensa_call_ind((as), (idx))
|
||||
|
||||
#else
|
||||
// Configuration for windowed calls with window size 8
|
||||
|
||||
#define REG_PARENT_RET ASM_XTENSA_REG_A2
|
||||
#define REG_PARENT_ARG_1 ASM_XTENSA_REG_A2
|
||||
#define REG_PARENT_ARG_2 ASM_XTENSA_REG_A3
|
||||
#define REG_PARENT_ARG_3 ASM_XTENSA_REG_A4
|
||||
#define REG_PARENT_ARG_4 ASM_XTENSA_REG_A5
|
||||
#define REG_RET ASM_XTENSA_REG_A10
|
||||
#define REG_ARG_1 ASM_XTENSA_REG_A10
|
||||
#define REG_ARG_2 ASM_XTENSA_REG_A11
|
||||
#define REG_ARG_3 ASM_XTENSA_REG_A12
|
||||
#define REG_ARG_4 ASM_XTENSA_REG_A13
|
||||
|
||||
#define REG_TEMP0 ASM_XTENSA_REG_A10
|
||||
#define REG_TEMP1 ASM_XTENSA_REG_A11
|
||||
#define REG_TEMP2 ASM_XTENSA_REG_A12
|
||||
|
||||
#define REG_LOCAL_1 ASM_XTENSA_REG_A4
|
||||
#define REG_LOCAL_2 ASM_XTENSA_REG_A5
|
||||
#define REG_LOCAL_3 ASM_XTENSA_REG_A6
|
||||
#define REG_LOCAL_NUM (3)
|
||||
|
||||
#define ASM_NUM_REGS_SAVED ASM_XTENSA_NUM_REGS_SAVED_WIN
|
||||
#define REG_FUN_TABLE ASM_XTENSA_REG_FUN_TABLE_WIN
|
||||
|
||||
#define ASM_ENTRY(as, nlocal) asm_xtensa_entry_win((as), (nlocal))
|
||||
#define ASM_EXIT(as) asm_xtensa_exit_win((as))
|
||||
#define ASM_CALL_IND(as, idx) asm_xtensa_call_ind_win((as), (idx))
|
||||
|
||||
#endif
|
||||
|
||||
#define ASM_T asm_xtensa_t
|
||||
#define ASM_END_PASS asm_xtensa_end_pass
|
||||
|
||||
#define ASM_JUMP asm_xtensa_j_label
|
||||
#define ASM_JUMP_IF_REG_ZERO(as, reg, label, bool_test) \
|
||||
asm_xtensa_bccz_reg_label(as, ASM_XTENSA_CCZ_EQ, reg, label)
|
||||
#define ASM_JUMP_IF_REG_NONZERO(as, reg, label, bool_test) \
|
||||
asm_xtensa_bccz_reg_label(as, ASM_XTENSA_CCZ_NE, reg, label)
|
||||
#define ASM_JUMP_IF_REG_EQ(as, reg1, reg2, label) \
|
||||
asm_xtensa_bcc_reg_reg_label(as, ASM_XTENSA_CC_EQ, reg1, reg2, label)
|
||||
#define ASM_JUMP_REG(as, reg) asm_xtensa_op_jx((as), (reg))
|
||||
|
||||
#define ASM_MOV_LOCAL_REG(as, local_num, reg_src) asm_xtensa_mov_local_reg((as), ASM_NUM_REGS_SAVED + (local_num), (reg_src))
|
||||
#define ASM_MOV_REG_IMM(as, reg_dest, imm) asm_xtensa_mov_reg_i32_optimised((as), (reg_dest), (imm))
|
||||
#define ASM_MOV_REG_IMM_FIX_U16(as, reg_dest, imm) asm_xtensa_mov_reg_i32((as), (reg_dest), (imm))
|
||||
#define ASM_MOV_REG_IMM_FIX_WORD(as, reg_dest, imm) asm_xtensa_mov_reg_i32((as), (reg_dest), (imm))
|
||||
#define ASM_MOV_REG_LOCAL(as, reg_dest, local_num) asm_xtensa_mov_reg_local((as), (reg_dest), ASM_NUM_REGS_SAVED + (local_num))
|
||||
#define ASM_MOV_REG_REG(as, reg_dest, reg_src) asm_xtensa_op_mov_n((as), (reg_dest), (reg_src))
|
||||
#define ASM_MOV_REG_LOCAL_ADDR(as, reg_dest, local_num) asm_xtensa_mov_reg_local_addr((as), (reg_dest), ASM_NUM_REGS_SAVED + (local_num))
|
||||
#define ASM_MOV_REG_PCREL(as, reg_dest, label) asm_xtensa_mov_reg_pcrel((as), (reg_dest), (label))
|
||||
|
||||
#define ASM_LSL_REG_REG(as, reg_dest, reg_shift) \
|
||||
do { \
|
||||
asm_xtensa_op_ssl((as), (reg_shift)); \
|
||||
asm_xtensa_op_sll((as), (reg_dest), (reg_dest)); \
|
||||
} while (0)
|
||||
#define ASM_LSR_REG_REG(as, reg_dest, reg_shift) \
|
||||
do { \
|
||||
asm_xtensa_op_ssr((as), (reg_shift)); \
|
||||
asm_xtensa_op_srl((as), (reg_dest), (reg_dest)); \
|
||||
} while (0)
|
||||
#define ASM_ASR_REG_REG(as, reg_dest, reg_shift) \
|
||||
do { \
|
||||
asm_xtensa_op_ssr((as), (reg_shift)); \
|
||||
asm_xtensa_op_sra((as), (reg_dest), (reg_dest)); \
|
||||
} while (0)
|
||||
#define ASM_OR_REG_REG(as, reg_dest, reg_src) asm_xtensa_op_or((as), (reg_dest), (reg_dest), (reg_src))
|
||||
#define ASM_XOR_REG_REG(as, reg_dest, reg_src) asm_xtensa_op_xor((as), (reg_dest), (reg_dest), (reg_src))
|
||||
#define ASM_AND_REG_REG(as, reg_dest, reg_src) asm_xtensa_op_and((as), (reg_dest), (reg_dest), (reg_src))
|
||||
#define ASM_ADD_REG_REG(as, reg_dest, reg_src) asm_xtensa_op_add_n((as), (reg_dest), (reg_dest), (reg_src))
|
||||
#define ASM_SUB_REG_REG(as, reg_dest, reg_src) asm_xtensa_op_sub((as), (reg_dest), (reg_dest), (reg_src))
|
||||
#define ASM_MUL_REG_REG(as, reg_dest, reg_src) asm_xtensa_op_mull((as), (reg_dest), (reg_dest), (reg_src))
|
||||
|
||||
#define ASM_LOAD_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_xtensa_op_l32i_n((as), (reg_dest), (reg_base), (word_offset))
|
||||
#define ASM_LOAD8_REG_REG(as, reg_dest, reg_base) asm_xtensa_op_l8ui((as), (reg_dest), (reg_base), 0)
|
||||
#define ASM_LOAD16_REG_REG(as, reg_dest, reg_base) asm_xtensa_op_l16ui((as), (reg_dest), (reg_base), 0)
|
||||
#define ASM_LOAD32_REG_REG(as, reg_dest, reg_base) asm_xtensa_op_l32i_n((as), (reg_dest), (reg_base), 0)
|
||||
|
||||
#define ASM_STORE_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_xtensa_op_s32i_n((as), (reg_dest), (reg_base), (word_offset))
|
||||
#define ASM_STORE8_REG_REG(as, reg_src, reg_base) asm_xtensa_op_s8i((as), (reg_src), (reg_base), 0)
|
||||
#define ASM_STORE16_REG_REG(as, reg_src, reg_base) asm_xtensa_op_s16i((as), (reg_src), (reg_base), 0)
|
||||
#define ASM_STORE32_REG_REG(as, reg_src, reg_base) asm_xtensa_op_s32i_n((as), (reg_src), (reg_base), 0)
|
||||
|
||||
#endif // GENERIC_ASM_API
|
||||
|
||||
#endif // MICROPY_INCLUDED_PY_ASMXTENSA_H
|
||||
@@ -0,0 +1,343 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014 Damien P. George
|
||||
* Copyright (c) 2014 Paul Sokolovsky
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "py/runtime.h"
|
||||
#include "py/bc0.h"
|
||||
#include "py/bc.h"
|
||||
|
||||
#if MICROPY_DEBUG_VERBOSE // print debugging info
|
||||
#define DEBUG_PRINT (1)
|
||||
#else // don't print debugging info
|
||||
#define DEBUG_PRINT (0)
|
||||
#define DEBUG_printf(...) (void)0
|
||||
#endif
|
||||
|
||||
#if !MICROPY_PERSISTENT_CODE
|
||||
|
||||
mp_uint_t mp_decode_uint(const byte **ptr) {
|
||||
mp_uint_t unum = 0;
|
||||
byte val;
|
||||
const byte *p = *ptr;
|
||||
do {
|
||||
val = *p++;
|
||||
unum = (unum << 7) | (val & 0x7f);
|
||||
} while ((val & 0x80) != 0);
|
||||
*ptr = p;
|
||||
return unum;
|
||||
}
|
||||
|
||||
// This function is used to help reduce stack usage at the caller, for the case when
|
||||
// the caller doesn't need to increase the ptr argument. If ptr is a local variable
|
||||
// and the caller uses mp_decode_uint(&ptr) instead of this function, then the compiler
|
||||
// must allocate a slot on the stack for ptr, and this slot cannot be reused for
|
||||
// anything else in the function because the pointer may have been stored in a global
|
||||
// and reused later in the function.
|
||||
mp_uint_t mp_decode_uint_value(const byte *ptr) {
|
||||
return mp_decode_uint(&ptr);
|
||||
}
|
||||
|
||||
// This function is used to help reduce stack usage at the caller, for the case when
|
||||
// the caller doesn't need the actual value and just wants to skip over it.
|
||||
const byte *mp_decode_uint_skip(const byte *ptr) {
|
||||
while ((*ptr++) & 0x80) {
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
STATIC NORETURN void fun_pos_args_mismatch(mp_obj_fun_bc_t *f, size_t expected, size_t given) {
|
||||
#if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE
|
||||
// generic message, used also for other argument issues
|
||||
(void)f;
|
||||
(void)expected;
|
||||
(void)given;
|
||||
mp_arg_error_terse_mismatch();
|
||||
#elif MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_NORMAL
|
||||
(void)f;
|
||||
mp_raise_msg_varg(&mp_type_TypeError,
|
||||
MP_ERROR_TEXT("function takes %d positional arguments but %d were given"), expected, given);
|
||||
#elif MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_DETAILED
|
||||
mp_raise_msg_varg(&mp_type_TypeError,
|
||||
MP_ERROR_TEXT("%q() takes %d positional arguments but %d were given"),
|
||||
mp_obj_fun_get_name(MP_OBJ_FROM_PTR(f)), expected, given);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if DEBUG_PRINT
|
||||
STATIC void dump_args(const mp_obj_t *a, size_t sz) {
|
||||
DEBUG_printf("%p: ", a);
|
||||
for (size_t i = 0; i < sz; i++) {
|
||||
DEBUG_printf("%p ", a[i]);
|
||||
}
|
||||
DEBUG_printf("\n");
|
||||
}
|
||||
#else
|
||||
#define dump_args(...) (void)0
|
||||
#endif
|
||||
|
||||
// On entry code_state should be allocated somewhere (stack/heap) and
|
||||
// contain the following valid entries:
|
||||
// - code_state->fun_bc should contain a pointer to the function object
|
||||
// - code_state->ip should contain the offset in bytes from the pointer
|
||||
// code_state->fun_bc->bytecode to the entry n_state (0 for bytecode, non-zero for native)
|
||||
void mp_setup_code_state(mp_code_state_t *code_state, size_t n_args, size_t n_kw, const mp_obj_t *args) {
|
||||
// This function is pretty complicated. It's main aim is to be efficient in speed and RAM
|
||||
// usage for the common case of positional only args.
|
||||
|
||||
// get the function object that we want to set up (could be bytecode or native code)
|
||||
mp_obj_fun_bc_t *self = code_state->fun_bc;
|
||||
|
||||
// ip comes in as an offset into bytecode, so turn it into a true pointer
|
||||
code_state->ip = self->bytecode + (size_t)code_state->ip;
|
||||
|
||||
#if MICROPY_STACKLESS
|
||||
code_state->prev = NULL;
|
||||
#endif
|
||||
|
||||
#if MICROPY_PY_SYS_SETTRACE
|
||||
code_state->prev_state = NULL;
|
||||
code_state->frame = NULL;
|
||||
#endif
|
||||
|
||||
// Get cached n_state (rather than decode it again)
|
||||
size_t n_state = code_state->n_state;
|
||||
|
||||
// Decode prelude
|
||||
size_t n_state_unused, n_exc_stack_unused, scope_flags, n_pos_args, n_kwonly_args, n_def_pos_args;
|
||||
MP_BC_PRELUDE_SIG_DECODE_INTO(code_state->ip, n_state_unused, n_exc_stack_unused, scope_flags, n_pos_args, n_kwonly_args, n_def_pos_args);
|
||||
(void)n_state_unused;
|
||||
(void)n_exc_stack_unused;
|
||||
|
||||
code_state->sp = &code_state->state[0] - 1;
|
||||
code_state->exc_sp_idx = 0;
|
||||
|
||||
// zero out the local stack to begin with
|
||||
memset(code_state->state, 0, n_state * sizeof(*code_state->state));
|
||||
|
||||
const mp_obj_t *kwargs = args + n_args;
|
||||
|
||||
// var_pos_kw_args points to the stack where the var-args tuple, and var-kw dict, should go (if they are needed)
|
||||
mp_obj_t *var_pos_kw_args = &code_state->state[n_state - 1 - n_pos_args - n_kwonly_args];
|
||||
|
||||
// check positional arguments
|
||||
|
||||
if (n_args > n_pos_args) {
|
||||
// given more than enough arguments
|
||||
if ((scope_flags & MP_SCOPE_FLAG_VARARGS) == 0) {
|
||||
fun_pos_args_mismatch(self, n_pos_args, n_args);
|
||||
}
|
||||
// put extra arguments in varargs tuple
|
||||
*var_pos_kw_args-- = mp_obj_new_tuple(n_args - n_pos_args, args + n_pos_args);
|
||||
n_args = n_pos_args;
|
||||
} else {
|
||||
if ((scope_flags & MP_SCOPE_FLAG_VARARGS) != 0) {
|
||||
DEBUG_printf("passing empty tuple as *args\n");
|
||||
*var_pos_kw_args-- = mp_const_empty_tuple;
|
||||
}
|
||||
// Apply processing and check below only if we don't have kwargs,
|
||||
// otherwise, kw handling code below has own extensive checks.
|
||||
if (n_kw == 0 && (scope_flags & MP_SCOPE_FLAG_DEFKWARGS) == 0) {
|
||||
if (n_args >= (size_t)(n_pos_args - n_def_pos_args)) {
|
||||
// given enough arguments, but may need to use some default arguments
|
||||
for (size_t i = n_args; i < n_pos_args; i++) {
|
||||
code_state->state[n_state - 1 - i] = self->extra_args[i - (n_pos_args - n_def_pos_args)];
|
||||
}
|
||||
} else {
|
||||
fun_pos_args_mismatch(self, n_pos_args - n_def_pos_args, n_args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// copy positional args into state
|
||||
for (size_t i = 0; i < n_args; i++) {
|
||||
code_state->state[n_state - 1 - i] = args[i];
|
||||
}
|
||||
|
||||
// check keyword arguments
|
||||
|
||||
if (n_kw != 0 || (scope_flags & MP_SCOPE_FLAG_DEFKWARGS) != 0) {
|
||||
DEBUG_printf("Initial args: ");
|
||||
dump_args(code_state->state + n_state - n_pos_args - n_kwonly_args, n_pos_args + n_kwonly_args);
|
||||
|
||||
mp_obj_t dict = MP_OBJ_NULL;
|
||||
if ((scope_flags & MP_SCOPE_FLAG_VARKEYWORDS) != 0) {
|
||||
dict = mp_obj_new_dict(n_kw); // TODO: better go conservative with 0?
|
||||
*var_pos_kw_args = dict;
|
||||
}
|
||||
|
||||
// get pointer to arg_names array
|
||||
const mp_obj_t *arg_names = (const mp_obj_t *)self->const_table;
|
||||
|
||||
for (size_t i = 0; i < n_kw; i++) {
|
||||
// the keys in kwargs are expected to be qstr objects
|
||||
mp_obj_t wanted_arg_name = kwargs[2 * i];
|
||||
for (size_t j = 0; j < n_pos_args + n_kwonly_args; j++) {
|
||||
if (wanted_arg_name == arg_names[j]) {
|
||||
if (code_state->state[n_state - 1 - j] != MP_OBJ_NULL) {
|
||||
mp_raise_msg_varg(&mp_type_TypeError,
|
||||
MP_ERROR_TEXT("function got multiple values for argument '%q'"), MP_OBJ_QSTR_VALUE(wanted_arg_name));
|
||||
}
|
||||
code_state->state[n_state - 1 - j] = kwargs[2 * i + 1];
|
||||
goto continue2;
|
||||
}
|
||||
}
|
||||
// Didn't find name match with positional args
|
||||
if ((scope_flags & MP_SCOPE_FLAG_VARKEYWORDS) == 0) {
|
||||
#if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("unexpected keyword argument"));
|
||||
#else
|
||||
mp_raise_msg_varg(&mp_type_TypeError,
|
||||
MP_ERROR_TEXT("unexpected keyword argument '%q'"), MP_OBJ_QSTR_VALUE(wanted_arg_name));
|
||||
#endif
|
||||
}
|
||||
mp_obj_dict_store(dict, kwargs[2 * i], kwargs[2 * i + 1]);
|
||||
continue2:;
|
||||
}
|
||||
|
||||
DEBUG_printf("Args with kws flattened: ");
|
||||
dump_args(code_state->state + n_state - n_pos_args - n_kwonly_args, n_pos_args + n_kwonly_args);
|
||||
|
||||
// fill in defaults for positional args
|
||||
mp_obj_t *d = &code_state->state[n_state - n_pos_args];
|
||||
mp_obj_t *s = &self->extra_args[n_def_pos_args - 1];
|
||||
for (size_t i = n_def_pos_args; i > 0; i--, d++, s--) {
|
||||
if (*d == MP_OBJ_NULL) {
|
||||
*d = *s;
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_printf("Args after filling default positional: ");
|
||||
dump_args(code_state->state + n_state - n_pos_args - n_kwonly_args, n_pos_args + n_kwonly_args);
|
||||
|
||||
// Check that all mandatory positional args are specified
|
||||
while (d < &code_state->state[n_state]) {
|
||||
if (*d++ == MP_OBJ_NULL) {
|
||||
mp_raise_msg_varg(&mp_type_TypeError,
|
||||
MP_ERROR_TEXT("function missing required positional argument #%d"), &code_state->state[n_state] - d);
|
||||
}
|
||||
}
|
||||
|
||||
// Check that all mandatory keyword args are specified
|
||||
// Fill in default kw args if we have them
|
||||
for (size_t i = 0; i < n_kwonly_args; i++) {
|
||||
if (code_state->state[n_state - 1 - n_pos_args - i] == MP_OBJ_NULL) {
|
||||
mp_map_elem_t *elem = NULL;
|
||||
if ((scope_flags & MP_SCOPE_FLAG_DEFKWARGS) != 0) {
|
||||
elem = mp_map_lookup(&((mp_obj_dict_t *)MP_OBJ_TO_PTR(self->extra_args[n_def_pos_args]))->map, arg_names[n_pos_args + i], MP_MAP_LOOKUP);
|
||||
}
|
||||
if (elem != NULL) {
|
||||
code_state->state[n_state - 1 - n_pos_args - i] = elem->value;
|
||||
} else {
|
||||
mp_raise_msg_varg(&mp_type_TypeError,
|
||||
MP_ERROR_TEXT("function missing required keyword argument '%q'"), MP_OBJ_QSTR_VALUE(arg_names[n_pos_args + i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// no keyword arguments given
|
||||
if (n_kwonly_args != 0) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("function missing keyword-only argument"));
|
||||
}
|
||||
if ((scope_flags & MP_SCOPE_FLAG_VARKEYWORDS) != 0) {
|
||||
*var_pos_kw_args = mp_obj_new_dict(0);
|
||||
}
|
||||
}
|
||||
|
||||
// read the size part of the prelude
|
||||
const byte *ip = code_state->ip;
|
||||
MP_BC_PRELUDE_SIZE_DECODE(ip);
|
||||
|
||||
// jump over code info (source file and line-number mapping)
|
||||
ip += n_info;
|
||||
|
||||
// bytecode prelude: initialise closed over variables
|
||||
for (; n_cell; --n_cell) {
|
||||
size_t local_num = *ip++;
|
||||
code_state->state[n_state - 1 - local_num] =
|
||||
mp_obj_new_cell(code_state->state[n_state - 1 - local_num]);
|
||||
}
|
||||
|
||||
#if !MICROPY_PERSISTENT_CODE
|
||||
// so bytecode is aligned
|
||||
ip = MP_ALIGN(ip, sizeof(mp_uint_t));
|
||||
#endif
|
||||
|
||||
// now that we skipped over the prelude, set the ip for the VM
|
||||
code_state->ip = ip;
|
||||
|
||||
DEBUG_printf("Calling: n_pos_args=%d, n_kwonly_args=%d\n", n_pos_args, n_kwonly_args);
|
||||
dump_args(code_state->state + n_state - n_pos_args - n_kwonly_args, n_pos_args + n_kwonly_args);
|
||||
dump_args(code_state->state, n_state);
|
||||
}
|
||||
|
||||
#if MICROPY_PERSISTENT_CODE_LOAD || MICROPY_PERSISTENT_CODE_SAVE
|
||||
|
||||
// The following table encodes the number of bytes that a specific opcode
|
||||
// takes up. Some opcodes have an extra byte, defined by MP_BC_MASK_EXTRA_BYTE.
|
||||
// There are 4 special opcodes that have an extra byte only when
|
||||
// MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE is enabled (and they take a qstr):
|
||||
// MP_BC_LOAD_NAME
|
||||
// MP_BC_LOAD_GLOBAL
|
||||
// MP_BC_LOAD_ATTR
|
||||
// MP_BC_STORE_ATTR
|
||||
uint mp_opcode_format(const byte *ip, size_t *opcode_size, bool count_var_uint) {
|
||||
uint f = MP_BC_FORMAT(*ip);
|
||||
const byte *ip_start = ip;
|
||||
if (f == MP_BC_FORMAT_QSTR) {
|
||||
if (MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE_DYNAMIC) {
|
||||
if (*ip == MP_BC_LOAD_NAME
|
||||
|| *ip == MP_BC_LOAD_GLOBAL
|
||||
|| *ip == MP_BC_LOAD_ATTR
|
||||
|| *ip == MP_BC_STORE_ATTR) {
|
||||
ip += 1;
|
||||
}
|
||||
}
|
||||
ip += 3;
|
||||
} else {
|
||||
int extra_byte = (*ip & MP_BC_MASK_EXTRA_BYTE) == 0;
|
||||
ip += 1;
|
||||
if (f == MP_BC_FORMAT_VAR_UINT) {
|
||||
if (count_var_uint) {
|
||||
while ((*ip++ & 0x80) != 0) {
|
||||
}
|
||||
}
|
||||
} else if (f == MP_BC_FORMAT_OFFSET) {
|
||||
ip += 2;
|
||||
}
|
||||
ip += extra_byte;
|
||||
}
|
||||
*opcode_size = ip - ip_start;
|
||||
return f;
|
||||
}
|
||||
|
||||
#endif // MICROPY_PERSISTENT_CODE_LOAD || MICROPY_PERSISTENT_CODE_SAVE
|
||||
@@ -0,0 +1,273 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013, 2014 Damien P. George
|
||||
* Copyright (c) 2014 Paul Sokolovsky
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef MICROPY_INCLUDED_PY_BC_H
|
||||
#define MICROPY_INCLUDED_PY_BC_H
|
||||
|
||||
#include "py/runtime.h"
|
||||
#include "py/objfun.h"
|
||||
|
||||
// bytecode layout:
|
||||
//
|
||||
// func signature : var uint
|
||||
// contains six values interleaved bit-wise as: xSSSSEAA [xFSSKAED repeated]
|
||||
// x = extension another byte follows
|
||||
// S = n_state - 1 number of entries in Python value stack
|
||||
// E = n_exc_stack number of entries in exception stack
|
||||
// F = scope_flags four bits of flags, MP_SCOPE_FLAG_xxx
|
||||
// A = n_pos_args number of arguments this function takes
|
||||
// K = n_kwonly_args number of keyword-only arguments this function takes
|
||||
// D = n_def_pos_args number of default positional arguments
|
||||
//
|
||||
// prelude size : var uint
|
||||
// contains two values interleaved bit-wise as: xIIIIIIC repeated
|
||||
// x = extension another byte follows
|
||||
// I = n_info number of bytes in source info section
|
||||
// C = n_cells number of bytes/cells in closure section
|
||||
//
|
||||
// source info section:
|
||||
// simple_name : var qstr
|
||||
// source_file : var qstr
|
||||
// <line number info>
|
||||
//
|
||||
// closure section:
|
||||
// local_num0 : byte
|
||||
// ... : byte
|
||||
// local_numN : byte N = n_cells-1
|
||||
//
|
||||
// <word alignment padding> only needed if bytecode contains pointers
|
||||
//
|
||||
// <bytecode>
|
||||
//
|
||||
//
|
||||
// constant table layout:
|
||||
//
|
||||
// argname0 : obj (qstr)
|
||||
// ... : obj (qstr)
|
||||
// argnameN : obj (qstr) N = num_pos_args + num_kwonly_args
|
||||
// const0 : obj
|
||||
// constN : obj
|
||||
|
||||
#define MP_BC_PRELUDE_SIG_ENCODE(S, E, scope, out_byte, out_env) \
|
||||
do { \
|
||||
/*// Get values to store in prelude */ \
|
||||
size_t F = scope->scope_flags & MP_SCOPE_FLAG_ALL_SIG; \
|
||||
size_t A = scope->num_pos_args; \
|
||||
size_t K = scope->num_kwonly_args; \
|
||||
size_t D = scope->num_def_pos_args; \
|
||||
\
|
||||
/* Adjust S to shrink range, to compress better */ \
|
||||
S -= 1; \
|
||||
\
|
||||
/* Encode prelude */ \
|
||||
/* xSSSSEAA */ \
|
||||
uint8_t z = (S & 0xf) << 3 | (E & 1) << 2 | (A & 3); \
|
||||
S >>= 4; \
|
||||
E >>= 1; \
|
||||
A >>= 2; \
|
||||
while (S | E | F | A | K | D) { \
|
||||
out_byte(out_env, 0x80 | z); \
|
||||
/* xFSSKAED */ \
|
||||
z = (F & 1) << 6 | (S & 3) << 4 | (K & 1) << 3 \
|
||||
| (A & 1) << 2 | (E & 1) << 1 | (D & 1); \
|
||||
S >>= 2; \
|
||||
E >>= 1; \
|
||||
F >>= 1; \
|
||||
A >>= 1; \
|
||||
K >>= 1; \
|
||||
D >>= 1; \
|
||||
} \
|
||||
out_byte(out_env, z); \
|
||||
} while (0)
|
||||
|
||||
#define MP_BC_PRELUDE_SIG_DECODE_INTO(ip, S, E, F, A, K, D) \
|
||||
do { \
|
||||
uint8_t z = *(ip)++; \
|
||||
/* xSSSSEAA */ \
|
||||
S = (z >> 3) & 0xf; \
|
||||
E = (z >> 2) & 0x1; \
|
||||
F = 0; \
|
||||
A = z & 0x3; \
|
||||
K = 0; \
|
||||
D = 0; \
|
||||
for (unsigned n = 0; z & 0x80; ++n) { \
|
||||
z = *(ip)++; \
|
||||
/* xFSSKAED */ \
|
||||
S |= (z & 0x30) << (2 * n); \
|
||||
E |= (z & 0x02) << n; \
|
||||
F |= ((z & 0x40) >> 6) << n; \
|
||||
A |= (z & 0x4) << n; \
|
||||
K |= ((z & 0x08) >> 3) << n; \
|
||||
D |= (z & 0x1) << n; \
|
||||
} \
|
||||
S += 1; \
|
||||
} while (0)
|
||||
|
||||
#define MP_BC_PRELUDE_SIG_DECODE(ip) \
|
||||
size_t n_state, n_exc_stack, scope_flags, n_pos_args, n_kwonly_args, n_def_pos_args; \
|
||||
MP_BC_PRELUDE_SIG_DECODE_INTO(ip, n_state, n_exc_stack, scope_flags, n_pos_args, n_kwonly_args, n_def_pos_args)
|
||||
|
||||
#define MP_BC_PRELUDE_SIZE_ENCODE(I, C, out_byte, out_env) \
|
||||
do { \
|
||||
/* Encode bit-wise as: xIIIIIIC */ \
|
||||
uint8_t z = 0; \
|
||||
do { \
|
||||
z = (I & 0x3f) << 1 | (C & 1); \
|
||||
C >>= 1; \
|
||||
I >>= 6; \
|
||||
if (C | I) { \
|
||||
z |= 0x80; \
|
||||
} \
|
||||
out_byte(out_env, z); \
|
||||
} while (C | I); \
|
||||
} while (0)
|
||||
|
||||
#define MP_BC_PRELUDE_SIZE_DECODE_INTO(ip, I, C) \
|
||||
do { \
|
||||
uint8_t z; \
|
||||
C = 0; \
|
||||
I = 0; \
|
||||
for (unsigned n = 0;; ++n) { \
|
||||
z = *(ip)++; \
|
||||
/* xIIIIIIC */ \
|
||||
C |= (z & 1) << n; \
|
||||
I |= ((z & 0x7e) >> 1) << (6 * n); \
|
||||
if (!(z & 0x80)) { \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define MP_BC_PRELUDE_SIZE_DECODE(ip) \
|
||||
size_t n_info, n_cell; \
|
||||
MP_BC_PRELUDE_SIZE_DECODE_INTO(ip, n_info, n_cell)
|
||||
|
||||
// Sentinel value for mp_code_state_t.exc_sp_idx
|
||||
#define MP_CODE_STATE_EXC_SP_IDX_SENTINEL ((uint16_t)-1)
|
||||
|
||||
// To convert mp_code_state_t.exc_sp_idx to/from a pointer to mp_exc_stack_t
|
||||
#define MP_CODE_STATE_EXC_SP_IDX_FROM_PTR(exc_stack, exc_sp) ((exc_sp) + 1 - (exc_stack))
|
||||
#define MP_CODE_STATE_EXC_SP_IDX_TO_PTR(exc_stack, exc_sp_idx) ((exc_stack) + (exc_sp_idx) - 1)
|
||||
|
||||
typedef struct _mp_bytecode_prelude_t {
|
||||
uint n_state;
|
||||
uint n_exc_stack;
|
||||
uint scope_flags;
|
||||
uint n_pos_args;
|
||||
uint n_kwonly_args;
|
||||
uint n_def_pos_args;
|
||||
qstr qstr_block_name;
|
||||
qstr qstr_source_file;
|
||||
const byte *line_info;
|
||||
const byte *opcodes;
|
||||
} mp_bytecode_prelude_t;
|
||||
|
||||
// Exception stack entry
|
||||
typedef struct _mp_exc_stack_t {
|
||||
const byte *handler;
|
||||
// bit 0 is currently unused
|
||||
// bit 1 is whether the opcode was SETUP_WITH or SETUP_FINALLY
|
||||
mp_obj_t *val_sp;
|
||||
// Saved exception
|
||||
mp_obj_base_t *prev_exc;
|
||||
} mp_exc_stack_t;
|
||||
|
||||
typedef struct _mp_code_state_t {
|
||||
// The fun_bc entry points to the underlying function object that is being executed.
|
||||
// It is needed to access the start of bytecode and the const_table.
|
||||
// It is also needed to prevent the GC from reclaiming the bytecode during execution,
|
||||
// because the ip pointer below will always point to the interior of the bytecode.
|
||||
mp_obj_fun_bc_t *fun_bc;
|
||||
const byte *ip;
|
||||
mp_obj_t *sp;
|
||||
uint16_t n_state;
|
||||
uint16_t exc_sp_idx;
|
||||
mp_obj_dict_t *old_globals;
|
||||
#if MICROPY_STACKLESS
|
||||
struct _mp_code_state_t *prev;
|
||||
#endif
|
||||
#if MICROPY_PY_SYS_SETTRACE
|
||||
struct _mp_code_state_t *prev_state;
|
||||
struct _mp_obj_frame_t *frame;
|
||||
#endif
|
||||
// Variable-length
|
||||
mp_obj_t state[0];
|
||||
// Variable-length, never accessed by name, only as (void*)(state + n_state)
|
||||
// mp_exc_stack_t exc_state[0];
|
||||
} mp_code_state_t;
|
||||
|
||||
mp_uint_t mp_decode_uint(const byte **ptr);
|
||||
mp_uint_t mp_decode_uint_value(const byte *ptr);
|
||||
const byte *mp_decode_uint_skip(const byte *ptr);
|
||||
|
||||
mp_vm_return_kind_t mp_execute_bytecode(mp_code_state_t *code_state, volatile mp_obj_t inject_exc);
|
||||
mp_code_state_t *mp_obj_fun_bc_prepare_codestate(mp_obj_t func, size_t n_args, size_t n_kw, const mp_obj_t *args);
|
||||
void mp_setup_code_state(mp_code_state_t *code_state, size_t n_args, size_t n_kw, const mp_obj_t *args);
|
||||
void mp_bytecode_print(const mp_print_t *print, const void *descr, const byte *code, mp_uint_t len, const mp_uint_t *const_table);
|
||||
void mp_bytecode_print2(const mp_print_t *print, const byte *code, size_t len, const mp_uint_t *const_table);
|
||||
const byte *mp_bytecode_print_str(const mp_print_t *print, const byte *ip);
|
||||
#define mp_bytecode_print_inst(print, code, const_table) mp_bytecode_print2(print, code, 1, const_table)
|
||||
|
||||
// Helper macros to access pointer with least significant bits holding flags
|
||||
#define MP_TAGPTR_PTR(x) ((void *)((uintptr_t)(x) & ~((uintptr_t)3)))
|
||||
#define MP_TAGPTR_TAG0(x) ((uintptr_t)(x) & 1)
|
||||
#define MP_TAGPTR_TAG1(x) ((uintptr_t)(x) & 2)
|
||||
#define MP_TAGPTR_MAKE(ptr, tag) ((void *)((uintptr_t)(ptr) | (tag)))
|
||||
|
||||
#if MICROPY_PERSISTENT_CODE_LOAD || MICROPY_PERSISTENT_CODE_SAVE
|
||||
|
||||
uint mp_opcode_format(const byte *ip, size_t *opcode_size, bool count_var_uint);
|
||||
|
||||
#endif
|
||||
|
||||
static inline size_t mp_bytecode_get_source_line(const byte *line_info, size_t bc_offset) {
|
||||
size_t source_line = 1;
|
||||
size_t c;
|
||||
while ((c = *line_info)) {
|
||||
size_t b, l;
|
||||
if ((c & 0x80) == 0) {
|
||||
// 0b0LLBBBBB encoding
|
||||
b = c & 0x1f;
|
||||
l = c >> 5;
|
||||
line_info += 1;
|
||||
} else {
|
||||
// 0b1LLLBBBB 0bLLLLLLLL encoding (l's LSB in second byte)
|
||||
b = c & 0xf;
|
||||
l = ((c << 4) & 0x700) | line_info[1];
|
||||
line_info += 2;
|
||||
}
|
||||
if (bc_offset >= b) {
|
||||
bc_offset -= b;
|
||||
source_line += l;
|
||||
} else {
|
||||
// found source line corresponding to bytecode offset
|
||||
break;
|
||||
}
|
||||
}
|
||||
return source_line;
|
||||
}
|
||||
|
||||
#endif // MICROPY_INCLUDED_PY_BC_H
|
||||
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013, 2014 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef MICROPY_INCLUDED_PY_BC0_H
|
||||
#define MICROPY_INCLUDED_PY_BC0_H
|
||||
|
||||
// MicroPython bytecode opcodes, grouped based on the format of the opcode
|
||||
|
||||
#define MP_BC_MASK_FORMAT (0xf0)
|
||||
#define MP_BC_MASK_EXTRA_BYTE (0x9e)
|
||||
|
||||
#define MP_BC_FORMAT_BYTE (0)
|
||||
#define MP_BC_FORMAT_QSTR (1)
|
||||
#define MP_BC_FORMAT_VAR_UINT (2)
|
||||
#define MP_BC_FORMAT_OFFSET (3)
|
||||
|
||||
// Nibbles in magic number are: BB BB BB BB BB BO VV QU
|
||||
#define MP_BC_FORMAT(op) ((0x000003a4 >> (2 * ((op) >> 4))) & 3)
|
||||
|
||||
// Load, Store, Delete, Import, Make, Build, Unpack, Call, Jump, Exception, For, sTack, Return, Yield, Op
|
||||
#define MP_BC_BASE_RESERVED (0x00) // ----------------
|
||||
#define MP_BC_BASE_QSTR_O (0x10) // LLLLLLSSSDDII---
|
||||
#define MP_BC_BASE_VINT_E (0x20) // MMLLLLSSDDBBBBBB
|
||||
#define MP_BC_BASE_VINT_O (0x30) // UUMMCCCC--------
|
||||
#define MP_BC_BASE_JUMP_E (0x40) // J-JJJJJEEEEF----
|
||||
#define MP_BC_BASE_BYTE_O (0x50) // LLLLSSDTTTTTEEFF
|
||||
#define MP_BC_BASE_BYTE_E (0x60) // --BREEEYYI------
|
||||
#define MP_BC_LOAD_CONST_SMALL_INT_MULTI (0x70) // LLLLLLLLLLLLLLLL
|
||||
// (0x80) // LLLLLLLLLLLLLLLL
|
||||
// (0x90) // LLLLLLLLLLLLLLLL
|
||||
// (0xa0) // LLLLLLLLLLLLLLLL
|
||||
#define MP_BC_LOAD_FAST_MULTI (0xb0) // LLLLLLLLLLLLLLLL
|
||||
#define MP_BC_STORE_FAST_MULTI (0xc0) // SSSSSSSSSSSSSSSS
|
||||
#define MP_BC_UNARY_OP_MULTI (0xd0) // OOOOOOO
|
||||
#define MP_BC_BINARY_OP_MULTI (0xd7) // OOOOOOOOO
|
||||
// (0xe0) // OOOOOOOOOOOOOOOO
|
||||
// (0xf0) // OOOOOOOOOO------
|
||||
|
||||
#define MP_BC_LOAD_CONST_SMALL_INT_MULTI_NUM (64)
|
||||
#define MP_BC_LOAD_CONST_SMALL_INT_MULTI_EXCESS (16)
|
||||
#define MP_BC_LOAD_FAST_MULTI_NUM (16)
|
||||
#define MP_BC_STORE_FAST_MULTI_NUM (16)
|
||||
#define MP_BC_UNARY_OP_MULTI_NUM (MP_UNARY_OP_NUM_BYTECODE)
|
||||
#define MP_BC_BINARY_OP_MULTI_NUM (MP_BINARY_OP_NUM_BYTECODE)
|
||||
|
||||
#define MP_BC_LOAD_CONST_FALSE (MP_BC_BASE_BYTE_O + 0x00)
|
||||
#define MP_BC_LOAD_CONST_NONE (MP_BC_BASE_BYTE_O + 0x01)
|
||||
#define MP_BC_LOAD_CONST_TRUE (MP_BC_BASE_BYTE_O + 0x02)
|
||||
#define MP_BC_LOAD_CONST_SMALL_INT (MP_BC_BASE_VINT_E + 0x02) // signed var-int
|
||||
#define MP_BC_LOAD_CONST_STRING (MP_BC_BASE_QSTR_O + 0x00) // qstr
|
||||
#define MP_BC_LOAD_CONST_OBJ (MP_BC_BASE_VINT_E + 0x03) // ptr
|
||||
#define MP_BC_LOAD_NULL (MP_BC_BASE_BYTE_O + 0x03)
|
||||
|
||||
#define MP_BC_LOAD_FAST_N (MP_BC_BASE_VINT_E + 0x04) // uint
|
||||
#define MP_BC_LOAD_DEREF (MP_BC_BASE_VINT_E + 0x05) // uint
|
||||
#define MP_BC_LOAD_NAME (MP_BC_BASE_QSTR_O + 0x01) // qstr
|
||||
#define MP_BC_LOAD_GLOBAL (MP_BC_BASE_QSTR_O + 0x02) // qstr
|
||||
#define MP_BC_LOAD_ATTR (MP_BC_BASE_QSTR_O + 0x03) // qstr
|
||||
#define MP_BC_LOAD_METHOD (MP_BC_BASE_QSTR_O + 0x04) // qstr
|
||||
#define MP_BC_LOAD_SUPER_METHOD (MP_BC_BASE_QSTR_O + 0x05) // qstr
|
||||
#define MP_BC_LOAD_BUILD_CLASS (MP_BC_BASE_BYTE_O + 0x04)
|
||||
#define MP_BC_LOAD_SUBSCR (MP_BC_BASE_BYTE_O + 0x05)
|
||||
|
||||
#define MP_BC_STORE_FAST_N (MP_BC_BASE_VINT_E + 0x06) // uint
|
||||
#define MP_BC_STORE_DEREF (MP_BC_BASE_VINT_E + 0x07) // uint
|
||||
#define MP_BC_STORE_NAME (MP_BC_BASE_QSTR_O + 0x06) // qstr
|
||||
#define MP_BC_STORE_GLOBAL (MP_BC_BASE_QSTR_O + 0x07) // qstr
|
||||
#define MP_BC_STORE_ATTR (MP_BC_BASE_QSTR_O + 0x08) // qstr
|
||||
#define MP_BC_STORE_SUBSCR (MP_BC_BASE_BYTE_O + 0x06)
|
||||
|
||||
#define MP_BC_DELETE_FAST (MP_BC_BASE_VINT_E + 0x08) // uint
|
||||
#define MP_BC_DELETE_DEREF (MP_BC_BASE_VINT_E + 0x09) // uint
|
||||
#define MP_BC_DELETE_NAME (MP_BC_BASE_QSTR_O + 0x09) // qstr
|
||||
#define MP_BC_DELETE_GLOBAL (MP_BC_BASE_QSTR_O + 0x0a) // qstr
|
||||
|
||||
#define MP_BC_DUP_TOP (MP_BC_BASE_BYTE_O + 0x07)
|
||||
#define MP_BC_DUP_TOP_TWO (MP_BC_BASE_BYTE_O + 0x08)
|
||||
#define MP_BC_POP_TOP (MP_BC_BASE_BYTE_O + 0x09)
|
||||
#define MP_BC_ROT_TWO (MP_BC_BASE_BYTE_O + 0x0a)
|
||||
#define MP_BC_ROT_THREE (MP_BC_BASE_BYTE_O + 0x0b)
|
||||
|
||||
#define MP_BC_JUMP (MP_BC_BASE_JUMP_E + 0x02) // rel byte code offset, 16-bit signed, in excess
|
||||
#define MP_BC_POP_JUMP_IF_TRUE (MP_BC_BASE_JUMP_E + 0x03) // rel byte code offset, 16-bit signed, in excess
|
||||
#define MP_BC_POP_JUMP_IF_FALSE (MP_BC_BASE_JUMP_E + 0x04) // rel byte code offset, 16-bit signed, in excess
|
||||
#define MP_BC_JUMP_IF_TRUE_OR_POP (MP_BC_BASE_JUMP_E + 0x05) // rel byte code offset, 16-bit signed, in excess
|
||||
#define MP_BC_JUMP_IF_FALSE_OR_POP (MP_BC_BASE_JUMP_E + 0x06) // rel byte code offset, 16-bit signed, in excess
|
||||
#define MP_BC_UNWIND_JUMP (MP_BC_BASE_JUMP_E + 0x00) // rel byte code offset, 16-bit signed, in excess; then a byte
|
||||
#define MP_BC_SETUP_WITH (MP_BC_BASE_JUMP_E + 0x07) // rel byte code offset, 16-bit unsigned
|
||||
#define MP_BC_SETUP_EXCEPT (MP_BC_BASE_JUMP_E + 0x08) // rel byte code offset, 16-bit unsigned
|
||||
#define MP_BC_SETUP_FINALLY (MP_BC_BASE_JUMP_E + 0x09) // rel byte code offset, 16-bit unsigned
|
||||
#define MP_BC_POP_EXCEPT_JUMP (MP_BC_BASE_JUMP_E + 0x0a) // rel byte code offset, 16-bit unsigned
|
||||
#define MP_BC_FOR_ITER (MP_BC_BASE_JUMP_E + 0x0b) // rel byte code offset, 16-bit unsigned
|
||||
#define MP_BC_WITH_CLEANUP (MP_BC_BASE_BYTE_O + 0x0c)
|
||||
#define MP_BC_END_FINALLY (MP_BC_BASE_BYTE_O + 0x0d)
|
||||
#define MP_BC_GET_ITER (MP_BC_BASE_BYTE_O + 0x0e)
|
||||
#define MP_BC_GET_ITER_STACK (MP_BC_BASE_BYTE_O + 0x0f)
|
||||
|
||||
#define MP_BC_BUILD_TUPLE (MP_BC_BASE_VINT_E + 0x0a) // uint
|
||||
#define MP_BC_BUILD_LIST (MP_BC_BASE_VINT_E + 0x0b) // uint
|
||||
#define MP_BC_BUILD_MAP (MP_BC_BASE_VINT_E + 0x0c) // uint
|
||||
#define MP_BC_STORE_MAP (MP_BC_BASE_BYTE_E + 0x02)
|
||||
#define MP_BC_BUILD_SET (MP_BC_BASE_VINT_E + 0x0d) // uint
|
||||
#define MP_BC_BUILD_SLICE (MP_BC_BASE_VINT_E + 0x0e) // uint
|
||||
#define MP_BC_STORE_COMP (MP_BC_BASE_VINT_E + 0x0f) // uint
|
||||
#define MP_BC_UNPACK_SEQUENCE (MP_BC_BASE_VINT_O + 0x00) // uint
|
||||
#define MP_BC_UNPACK_EX (MP_BC_BASE_VINT_O + 0x01) // uint
|
||||
|
||||
#define MP_BC_RETURN_VALUE (MP_BC_BASE_BYTE_E + 0x03)
|
||||
#define MP_BC_RAISE_LAST (MP_BC_BASE_BYTE_E + 0x04)
|
||||
#define MP_BC_RAISE_OBJ (MP_BC_BASE_BYTE_E + 0x05)
|
||||
#define MP_BC_RAISE_FROM (MP_BC_BASE_BYTE_E + 0x06)
|
||||
#define MP_BC_YIELD_VALUE (MP_BC_BASE_BYTE_E + 0x07)
|
||||
#define MP_BC_YIELD_FROM (MP_BC_BASE_BYTE_E + 0x08)
|
||||
|
||||
#define MP_BC_MAKE_FUNCTION (MP_BC_BASE_VINT_O + 0x02) // uint
|
||||
#define MP_BC_MAKE_FUNCTION_DEFARGS (MP_BC_BASE_VINT_O + 0x03) // uint
|
||||
#define MP_BC_MAKE_CLOSURE (MP_BC_BASE_VINT_E + 0x00) // uint; extra byte
|
||||
#define MP_BC_MAKE_CLOSURE_DEFARGS (MP_BC_BASE_VINT_E + 0x01) // uint; extra byte
|
||||
#define MP_BC_CALL_FUNCTION (MP_BC_BASE_VINT_O + 0x04) // uint
|
||||
#define MP_BC_CALL_FUNCTION_VAR_KW (MP_BC_BASE_VINT_O + 0x05) // uint
|
||||
#define MP_BC_CALL_METHOD (MP_BC_BASE_VINT_O + 0x06) // uint
|
||||
#define MP_BC_CALL_METHOD_VAR_KW (MP_BC_BASE_VINT_O + 0x07) // uint
|
||||
|
||||
#define MP_BC_IMPORT_NAME (MP_BC_BASE_QSTR_O + 0x0b) // qstr
|
||||
#define MP_BC_IMPORT_FROM (MP_BC_BASE_QSTR_O + 0x0c) // qstr
|
||||
#define MP_BC_IMPORT_STAR (MP_BC_BASE_BYTE_E + 0x09)
|
||||
|
||||
#endif // MICROPY_INCLUDED_PY_BC0_H
|
||||
@@ -0,0 +1,433 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2017 Paul Sokolovsky
|
||||
* Copyright (c) 2014-2019 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "py/binary.h"
|
||||
#include "py/smallint.h"
|
||||
#include "py/objint.h"
|
||||
#include "py/runtime.h"
|
||||
|
||||
// Helpers to work with binary-encoded data
|
||||
|
||||
#ifndef alignof
|
||||
#define alignof(type) offsetof(struct { char c; type t; }, t)
|
||||
#endif
|
||||
|
||||
size_t mp_binary_get_size(char struct_type, char val_type, size_t *palign) {
|
||||
size_t size = 0;
|
||||
int align = 1;
|
||||
switch (struct_type) {
|
||||
case '<':
|
||||
case '>':
|
||||
switch (val_type) {
|
||||
case 'b':
|
||||
case 'B':
|
||||
size = 1;
|
||||
break;
|
||||
case 'h':
|
||||
case 'H':
|
||||
size = 2;
|
||||
break;
|
||||
case 'i':
|
||||
case 'I':
|
||||
size = 4;
|
||||
break;
|
||||
case 'l':
|
||||
case 'L':
|
||||
size = 4;
|
||||
break;
|
||||
case 'q':
|
||||
case 'Q':
|
||||
size = 8;
|
||||
break;
|
||||
case 'P':
|
||||
case 'O':
|
||||
case 'S':
|
||||
size = sizeof(void *);
|
||||
break;
|
||||
case 'f':
|
||||
size = sizeof(float);
|
||||
break;
|
||||
case 'd':
|
||||
size = sizeof(double);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case '@': {
|
||||
// TODO:
|
||||
// The simplest heuristic for alignment is to align by value
|
||||
// size, but that doesn't work for "bigger than int" types,
|
||||
// for example, long long may very well have long alignment
|
||||
// So, we introduce separate alignment handling, but having
|
||||
// formal support for that is different from actually supporting
|
||||
// particular (or any) ABI.
|
||||
switch (val_type) {
|
||||
case BYTEARRAY_TYPECODE:
|
||||
case 'b':
|
||||
case 'B':
|
||||
align = size = 1;
|
||||
break;
|
||||
case 'h':
|
||||
case 'H':
|
||||
align = alignof(short);
|
||||
size = sizeof(short);
|
||||
break;
|
||||
case 'i':
|
||||
case 'I':
|
||||
align = alignof(int);
|
||||
size = sizeof(int);
|
||||
break;
|
||||
case 'l':
|
||||
case 'L':
|
||||
align = alignof(long);
|
||||
size = sizeof(long);
|
||||
break;
|
||||
case 'q':
|
||||
case 'Q':
|
||||
align = alignof(long long);
|
||||
size = sizeof(long long);
|
||||
break;
|
||||
case 'P':
|
||||
case 'O':
|
||||
case 'S':
|
||||
align = alignof(void *);
|
||||
size = sizeof(void *);
|
||||
break;
|
||||
case 'f':
|
||||
align = alignof(float);
|
||||
size = sizeof(float);
|
||||
break;
|
||||
case 'd':
|
||||
align = alignof(double);
|
||||
size = sizeof(double);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (size == 0) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("bad typecode"));
|
||||
}
|
||||
|
||||
if (palign != NULL) {
|
||||
*palign = align;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
mp_obj_t mp_binary_get_val_array(char typecode, void *p, size_t index) {
|
||||
mp_int_t val = 0;
|
||||
switch (typecode) {
|
||||
case 'b':
|
||||
val = ((signed char *)p)[index];
|
||||
break;
|
||||
case BYTEARRAY_TYPECODE:
|
||||
case 'B':
|
||||
val = ((unsigned char *)p)[index];
|
||||
break;
|
||||
case 'h':
|
||||
val = ((short *)p)[index];
|
||||
break;
|
||||
case 'H':
|
||||
val = ((unsigned short *)p)[index];
|
||||
break;
|
||||
case 'i':
|
||||
return mp_obj_new_int(((int *)p)[index]);
|
||||
case 'I':
|
||||
return mp_obj_new_int_from_uint(((unsigned int *)p)[index]);
|
||||
case 'l':
|
||||
return mp_obj_new_int(((long *)p)[index]);
|
||||
case 'L':
|
||||
return mp_obj_new_int_from_uint(((unsigned long *)p)[index]);
|
||||
#if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE
|
||||
case 'q':
|
||||
return mp_obj_new_int_from_ll(((long long *)p)[index]);
|
||||
case 'Q':
|
||||
return mp_obj_new_int_from_ull(((unsigned long long *)p)[index]);
|
||||
#endif
|
||||
#if MICROPY_PY_BUILTINS_FLOAT
|
||||
case 'f':
|
||||
return mp_obj_new_float_from_f(((float *)p)[index]);
|
||||
case 'd':
|
||||
return mp_obj_new_float_from_d(((double *)p)[index]);
|
||||
#endif
|
||||
// Extension to CPython: array of objects
|
||||
case 'O':
|
||||
return ((mp_obj_t *)p)[index];
|
||||
// Extension to CPython: array of pointers
|
||||
case 'P':
|
||||
return mp_obj_new_int((mp_int_t)(uintptr_t)((void **)p)[index]);
|
||||
}
|
||||
return MP_OBJ_NEW_SMALL_INT(val);
|
||||
}
|
||||
|
||||
// The long long type is guaranteed to hold at least 64 bits, and size is at
|
||||
// most 8 (for q and Q), so we will always be able to parse the given data
|
||||
// and fit it into a long long.
|
||||
long long mp_binary_get_int(size_t size, bool is_signed, bool big_endian, const byte *src) {
|
||||
int delta;
|
||||
if (!big_endian) {
|
||||
delta = -1;
|
||||
src += size - 1;
|
||||
} else {
|
||||
delta = 1;
|
||||
}
|
||||
|
||||
long long val = 0;
|
||||
if (is_signed && *src & 0x80) {
|
||||
val = -1;
|
||||
}
|
||||
for (uint i = 0; i < size; i++) {
|
||||
val <<= 8;
|
||||
val |= *src;
|
||||
src += delta;
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
#define is_signed(typecode) (typecode > 'Z')
|
||||
mp_obj_t mp_binary_get_val(char struct_type, char val_type, byte *p_base, byte **ptr) {
|
||||
byte *p = *ptr;
|
||||
size_t align;
|
||||
|
||||
size_t size = mp_binary_get_size(struct_type, val_type, &align);
|
||||
if (struct_type == '@') {
|
||||
// Align p relative to p_base
|
||||
p = p_base + (uintptr_t)MP_ALIGN(p - p_base, align);
|
||||
#if MP_ENDIANNESS_LITTLE
|
||||
struct_type = '<';
|
||||
#else
|
||||
struct_type = '>';
|
||||
#endif
|
||||
}
|
||||
*ptr = p + size;
|
||||
|
||||
long long val = mp_binary_get_int(size, is_signed(val_type), (struct_type == '>'), p);
|
||||
|
||||
if (val_type == 'O') {
|
||||
return (mp_obj_t)(mp_uint_t)val;
|
||||
} else if (val_type == 'S') {
|
||||
const char *s_val = (const char *)(uintptr_t)(mp_uint_t)val;
|
||||
return mp_obj_new_str(s_val, strlen(s_val));
|
||||
#if MICROPY_PY_BUILTINS_FLOAT
|
||||
} else if (val_type == 'f') {
|
||||
union { uint32_t i;
|
||||
float f;
|
||||
} fpu = {val};
|
||||
return mp_obj_new_float_from_f(fpu.f);
|
||||
} else if (val_type == 'd') {
|
||||
union { uint64_t i;
|
||||
double f;
|
||||
} fpu = {val};
|
||||
return mp_obj_new_float_from_d(fpu.f);
|
||||
#endif
|
||||
} else if (is_signed(val_type)) {
|
||||
if ((long long)MP_SMALL_INT_MIN <= val && val <= (long long)MP_SMALL_INT_MAX) {
|
||||
return mp_obj_new_int((mp_int_t)val);
|
||||
} else {
|
||||
return mp_obj_new_int_from_ll(val);
|
||||
}
|
||||
} else {
|
||||
if ((unsigned long long)val <= (unsigned long long)MP_SMALL_INT_MAX) {
|
||||
return mp_obj_new_int_from_uint((mp_uint_t)val);
|
||||
} else {
|
||||
return mp_obj_new_int_from_ull(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mp_binary_set_int(size_t val_sz, bool big_endian, byte *dest, mp_uint_t val) {
|
||||
if (MP_ENDIANNESS_LITTLE && !big_endian) {
|
||||
memcpy(dest, &val, val_sz);
|
||||
} else if (MP_ENDIANNESS_BIG && big_endian) {
|
||||
// only copy the least-significant val_sz bytes
|
||||
memcpy(dest, (byte *)&val + sizeof(mp_uint_t) - val_sz, val_sz);
|
||||
} else {
|
||||
const byte *src;
|
||||
if (MP_ENDIANNESS_LITTLE) {
|
||||
src = (const byte *)&val + val_sz;
|
||||
} else {
|
||||
src = (const byte *)&val + sizeof(mp_uint_t);
|
||||
}
|
||||
while (val_sz--) {
|
||||
*dest++ = *--src;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mp_binary_set_val(char struct_type, char val_type, mp_obj_t val_in, byte *p_base, byte **ptr) {
|
||||
byte *p = *ptr;
|
||||
size_t align;
|
||||
|
||||
size_t size = mp_binary_get_size(struct_type, val_type, &align);
|
||||
if (struct_type == '@') {
|
||||
// Align p relative to p_base
|
||||
p = p_base + (uintptr_t)MP_ALIGN(p - p_base, align);
|
||||
if (MP_ENDIANNESS_LITTLE) {
|
||||
struct_type = '<';
|
||||
} else {
|
||||
struct_type = '>';
|
||||
}
|
||||
}
|
||||
*ptr = p + size;
|
||||
|
||||
mp_uint_t val;
|
||||
switch (val_type) {
|
||||
case 'O':
|
||||
val = (mp_uint_t)val_in;
|
||||
break;
|
||||
#if MICROPY_PY_BUILTINS_FLOAT
|
||||
case 'f': {
|
||||
union { uint32_t i;
|
||||
float f;
|
||||
} fp_sp;
|
||||
fp_sp.f = mp_obj_get_float_to_f(val_in);
|
||||
val = fp_sp.i;
|
||||
break;
|
||||
}
|
||||
case 'd': {
|
||||
union { uint64_t i64;
|
||||
uint32_t i32[2];
|
||||
double f;
|
||||
} fp_dp;
|
||||
fp_dp.f = mp_obj_get_float_to_d(val_in);
|
||||
if (BYTES_PER_WORD == 8) {
|
||||
val = fp_dp.i64;
|
||||
} else {
|
||||
int be = struct_type == '>';
|
||||
mp_binary_set_int(sizeof(uint32_t), be, p, fp_dp.i32[MP_ENDIANNESS_BIG ^ be]);
|
||||
p += sizeof(uint32_t);
|
||||
val = fp_dp.i32[MP_ENDIANNESS_LITTLE ^ be];
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
#if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE
|
||||
if (mp_obj_is_type(val_in, &mp_type_int)) {
|
||||
mp_obj_int_to_bytes_impl(val_in, struct_type == '>', size, p);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
val = mp_obj_get_int(val_in);
|
||||
// zero/sign extend if needed
|
||||
if (BYTES_PER_WORD < 8 && size > sizeof(val)) {
|
||||
int c = (is_signed(val_type) && (mp_int_t)val < 0) ? 0xff : 0x00;
|
||||
memset(p, c, size);
|
||||
if (struct_type == '>') {
|
||||
p += size - sizeof(val);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
mp_binary_set_int(MIN((size_t)size, sizeof(val)), struct_type == '>', p, val);
|
||||
}
|
||||
|
||||
void mp_binary_set_val_array(char typecode, void *p, size_t index, mp_obj_t val_in) {
|
||||
switch (typecode) {
|
||||
#if MICROPY_PY_BUILTINS_FLOAT
|
||||
case 'f':
|
||||
((float *)p)[index] = mp_obj_get_float_to_f(val_in);
|
||||
break;
|
||||
case 'd':
|
||||
((double *)p)[index] = mp_obj_get_float_to_d(val_in);
|
||||
break;
|
||||
#endif
|
||||
// Extension to CPython: array of objects
|
||||
case 'O':
|
||||
((mp_obj_t *)p)[index] = val_in;
|
||||
break;
|
||||
default:
|
||||
#if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE
|
||||
if (mp_obj_is_type(val_in, &mp_type_int)) {
|
||||
size_t size = mp_binary_get_size('@', typecode, NULL);
|
||||
mp_obj_int_to_bytes_impl(val_in, MP_ENDIANNESS_BIG,
|
||||
size, (uint8_t *)p + index * size);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
mp_binary_set_val_array_from_int(typecode, p, index, mp_obj_get_int(val_in));
|
||||
}
|
||||
}
|
||||
|
||||
void mp_binary_set_val_array_from_int(char typecode, void *p, size_t index, mp_int_t val) {
|
||||
switch (typecode) {
|
||||
case 'b':
|
||||
((signed char *)p)[index] = val;
|
||||
break;
|
||||
case BYTEARRAY_TYPECODE:
|
||||
case 'B':
|
||||
((unsigned char *)p)[index] = val;
|
||||
break;
|
||||
case 'h':
|
||||
((short *)p)[index] = val;
|
||||
break;
|
||||
case 'H':
|
||||
((unsigned short *)p)[index] = val;
|
||||
break;
|
||||
case 'i':
|
||||
((int *)p)[index] = val;
|
||||
break;
|
||||
case 'I':
|
||||
((unsigned int *)p)[index] = val;
|
||||
break;
|
||||
case 'l':
|
||||
((long *)p)[index] = val;
|
||||
break;
|
||||
case 'L':
|
||||
((unsigned long *)p)[index] = val;
|
||||
break;
|
||||
#if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE
|
||||
case 'q':
|
||||
((long long *)p)[index] = val;
|
||||
break;
|
||||
case 'Q':
|
||||
((unsigned long long *)p)[index] = val;
|
||||
break;
|
||||
#endif
|
||||
#if MICROPY_PY_BUILTINS_FLOAT
|
||||
case 'f':
|
||||
((float *)p)[index] = (float)val;
|
||||
break;
|
||||
case 'd':
|
||||
((double *)p)[index] = (double)val;
|
||||
break;
|
||||
#endif
|
||||
// Extension to CPython: array of pointers
|
||||
case 'P':
|
||||
((void **)p)[index] = (void *)(uintptr_t)val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014 Paul Sokolovsky
|
||||
* Copyright (c) 2014-2017 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef MICROPY_INCLUDED_PY_BINARY_H
|
||||
#define MICROPY_INCLUDED_PY_BINARY_H
|
||||
|
||||
#include "py/obj.h"
|
||||
|
||||
// Use special typecode to differentiate repr() of bytearray vs array.array('B')
|
||||
// (underlyingly they're same). Can't use 0 here because that's used to detect
|
||||
// type-specification errors due to end-of-string.
|
||||
#define BYTEARRAY_TYPECODE 1
|
||||
|
||||
size_t mp_binary_get_size(char struct_type, char val_type, size_t *palign);
|
||||
mp_obj_t mp_binary_get_val_array(char typecode, void *p, size_t index);
|
||||
void mp_binary_set_val_array(char typecode, void *p, size_t index, mp_obj_t val_in);
|
||||
void mp_binary_set_val_array_from_int(char typecode, void *p, size_t index, mp_int_t val);
|
||||
mp_obj_t mp_binary_get_val(char struct_type, char val_type, byte *p_base, byte **ptr);
|
||||
void mp_binary_set_val(char struct_type, char val_type, mp_obj_t val_in, byte *p_base, byte **ptr);
|
||||
long long mp_binary_get_int(size_t size, bool is_signed, bool big_endian, const byte *src);
|
||||
void mp_binary_set_int(size_t val_sz, bool big_endian, byte *dest, mp_uint_t val);
|
||||
|
||||
#endif // MICROPY_INCLUDED_PY_BINARY_H
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013, 2014 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef MICROPY_INCLUDED_PY_BUILTIN_H
|
||||
#define MICROPY_INCLUDED_PY_BUILTIN_H
|
||||
|
||||
#include "py/obj.h"
|
||||
|
||||
mp_obj_t mp_builtin___import__(size_t n_args, const mp_obj_t *args);
|
||||
mp_obj_t mp_builtin_open(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs);
|
||||
mp_obj_t mp_micropython_mem_info(size_t n_args, const mp_obj_t *args);
|
||||
|
||||
MP_DECLARE_CONST_FUN_OBJ_VAR(mp_builtin___build_class___obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin___import___obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(mp_builtin___repl_print___obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(mp_builtin_abs_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(mp_builtin_all_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(mp_builtin_any_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(mp_builtin_bin_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(mp_builtin_callable_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_compile_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(mp_builtin_chr_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_2(mp_builtin_delattr_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_dir_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_2(mp_builtin_divmod_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_eval_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_exec_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_execfile_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_getattr_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_3(mp_builtin_setattr_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_0(mp_builtin_globals_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_2(mp_builtin_hasattr_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(mp_builtin_hash_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_help_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(mp_builtin_hex_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(mp_builtin_id_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_2(mp_builtin_isinstance_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_2(mp_builtin_issubclass_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(mp_builtin_iter_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(mp_builtin_len_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_0(mp_builtin_locals_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(mp_builtin_max_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(mp_builtin_min_obj);
|
||||
#if MICROPY_PY_BUILTINS_NEXT2
|
||||
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_next_obj);
|
||||
#else
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(mp_builtin_next_obj);
|
||||
#endif
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(mp_builtin_oct_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(mp_builtin_ord_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_pow_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(mp_builtin_print_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_1(mp_builtin_repr_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_round_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(mp_builtin_sorted_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_sum_obj);
|
||||
// Defined by a port, but declared here for simplicity
|
||||
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_input_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_KW(mp_builtin_open_obj);
|
||||
|
||||
MP_DECLARE_CONST_FUN_OBJ_2(mp_namedtuple_obj);
|
||||
|
||||
MP_DECLARE_CONST_FUN_OBJ_2(mp_op_contains_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_2(mp_op_getitem_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_3(mp_op_setitem_obj);
|
||||
MP_DECLARE_CONST_FUN_OBJ_2(mp_op_delitem_obj);
|
||||
|
||||
extern const mp_obj_module_t mp_module___main__;
|
||||
extern const mp_obj_module_t mp_module_builtins;
|
||||
extern const mp_obj_module_t mp_module_uarray;
|
||||
extern const mp_obj_module_t mp_module_collections;
|
||||
extern const mp_obj_module_t mp_module_io;
|
||||
extern const mp_obj_module_t mp_module_math;
|
||||
extern const mp_obj_module_t mp_module_cmath;
|
||||
extern const mp_obj_module_t mp_module_micropython;
|
||||
extern const mp_obj_module_t mp_module_ustruct;
|
||||
extern const mp_obj_module_t mp_module_sys;
|
||||
extern const mp_obj_module_t mp_module_gc;
|
||||
extern const mp_obj_module_t mp_module_thread;
|
||||
|
||||
extern const mp_obj_dict_t mp_module_builtins_globals;
|
||||
|
||||
// extmod modules
|
||||
extern const mp_obj_module_t mp_module_uasyncio;
|
||||
extern const mp_obj_module_t mp_module_uerrno;
|
||||
extern const mp_obj_module_t mp_module_uctypes;
|
||||
extern const mp_obj_module_t mp_module_uzlib;
|
||||
extern const mp_obj_module_t mp_module_ujson;
|
||||
extern const mp_obj_module_t mp_module_ure;
|
||||
extern const mp_obj_module_t mp_module_uheapq;
|
||||
extern const mp_obj_module_t mp_module_uhashlib;
|
||||
extern const mp_obj_module_t mp_module_ucryptolib;
|
||||
extern const mp_obj_module_t mp_module_ubinascii;
|
||||
extern const mp_obj_module_t mp_module_urandom;
|
||||
extern const mp_obj_module_t mp_module_uselect;
|
||||
extern const mp_obj_module_t mp_module_ussl;
|
||||
extern const mp_obj_module_t mp_module_utimeq;
|
||||
extern const mp_obj_module_t mp_module_machine;
|
||||
extern const mp_obj_module_t mp_module_lwip;
|
||||
extern const mp_obj_module_t mp_module_uwebsocket;
|
||||
extern const mp_obj_module_t mp_module_webrepl;
|
||||
extern const mp_obj_module_t mp_module_framebuf;
|
||||
extern const mp_obj_module_t mp_module_btree;
|
||||
extern const mp_obj_module_t mp_module_ubluetooth;
|
||||
|
||||
extern const char MICROPY_PY_BUILTINS_HELP_TEXT[];
|
||||
|
||||
#endif // MICROPY_INCLUDED_PY_BUILTIN_H
|
||||
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013, 2014 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "py/objfun.h"
|
||||
#include "py/compile.h"
|
||||
#include "py/runtime.h"
|
||||
#include "py/builtin.h"
|
||||
|
||||
#if MICROPY_PY_BUILTINS_COMPILE
|
||||
|
||||
typedef struct _mp_obj_code_t {
|
||||
mp_obj_base_t base;
|
||||
mp_obj_t module_fun;
|
||||
} mp_obj_code_t;
|
||||
|
||||
STATIC const mp_obj_type_t mp_type_code = {
|
||||
{ &mp_type_type },
|
||||
.name = MP_QSTR_code,
|
||||
};
|
||||
|
||||
STATIC mp_obj_t code_execute(mp_obj_code_t *self, mp_obj_dict_t *globals, mp_obj_dict_t *locals) {
|
||||
// save context and set new context
|
||||
mp_obj_dict_t *old_globals = mp_globals_get();
|
||||
mp_obj_dict_t *old_locals = mp_locals_get();
|
||||
mp_globals_set(globals);
|
||||
mp_locals_set(locals);
|
||||
|
||||
// a bit of a hack: fun_bc will re-set globals, so need to make sure it's
|
||||
// the correct one
|
||||
if (mp_obj_is_type(self->module_fun, &mp_type_fun_bc)) {
|
||||
mp_obj_fun_bc_t *fun_bc = MP_OBJ_TO_PTR(self->module_fun);
|
||||
fun_bc->globals = globals;
|
||||
}
|
||||
|
||||
// execute code
|
||||
nlr_buf_t nlr;
|
||||
if (nlr_push(&nlr) == 0) {
|
||||
mp_obj_t ret = mp_call_function_0(self->module_fun);
|
||||
nlr_pop();
|
||||
mp_globals_set(old_globals);
|
||||
mp_locals_set(old_locals);
|
||||
return ret;
|
||||
} else {
|
||||
// exception; restore context and re-raise same exception
|
||||
mp_globals_set(old_globals);
|
||||
mp_locals_set(old_locals);
|
||||
nlr_jump(nlr.ret_val);
|
||||
}
|
||||
}
|
||||
|
||||
STATIC mp_obj_t mp_builtin_compile(size_t n_args, const mp_obj_t *args) {
|
||||
(void)n_args;
|
||||
|
||||
// get the source
|
||||
size_t str_len;
|
||||
const char *str = mp_obj_str_get_data(args[0], &str_len);
|
||||
|
||||
// get the filename
|
||||
qstr filename = mp_obj_str_get_qstr(args[1]);
|
||||
|
||||
// create the lexer
|
||||
mp_lexer_t *lex = mp_lexer_new_from_str_len(filename, str, str_len, 0);
|
||||
|
||||
// get the compile mode
|
||||
qstr mode = mp_obj_str_get_qstr(args[2]);
|
||||
mp_parse_input_kind_t parse_input_kind;
|
||||
switch (mode) {
|
||||
case MP_QSTR_single:
|
||||
parse_input_kind = MP_PARSE_SINGLE_INPUT;
|
||||
break;
|
||||
case MP_QSTR_exec:
|
||||
parse_input_kind = MP_PARSE_FILE_INPUT;
|
||||
break;
|
||||
case MP_QSTR_eval:
|
||||
parse_input_kind = MP_PARSE_EVAL_INPUT;
|
||||
break;
|
||||
default:
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("bad compile mode"));
|
||||
}
|
||||
|
||||
mp_obj_code_t *code = m_new_obj(mp_obj_code_t);
|
||||
code->base.type = &mp_type_code;
|
||||
code->module_fun = mp_parse_compile_execute(lex, parse_input_kind, NULL, NULL);
|
||||
return MP_OBJ_FROM_PTR(code);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_compile_obj, 3, 6, mp_builtin_compile);
|
||||
|
||||
#endif // MICROPY_PY_BUILTINS_COMPILE
|
||||
|
||||
#if MICROPY_PY_BUILTINS_EVAL_EXEC
|
||||
|
||||
STATIC mp_obj_t eval_exec_helper(size_t n_args, const mp_obj_t *args, mp_parse_input_kind_t parse_input_kind) {
|
||||
// work out the context
|
||||
mp_obj_dict_t *globals = mp_globals_get();
|
||||
mp_obj_dict_t *locals = mp_locals_get();
|
||||
for (size_t i = 1; i < 3 && i < n_args; ++i) {
|
||||
if (args[i] != mp_const_none) {
|
||||
if (!mp_obj_is_type(args[i], &mp_type_dict)) {
|
||||
mp_raise_TypeError(NULL);
|
||||
}
|
||||
locals = MP_OBJ_TO_PTR(args[i]);
|
||||
if (i == 1) {
|
||||
globals = locals;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if MICROPY_PY_BUILTINS_COMPILE
|
||||
if (mp_obj_is_type(args[0], &mp_type_code)) {
|
||||
return code_execute(MP_OBJ_TO_PTR(args[0]), globals, locals);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Extract the source code.
|
||||
mp_buffer_info_t bufinfo;
|
||||
mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_READ);
|
||||
|
||||
// create the lexer
|
||||
// MP_PARSE_SINGLE_INPUT is used to indicate a file input
|
||||
mp_lexer_t *lex;
|
||||
if (MICROPY_PY_BUILTINS_EXECFILE && parse_input_kind == MP_PARSE_SINGLE_INPUT) {
|
||||
lex = mp_lexer_new_from_file(bufinfo.buf);
|
||||
parse_input_kind = MP_PARSE_FILE_INPUT;
|
||||
} else {
|
||||
lex = mp_lexer_new_from_str_len(MP_QSTR__lt_string_gt_, bufinfo.buf, bufinfo.len, 0);
|
||||
}
|
||||
|
||||
return mp_parse_compile_execute(lex, parse_input_kind, globals, locals);
|
||||
}
|
||||
|
||||
STATIC mp_obj_t mp_builtin_eval(size_t n_args, const mp_obj_t *args) {
|
||||
return eval_exec_helper(n_args, args, MP_PARSE_EVAL_INPUT);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_eval_obj, 1, 3, mp_builtin_eval);
|
||||
|
||||
STATIC mp_obj_t mp_builtin_exec(size_t n_args, const mp_obj_t *args) {
|
||||
return eval_exec_helper(n_args, args, MP_PARSE_FILE_INPUT);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_exec_obj, 1, 3, mp_builtin_exec);
|
||||
|
||||
#endif // MICROPY_PY_BUILTINS_EVAL_EXEC
|
||||
|
||||
#if MICROPY_PY_BUILTINS_EXECFILE
|
||||
STATIC mp_obj_t mp_builtin_execfile(size_t n_args, const mp_obj_t *args) {
|
||||
// MP_PARSE_SINGLE_INPUT is used to indicate a file input
|
||||
return eval_exec_helper(n_args, args, MP_PARSE_SINGLE_INPUT);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_execfile_obj, 1, 3, mp_builtin_execfile);
|
||||
#endif
|
||||
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013-2016 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "py/builtin.h"
|
||||
#include "py/objmodule.h"
|
||||
|
||||
#if MICROPY_PY_BUILTINS_HELP
|
||||
|
||||
const char mp_help_default_text[] =
|
||||
"Welcome to MicroPython!\n"
|
||||
"\n"
|
||||
"For online docs please visit http://docs.micropython.org/\n"
|
||||
"\n"
|
||||
"Control commands:\n"
|
||||
" CTRL-A -- on a blank line, enter raw REPL mode\n"
|
||||
" CTRL-B -- on a blank line, enter normal REPL mode\n"
|
||||
" CTRL-C -- interrupt a running program\n"
|
||||
" CTRL-D -- on a blank line, exit or do a soft reset\n"
|
||||
" CTRL-E -- on a blank line, enter paste mode\n"
|
||||
"\n"
|
||||
"For further help on a specific object, type help(obj)\n"
|
||||
;
|
||||
|
||||
STATIC void mp_help_print_info_about_object(mp_obj_t name_o, mp_obj_t value) {
|
||||
mp_print_str(MP_PYTHON_PRINTER, " ");
|
||||
mp_obj_print(name_o, PRINT_STR);
|
||||
mp_print_str(MP_PYTHON_PRINTER, " -- ");
|
||||
mp_obj_print(value, PRINT_STR);
|
||||
mp_print_str(MP_PYTHON_PRINTER, "\n");
|
||||
}
|
||||
|
||||
#if MICROPY_PY_BUILTINS_HELP_MODULES
|
||||
STATIC void mp_help_add_from_map(mp_obj_t list, const mp_map_t *map) {
|
||||
for (size_t i = 0; i < map->alloc; i++) {
|
||||
if (mp_map_slot_is_filled(map, i)) {
|
||||
mp_obj_list_append(list, map->table[i].key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if MICROPY_MODULE_FROZEN
|
||||
STATIC void mp_help_add_from_names(mp_obj_t list, const char *name) {
|
||||
while (*name) {
|
||||
size_t l = strlen(name);
|
||||
// name should end in '.py' and we strip it off
|
||||
mp_obj_list_append(list, mp_obj_new_str(name, l - 3));
|
||||
name += l + 1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
STATIC void mp_help_print_modules(void) {
|
||||
mp_obj_t list = mp_obj_new_list(0, NULL);
|
||||
|
||||
mp_help_add_from_map(list, &mp_builtin_module_map);
|
||||
|
||||
#if MICROPY_MODULE_FROZEN_STR
|
||||
extern const char mp_frozen_str_names[];
|
||||
mp_help_add_from_names(list, mp_frozen_str_names);
|
||||
#endif
|
||||
|
||||
#if MICROPY_MODULE_FROZEN_MPY
|
||||
extern const char mp_frozen_mpy_names[];
|
||||
mp_help_add_from_names(list, mp_frozen_mpy_names);
|
||||
#endif
|
||||
|
||||
// sort the list so it's printed in alphabetical order
|
||||
mp_obj_list_sort(1, &list, (mp_map_t *)&mp_const_empty_map);
|
||||
|
||||
// print the list of modules in a column-first order
|
||||
#define NUM_COLUMNS (4)
|
||||
#define COLUMN_WIDTH (18)
|
||||
size_t len;
|
||||
mp_obj_t *items;
|
||||
mp_obj_list_get(list, &len, &items);
|
||||
unsigned int num_rows = (len + NUM_COLUMNS - 1) / NUM_COLUMNS;
|
||||
for (unsigned int i = 0; i < num_rows; ++i) {
|
||||
unsigned int j = i;
|
||||
for (;;) {
|
||||
int l = mp_print_str(MP_PYTHON_PRINTER, mp_obj_str_get_str(items[j]));
|
||||
j += num_rows;
|
||||
if (j >= len) {
|
||||
break;
|
||||
}
|
||||
int gap = COLUMN_WIDTH - l;
|
||||
while (gap < 1) {
|
||||
gap += COLUMN_WIDTH;
|
||||
}
|
||||
while (gap--) {
|
||||
mp_print_str(MP_PYTHON_PRINTER, " ");
|
||||
}
|
||||
}
|
||||
mp_print_str(MP_PYTHON_PRINTER, "\n");
|
||||
}
|
||||
|
||||
#if MICROPY_ENABLE_EXTERNAL_IMPORT
|
||||
// let the user know there may be other modules available from the filesystem
|
||||
mp_print_str(MP_PYTHON_PRINTER, "Plus any modules on the filesystem\n");
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
STATIC void mp_help_print_obj(const mp_obj_t obj) {
|
||||
#if MICROPY_PY_BUILTINS_HELP_MODULES
|
||||
if (obj == MP_OBJ_NEW_QSTR(MP_QSTR_modules)) {
|
||||
mp_help_print_modules();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
const mp_obj_type_t *type = mp_obj_get_type(obj);
|
||||
|
||||
// try to print something sensible about the given object
|
||||
mp_print_str(MP_PYTHON_PRINTER, "object ");
|
||||
mp_obj_print(obj, PRINT_STR);
|
||||
mp_printf(MP_PYTHON_PRINTER, " is of type %q\n", type->name);
|
||||
|
||||
mp_map_t *map = NULL;
|
||||
if (type == &mp_type_module) {
|
||||
map = &mp_obj_module_get_globals(obj)->map;
|
||||
} else {
|
||||
if (type == &mp_type_type) {
|
||||
type = MP_OBJ_TO_PTR(obj);
|
||||
}
|
||||
if (type->locals_dict != NULL) {
|
||||
map = &type->locals_dict->map;
|
||||
}
|
||||
}
|
||||
if (map != NULL) {
|
||||
for (uint i = 0; i < map->alloc; i++) {
|
||||
if (map->table[i].key != MP_OBJ_NULL) {
|
||||
mp_help_print_info_about_object(map->table[i].key, map->table[i].value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
STATIC mp_obj_t mp_builtin_help(size_t n_args, const mp_obj_t *args) {
|
||||
if (n_args == 0) {
|
||||
// print a general help message
|
||||
mp_print_str(MP_PYTHON_PRINTER, MICROPY_PY_BUILTINS_HELP_TEXT);
|
||||
} else {
|
||||
// try to print something sensible about the given object
|
||||
mp_help_print_obj(args[0]);
|
||||
}
|
||||
|
||||
return mp_const_none;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_help_obj, 0, 1, mp_builtin_help);
|
||||
|
||||
#endif // MICROPY_PY_BUILTINS_HELP
|
||||
@@ -0,0 +1,511 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013-2019 Damien P. George
|
||||
* Copyright (c) 2014 Paul Sokolovsky
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "py/compile.h"
|
||||
#include "py/objmodule.h"
|
||||
#include "py/persistentcode.h"
|
||||
#include "py/runtime.h"
|
||||
#include "py/builtin.h"
|
||||
#include "py/frozenmod.h"
|
||||
|
||||
#if MICROPY_DEBUG_VERBOSE // print debugging info
|
||||
#define DEBUG_PRINT (1)
|
||||
#define DEBUG_printf DEBUG_printf
|
||||
#else // don't print debugging info
|
||||
#define DEBUG_PRINT (0)
|
||||
#define DEBUG_printf(...) (void)0
|
||||
#endif
|
||||
|
||||
#if MICROPY_ENABLE_EXTERNAL_IMPORT
|
||||
|
||||
#define PATH_SEP_CHAR '/'
|
||||
|
||||
bool mp_obj_is_package(mp_obj_t module) {
|
||||
mp_obj_t dest[2];
|
||||
mp_load_method_maybe(module, MP_QSTR___path__, dest);
|
||||
return dest[0] != MP_OBJ_NULL;
|
||||
}
|
||||
|
||||
// Stat either frozen or normal module by a given path
|
||||
// (whatever is available, if at all).
|
||||
STATIC mp_import_stat_t mp_import_stat_any(const char *path) {
|
||||
#if MICROPY_MODULE_FROZEN
|
||||
mp_import_stat_t st = mp_frozen_stat(path);
|
||||
if (st != MP_IMPORT_STAT_NO_EXIST) {
|
||||
return st;
|
||||
}
|
||||
#endif
|
||||
return mp_import_stat(path);
|
||||
}
|
||||
|
||||
STATIC mp_import_stat_t stat_file_py_or_mpy(vstr_t *path) {
|
||||
mp_import_stat_t stat = mp_import_stat_any(vstr_null_terminated_str(path));
|
||||
if (stat == MP_IMPORT_STAT_FILE) {
|
||||
return stat;
|
||||
}
|
||||
|
||||
#if MICROPY_PERSISTENT_CODE_LOAD
|
||||
vstr_ins_byte(path, path->len - 2, 'm');
|
||||
stat = mp_import_stat_any(vstr_null_terminated_str(path));
|
||||
if (stat == MP_IMPORT_STAT_FILE) {
|
||||
return stat;
|
||||
}
|
||||
#endif
|
||||
|
||||
return MP_IMPORT_STAT_NO_EXIST;
|
||||
}
|
||||
|
||||
STATIC mp_import_stat_t stat_dir_or_file(vstr_t *path) {
|
||||
mp_import_stat_t stat = mp_import_stat_any(vstr_null_terminated_str(path));
|
||||
DEBUG_printf("stat %s: %d\n", vstr_str(path), stat);
|
||||
if (stat == MP_IMPORT_STAT_DIR) {
|
||||
return stat;
|
||||
}
|
||||
|
||||
// not a directory, add .py and try as a file
|
||||
vstr_add_str(path, ".py");
|
||||
return stat_file_py_or_mpy(path);
|
||||
}
|
||||
|
||||
STATIC mp_import_stat_t find_file(const char *file_str, uint file_len, vstr_t *dest) {
|
||||
#if MICROPY_PY_SYS
|
||||
// extract the list of paths
|
||||
size_t path_num;
|
||||
mp_obj_t *path_items;
|
||||
mp_obj_list_get(mp_sys_path, &path_num, &path_items);
|
||||
|
||||
if (path_num != 0) {
|
||||
// go through each path looking for a directory or file
|
||||
for (size_t i = 0; i < path_num; i++) {
|
||||
vstr_reset(dest);
|
||||
size_t p_len;
|
||||
const char *p = mp_obj_str_get_data(path_items[i], &p_len);
|
||||
if (p_len > 0) {
|
||||
vstr_add_strn(dest, p, p_len);
|
||||
vstr_add_char(dest, PATH_SEP_CHAR);
|
||||
}
|
||||
vstr_add_strn(dest, file_str, file_len);
|
||||
mp_import_stat_t stat = stat_dir_or_file(dest);
|
||||
if (stat != MP_IMPORT_STAT_NO_EXIST) {
|
||||
return stat;
|
||||
}
|
||||
}
|
||||
|
||||
// could not find a directory or file
|
||||
return MP_IMPORT_STAT_NO_EXIST;
|
||||
}
|
||||
#endif
|
||||
|
||||
// mp_sys_path is empty, so just use the given file name
|
||||
vstr_add_strn(dest, file_str, file_len);
|
||||
return stat_dir_or_file(dest);
|
||||
}
|
||||
|
||||
#if MICROPY_MODULE_FROZEN_STR || MICROPY_ENABLE_COMPILER
|
||||
STATIC void do_load_from_lexer(mp_obj_t module_obj, mp_lexer_t *lex) {
|
||||
#if MICROPY_PY___FILE__
|
||||
qstr source_name = lex->source_name;
|
||||
mp_store_attr(module_obj, MP_QSTR___file__, MP_OBJ_NEW_QSTR(source_name));
|
||||
#endif
|
||||
|
||||
// parse, compile and execute the module in its context
|
||||
mp_obj_dict_t *mod_globals = mp_obj_module_get_globals(module_obj);
|
||||
mp_parse_compile_execute(lex, MP_PARSE_FILE_INPUT, mod_globals, mod_globals);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if MICROPY_PERSISTENT_CODE_LOAD || MICROPY_MODULE_FROZEN_MPY
|
||||
STATIC void do_execute_raw_code(mp_obj_t module_obj, mp_raw_code_t *raw_code, const char *source_name) {
|
||||
(void)source_name;
|
||||
|
||||
#if MICROPY_PY___FILE__
|
||||
mp_store_attr(module_obj, MP_QSTR___file__, MP_OBJ_NEW_QSTR(qstr_from_str(source_name)));
|
||||
#endif
|
||||
|
||||
// execute the module in its context
|
||||
mp_obj_dict_t *mod_globals = mp_obj_module_get_globals(module_obj);
|
||||
|
||||
// save context
|
||||
mp_obj_dict_t *volatile old_globals = mp_globals_get();
|
||||
mp_obj_dict_t *volatile old_locals = mp_locals_get();
|
||||
|
||||
// set new context
|
||||
mp_globals_set(mod_globals);
|
||||
mp_locals_set(mod_globals);
|
||||
|
||||
nlr_buf_t nlr;
|
||||
if (nlr_push(&nlr) == 0) {
|
||||
mp_obj_t module_fun = mp_make_function_from_raw_code(raw_code, MP_OBJ_NULL, MP_OBJ_NULL);
|
||||
mp_call_function_0(module_fun);
|
||||
|
||||
// finish nlr block, restore context
|
||||
nlr_pop();
|
||||
mp_globals_set(old_globals);
|
||||
mp_locals_set(old_locals);
|
||||
} else {
|
||||
// exception; restore context and re-raise same exception
|
||||
mp_globals_set(old_globals);
|
||||
mp_locals_set(old_locals);
|
||||
nlr_jump(nlr.ret_val);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
STATIC void do_load(mp_obj_t module_obj, vstr_t *file) {
|
||||
#if MICROPY_MODULE_FROZEN || MICROPY_ENABLE_COMPILER || (MICROPY_PERSISTENT_CODE_LOAD && MICROPY_HAS_FILE_READER)
|
||||
char *file_str = vstr_null_terminated_str(file);
|
||||
#endif
|
||||
|
||||
// If we support frozen modules (either as str or mpy) then try to find the
|
||||
// requested filename in the list of frozen module filenames.
|
||||
#if MICROPY_MODULE_FROZEN
|
||||
void *modref;
|
||||
int frozen_type = mp_find_frozen_module(file_str, file->len, &modref);
|
||||
#endif
|
||||
|
||||
// If we support frozen str modules and the compiler is enabled, and we
|
||||
// found the filename in the list of frozen files, then load and execute it.
|
||||
#if MICROPY_MODULE_FROZEN_STR
|
||||
if (frozen_type == MP_FROZEN_STR) {
|
||||
do_load_from_lexer(module_obj, modref);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// If we support frozen mpy modules and we found a corresponding file (and
|
||||
// its data) in the list of frozen files, execute it.
|
||||
#if MICROPY_MODULE_FROZEN_MPY
|
||||
if (frozen_type == MP_FROZEN_MPY) {
|
||||
do_execute_raw_code(module_obj, modref, file_str);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// If we support loading .mpy files then check if the file extension is of
|
||||
// the correct format and, if so, load and execute the file.
|
||||
#if MICROPY_HAS_FILE_READER && MICROPY_PERSISTENT_CODE_LOAD
|
||||
if (file_str[file->len - 3] == 'm') {
|
||||
mp_raw_code_t *raw_code = mp_raw_code_load_file(file_str);
|
||||
do_execute_raw_code(module_obj, raw_code, file_str);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// If we can compile scripts then load the file and compile and execute it.
|
||||
#if MICROPY_ENABLE_COMPILER
|
||||
{
|
||||
mp_lexer_t *lex = mp_lexer_new_from_file(file_str);
|
||||
do_load_from_lexer(module_obj, lex);
|
||||
return;
|
||||
}
|
||||
#else
|
||||
// If we get here then the file was not frozen and we can't compile scripts.
|
||||
mp_raise_msg(&mp_type_ImportError, MP_ERROR_TEXT("script compilation not supported"));
|
||||
#endif
|
||||
}
|
||||
|
||||
STATIC void chop_component(const char *start, const char **end) {
|
||||
const char *p = *end;
|
||||
while (p > start) {
|
||||
if (*--p == '.') {
|
||||
*end = p;
|
||||
return;
|
||||
}
|
||||
}
|
||||
*end = p;
|
||||
}
|
||||
|
||||
mp_obj_t mp_builtin___import__(size_t n_args, const mp_obj_t *args) {
|
||||
#if DEBUG_PRINT
|
||||
DEBUG_printf("__import__:\n");
|
||||
for (size_t i = 0; i < n_args; i++) {
|
||||
DEBUG_printf(" ");
|
||||
mp_obj_print(args[i], PRINT_REPR);
|
||||
DEBUG_printf("\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
mp_obj_t module_name = args[0];
|
||||
mp_obj_t fromtuple = mp_const_none;
|
||||
mp_int_t level = 0;
|
||||
if (n_args >= 4) {
|
||||
fromtuple = args[3];
|
||||
if (n_args >= 5) {
|
||||
level = MP_OBJ_SMALL_INT_VALUE(args[4]);
|
||||
if (level < 0) {
|
||||
mp_raise_ValueError(NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t mod_len;
|
||||
const char *mod_str = mp_obj_str_get_data(module_name, &mod_len);
|
||||
|
||||
if (level != 0) {
|
||||
// What we want to do here is to take name of current module,
|
||||
// chop <level> trailing components, and concatenate with passed-in
|
||||
// module name, thus resolving relative import name into absolute.
|
||||
// This even appears to be correct per
|
||||
// http://legacy.python.org/dev/peps/pep-0328/#relative-imports-and-name
|
||||
// "Relative imports use a module's __name__ attribute to determine that
|
||||
// module's position in the package hierarchy."
|
||||
level--;
|
||||
mp_obj_t this_name_q = mp_obj_dict_get(MP_OBJ_FROM_PTR(mp_globals_get()), MP_OBJ_NEW_QSTR(MP_QSTR___name__));
|
||||
assert(this_name_q != MP_OBJ_NULL);
|
||||
#if MICROPY_CPYTHON_COMPAT
|
||||
if (MP_OBJ_QSTR_VALUE(this_name_q) == MP_QSTR___main__) {
|
||||
// This is a module run by -m command-line switch, get its real name from backup attribute
|
||||
this_name_q = mp_obj_dict_get(MP_OBJ_FROM_PTR(mp_globals_get()), MP_OBJ_NEW_QSTR(MP_QSTR___main__));
|
||||
}
|
||||
#endif
|
||||
mp_map_t *globals_map = &mp_globals_get()->map;
|
||||
mp_map_elem_t *elem = mp_map_lookup(globals_map, MP_OBJ_NEW_QSTR(MP_QSTR___path__), MP_MAP_LOOKUP);
|
||||
bool is_pkg = (elem != NULL);
|
||||
|
||||
#if DEBUG_PRINT
|
||||
DEBUG_printf("Current module/package: ");
|
||||
mp_obj_print(this_name_q, PRINT_REPR);
|
||||
DEBUG_printf(", is_package: %d", is_pkg);
|
||||
DEBUG_printf("\n");
|
||||
#endif
|
||||
|
||||
size_t this_name_l;
|
||||
const char *this_name = mp_obj_str_get_data(this_name_q, &this_name_l);
|
||||
|
||||
const char *p = this_name + this_name_l;
|
||||
if (!is_pkg) {
|
||||
// We have module, but relative imports are anchored at package, so
|
||||
// go there.
|
||||
chop_component(this_name, &p);
|
||||
}
|
||||
|
||||
while (level--) {
|
||||
chop_component(this_name, &p);
|
||||
}
|
||||
|
||||
// We must have some component left over to import from
|
||||
if (p == this_name) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("can't perform relative import"));
|
||||
}
|
||||
|
||||
uint new_mod_l = (mod_len == 0 ? (size_t)(p - this_name) : (size_t)(p - this_name) + 1 + mod_len);
|
||||
char *new_mod = mp_local_alloc(new_mod_l);
|
||||
memcpy(new_mod, this_name, p - this_name);
|
||||
if (mod_len != 0) {
|
||||
new_mod[p - this_name] = '.';
|
||||
memcpy(new_mod + (p - this_name) + 1, mod_str, mod_len);
|
||||
}
|
||||
|
||||
qstr new_mod_q = qstr_from_strn(new_mod, new_mod_l);
|
||||
mp_local_free(new_mod);
|
||||
DEBUG_printf("Resolved base name for relative import: '%s'\n", qstr_str(new_mod_q));
|
||||
module_name = MP_OBJ_NEW_QSTR(new_mod_q);
|
||||
mod_str = qstr_str(new_mod_q);
|
||||
mod_len = new_mod_l;
|
||||
}
|
||||
|
||||
if (mod_len == 0) {
|
||||
mp_raise_ValueError(NULL);
|
||||
}
|
||||
|
||||
// check if module already exists
|
||||
qstr module_name_qstr = mp_obj_str_get_qstr(module_name);
|
||||
mp_obj_t module_obj = mp_module_get(module_name_qstr);
|
||||
if (module_obj != MP_OBJ_NULL) {
|
||||
DEBUG_printf("Module already loaded\n");
|
||||
// If it's not a package, return module right away
|
||||
char *p = strchr(mod_str, '.');
|
||||
if (p == NULL) {
|
||||
return module_obj;
|
||||
}
|
||||
// If fromlist is not empty, return leaf module
|
||||
if (fromtuple != mp_const_none) {
|
||||
return module_obj;
|
||||
}
|
||||
// Otherwise, we need to return top-level package
|
||||
qstr pkg_name = qstr_from_strn(mod_str, p - mod_str);
|
||||
return mp_module_get(pkg_name);
|
||||
}
|
||||
DEBUG_printf("Module not yet loaded\n");
|
||||
|
||||
uint last = 0;
|
||||
VSTR_FIXED(path, MICROPY_ALLOC_PATH_MAX)
|
||||
module_obj = MP_OBJ_NULL;
|
||||
mp_obj_t top_module_obj = MP_OBJ_NULL;
|
||||
mp_obj_t outer_module_obj = MP_OBJ_NULL;
|
||||
uint i;
|
||||
for (i = 1; i <= mod_len; i++) {
|
||||
if (i == mod_len || mod_str[i] == '.') {
|
||||
// create a qstr for the module name up to this depth
|
||||
qstr mod_name = qstr_from_strn(mod_str, i);
|
||||
DEBUG_printf("Processing module: %s\n", qstr_str(mod_name));
|
||||
DEBUG_printf("Previous path: =%.*s=\n", vstr_len(&path), vstr_str(&path));
|
||||
|
||||
// find the file corresponding to the module name
|
||||
mp_import_stat_t stat;
|
||||
if (vstr_len(&path) == 0) {
|
||||
// first module in the dotted-name; search for a directory or file
|
||||
stat = find_file(mod_str, i, &path);
|
||||
} else {
|
||||
// latter module in the dotted-name; append to path
|
||||
vstr_add_char(&path, PATH_SEP_CHAR);
|
||||
vstr_add_strn(&path, mod_str + last, i - last);
|
||||
stat = stat_dir_or_file(&path);
|
||||
}
|
||||
DEBUG_printf("Current path: %.*s\n", vstr_len(&path), vstr_str(&path));
|
||||
|
||||
if (stat == MP_IMPORT_STAT_NO_EXIST) {
|
||||
module_obj = MP_OBJ_NULL;
|
||||
#if MICROPY_MODULE_WEAK_LINKS
|
||||
// check if there is a weak link to this module
|
||||
if (i == mod_len) {
|
||||
module_obj = mp_module_search_umodule(mod_str);
|
||||
if (module_obj != MP_OBJ_NULL) {
|
||||
// found weak linked module
|
||||
mp_module_call_init(mod_name, module_obj);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (module_obj == MP_OBJ_NULL) {
|
||||
// couldn't find the file, so fail
|
||||
#if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE
|
||||
mp_raise_msg(&mp_type_ImportError, MP_ERROR_TEXT("module not found"));
|
||||
#else
|
||||
mp_raise_msg_varg(&mp_type_ImportError, MP_ERROR_TEXT("no module named '%q'"), mod_name);
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
// found the file, so get the module
|
||||
module_obj = mp_module_get(mod_name);
|
||||
}
|
||||
|
||||
if (module_obj == MP_OBJ_NULL) {
|
||||
// module not already loaded, so load it!
|
||||
|
||||
module_obj = mp_obj_new_module(mod_name);
|
||||
|
||||
// if args[3] (fromtuple) has magic value False, set up
|
||||
// this module for command-line "-m" option (set module's
|
||||
// name to __main__ instead of real name). Do this only
|
||||
// for *modules* however - packages never have their names
|
||||
// replaced, instead they're -m'ed using a special __main__
|
||||
// submodule in them. (This all apparently is done to not
|
||||
// touch package name itself, which is important for future
|
||||
// imports).
|
||||
if (i == mod_len && fromtuple == mp_const_false && stat != MP_IMPORT_STAT_DIR) {
|
||||
mp_obj_module_t *o = MP_OBJ_TO_PTR(module_obj);
|
||||
mp_obj_dict_store(MP_OBJ_FROM_PTR(o->globals), MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR___main__));
|
||||
#if MICROPY_CPYTHON_COMPAT
|
||||
// Store module as "__main__" in the dictionary of loaded modules (returned by sys.modules).
|
||||
mp_obj_dict_store(MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_loaded_modules_dict)), MP_OBJ_NEW_QSTR(MP_QSTR___main__), module_obj);
|
||||
// Store real name in "__main__" attribute. Chosen semi-randonly, to reuse existing qstr's.
|
||||
mp_obj_dict_store(MP_OBJ_FROM_PTR(o->globals), MP_OBJ_NEW_QSTR(MP_QSTR___main__), MP_OBJ_NEW_QSTR(mod_name));
|
||||
#endif
|
||||
}
|
||||
|
||||
if (stat == MP_IMPORT_STAT_DIR) {
|
||||
DEBUG_printf("%.*s is dir\n", vstr_len(&path), vstr_str(&path));
|
||||
// https://docs.python.org/3/reference/import.html
|
||||
// "Specifically, any module that contains a __path__ attribute is considered a package."
|
||||
mp_store_attr(module_obj, MP_QSTR___path__, mp_obj_new_str(vstr_str(&path), vstr_len(&path)));
|
||||
size_t orig_path_len = path.len;
|
||||
vstr_add_char(&path, PATH_SEP_CHAR);
|
||||
vstr_add_str(&path, "__init__.py");
|
||||
if (stat_file_py_or_mpy(&path) != MP_IMPORT_STAT_FILE) {
|
||||
// mp_warning("%s is imported as namespace package", vstr_str(&path));
|
||||
} else {
|
||||
do_load(module_obj, &path);
|
||||
}
|
||||
path.len = orig_path_len;
|
||||
} else { // MP_IMPORT_STAT_FILE
|
||||
do_load(module_obj, &path);
|
||||
// This should be the last component in the import path. If there are
|
||||
// remaining components then it's an ImportError because the current path
|
||||
// (the module that was just loaded) is not a package. This will be caught
|
||||
// on the next iteration because the file will not exist.
|
||||
}
|
||||
}
|
||||
if (outer_module_obj != MP_OBJ_NULL) {
|
||||
qstr s = qstr_from_strn(mod_str + last, i - last);
|
||||
mp_store_attr(outer_module_obj, s, module_obj);
|
||||
}
|
||||
outer_module_obj = module_obj;
|
||||
if (top_module_obj == MP_OBJ_NULL) {
|
||||
top_module_obj = module_obj;
|
||||
}
|
||||
last = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// If fromlist is not empty, return leaf module
|
||||
if (fromtuple != mp_const_none) {
|
||||
return module_obj;
|
||||
}
|
||||
// Otherwise, we need to return top-level package
|
||||
return top_module_obj;
|
||||
}
|
||||
|
||||
#else // MICROPY_ENABLE_EXTERNAL_IMPORT
|
||||
|
||||
mp_obj_t mp_builtin___import__(size_t n_args, const mp_obj_t *args) {
|
||||
// Check that it's not a relative import
|
||||
if (n_args >= 5 && MP_OBJ_SMALL_INT_VALUE(args[4]) != 0) {
|
||||
mp_raise_NotImplementedError(MP_ERROR_TEXT("relative import"));
|
||||
}
|
||||
|
||||
// Check if module already exists, and return it if it does
|
||||
qstr module_name_qstr = mp_obj_str_get_qstr(args[0]);
|
||||
mp_obj_t module_obj = mp_module_get(module_name_qstr);
|
||||
if (module_obj != MP_OBJ_NULL) {
|
||||
return module_obj;
|
||||
}
|
||||
|
||||
#if MICROPY_MODULE_WEAK_LINKS
|
||||
// Check if there is a weak link to this module
|
||||
module_obj = mp_module_search_umodule(qstr_str(module_name_qstr));
|
||||
if (module_obj != MP_OBJ_NULL) {
|
||||
// Found weak-linked module
|
||||
mp_module_call_init(module_name_qstr, module_obj);
|
||||
return module_obj;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Couldn't find the module, so fail
|
||||
#if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE
|
||||
mp_raise_msg(&mp_type_ImportError, MP_ERROR_TEXT("module not found"));
|
||||
#else
|
||||
mp_raise_msg_varg(&mp_type_ImportError, MP_ERROR_TEXT("no module named '%q'"), module_name_qstr);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // MICROPY_ENABLE_EXTERNAL_IMPORT
|
||||
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin___import___obj, 1, 5, mp_builtin___import__);
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013, 2014 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef MICROPY_INCLUDED_PY_COMPILE_H
|
||||
#define MICROPY_INCLUDED_PY_COMPILE_H
|
||||
|
||||
#include "py/lexer.h"
|
||||
#include "py/parse.h"
|
||||
#include "py/emitglue.h"
|
||||
|
||||
// the compiler will raise an exception if an error occurred
|
||||
// the compiler will clear the parse tree before it returns
|
||||
mp_obj_t mp_compile(mp_parse_tree_t *parse_tree, qstr source_file, bool is_repl);
|
||||
|
||||
#if MICROPY_PERSISTENT_CODE_SAVE
|
||||
// this has the same semantics as mp_compile
|
||||
mp_raw_code_t *mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_file, bool is_repl);
|
||||
#endif
|
||||
|
||||
// this is implemented in runtime.c
|
||||
mp_obj_t mp_parse_compile_execute(mp_lexer_t *lex, mp_parse_input_kind_t parse_input_kind, mp_obj_dict_t *globals, mp_obj_dict_t *locals);
|
||||
|
||||
#endif // MICROPY_INCLUDED_PY_COMPILE_H
|
||||
@@ -0,0 +1,254 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef MICROPY_INCLUDED_PY_DYNRUNTIME_H
|
||||
#define MICROPY_INCLUDED_PY_DYNRUNTIME_H
|
||||
|
||||
// This header file contains definitions to dynamically implement the static
|
||||
// MicroPython runtime API defined in py/obj.h and py/runtime.h.
|
||||
|
||||
#include "py/nativeglue.h"
|
||||
#include "py/objstr.h"
|
||||
#include "py/objtype.h"
|
||||
|
||||
#if !MICROPY_ENABLE_DYNRUNTIME
|
||||
#error "dynruntime.h included in non-dynamic-module build."
|
||||
#endif
|
||||
|
||||
#undef MP_ROM_QSTR
|
||||
#undef MP_OBJ_QSTR_VALUE
|
||||
#undef MP_OBJ_NEW_QSTR
|
||||
#undef mp_const_none
|
||||
#undef mp_const_false
|
||||
#undef mp_const_true
|
||||
#undef mp_const_empty_tuple
|
||||
#undef nlr_raise
|
||||
|
||||
/******************************************************************************/
|
||||
// Memory allocation
|
||||
|
||||
#define m_malloc(n) (m_malloc_dyn((n)))
|
||||
#define m_free(ptr) (m_free_dyn((ptr)))
|
||||
#define m_realloc(ptr, new_num_bytes) (m_realloc_dyn((ptr), (new_num_bytes)))
|
||||
|
||||
static inline void *m_malloc_dyn(size_t n) {
|
||||
// TODO won't raise on OOM
|
||||
return mp_fun_table.realloc_(NULL, n, false);
|
||||
}
|
||||
|
||||
static inline void m_free_dyn(void *ptr) {
|
||||
mp_fun_table.realloc_(ptr, 0, false);
|
||||
}
|
||||
|
||||
static inline void *m_realloc_dyn(void *ptr, size_t new_num_bytes) {
|
||||
// TODO won't raise on OOM
|
||||
return mp_fun_table.realloc_(ptr, new_num_bytes, true);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
// Printing
|
||||
|
||||
#define mp_plat_print (*mp_fun_table.plat_print)
|
||||
#define mp_printf(p, ...) (mp_fun_table.printf_((p), __VA_ARGS__))
|
||||
#define mp_vprintf(p, fmt, args) (mp_fun_table.vprintf_((p), (fmt), (args)))
|
||||
|
||||
/******************************************************************************/
|
||||
// Types and objects
|
||||
|
||||
#define MP_OBJ_NEW_QSTR(x) MP_OBJ_NEW_QSTR_##x
|
||||
|
||||
#define mp_type_type (*mp_fun_table.type_type)
|
||||
#define mp_type_str (*mp_fun_table.type_str)
|
||||
#define mp_type_list (*mp_fun_table.type_list)
|
||||
#define mp_type_EOFError (*(mp_obj_type_t *)(mp_load_global(MP_QSTR_EOFError)))
|
||||
#define mp_type_IndexError (*(mp_obj_type_t *)(mp_load_global(MP_QSTR_IndexError)))
|
||||
#define mp_type_KeyError (*(mp_obj_type_t *)(mp_load_global(MP_QSTR_KeyError)))
|
||||
#define mp_type_NotImplementedError (*(mp_obj_type_t *)(mp_load_global(MP_QSTR_NotImplementedError)))
|
||||
#define mp_type_RuntimeError (*(mp_obj_type_t *)(mp_load_global(MP_QSTR_RuntimeError)))
|
||||
#define mp_type_TypeError (*(mp_obj_type_t *)(mp_load_global(MP_QSTR_TypeError)))
|
||||
#define mp_type_ValueError (*(mp_obj_type_t *)(mp_load_global(MP_QSTR_ValueError)))
|
||||
|
||||
#define mp_stream_read_obj (*mp_fun_table.stream_read_obj)
|
||||
#define mp_stream_readinto_obj (*mp_fun_table.stream_readinto_obj)
|
||||
#define mp_stream_unbuffered_readline_obj (*mp_fun_table.stream_unbuffered_readline_obj)
|
||||
#define mp_stream_write_obj (*mp_fun_table.stream_write_obj)
|
||||
|
||||
#define mp_const_none ((mp_obj_t)mp_fun_table.const_none)
|
||||
#define mp_const_false ((mp_obj_t)mp_fun_table.const_false)
|
||||
#define mp_const_true ((mp_obj_t)mp_fun_table.const_true)
|
||||
#define mp_const_empty_tuple (mp_fun_table.new_tuple(0, NULL))
|
||||
|
||||
#define mp_obj_new_bool(b) ((b) ? (mp_obj_t)mp_fun_table.const_true : (mp_obj_t)mp_fun_table.const_false)
|
||||
#define mp_obj_new_int(i) (mp_fun_table.native_to_obj(i, MP_NATIVE_TYPE_INT))
|
||||
#define mp_obj_new_int_from_uint(i) (mp_fun_table.native_to_obj(i, MP_NATIVE_TYPE_UINT))
|
||||
#define mp_obj_new_str(data, len) (mp_fun_table.obj_new_str((data), (len)))
|
||||
#define mp_obj_new_str_of_type(t, d, l) (mp_obj_new_str_of_type_dyn((t), (d), (l)))
|
||||
#define mp_obj_new_bytes(data, len) (mp_fun_table.obj_new_bytes((data), (len)))
|
||||
#define mp_obj_new_bytearray_by_ref(n, i) (mp_fun_table.obj_new_bytearray_by_ref((n), (i)))
|
||||
#define mp_obj_new_tuple(n, items) (mp_fun_table.new_tuple((n), (items)))
|
||||
#define mp_obj_new_list(n, items) (mp_fun_table.new_list((n), (items)))
|
||||
|
||||
#define mp_obj_get_type(o) (mp_fun_table.obj_get_type((o)))
|
||||
#define mp_obj_cast_to_native_base(o, t) (mp_obj_cast_to_native_base_dyn((o), (t)))
|
||||
#define mp_obj_get_int(o) (mp_fun_table.native_from_obj(o, MP_NATIVE_TYPE_INT))
|
||||
#define mp_obj_get_int_truncated(o) (mp_fun_table.native_from_obj(o, MP_NATIVE_TYPE_UINT))
|
||||
#define mp_obj_str_get_str(s) (mp_obj_str_get_data_dyn((s), NULL))
|
||||
#define mp_obj_str_get_data(o, len) (mp_obj_str_get_data_dyn((o), (len)))
|
||||
#define mp_get_buffer_raise(o, bufinfo, fl) (mp_fun_table.get_buffer_raise((o), (bufinfo), (fl)))
|
||||
#define mp_get_stream_raise(s, flags) (mp_fun_table.get_stream_raise((s), (flags)))
|
||||
|
||||
#define mp_obj_len(o) (mp_obj_len_dyn(o))
|
||||
#define mp_obj_subscr(base, index, val) (mp_fun_table.obj_subscr((base), (index), (val)))
|
||||
#define mp_obj_list_append(list, item) (mp_fun_table.list_append((list), (item)))
|
||||
|
||||
static inline mp_obj_t mp_obj_new_str_of_type_dyn(const mp_obj_type_t *type, const byte *data, size_t len) {
|
||||
if (type == &mp_type_str) {
|
||||
return mp_obj_new_str((const char *)data, len);
|
||||
} else {
|
||||
return mp_obj_new_bytes(data, len);
|
||||
}
|
||||
}
|
||||
|
||||
static inline mp_obj_t mp_obj_cast_to_native_base_dyn(mp_obj_t self_in, mp_const_obj_t native_type) {
|
||||
const mp_obj_type_t *self_type = mp_obj_get_type(self_in);
|
||||
|
||||
if (MP_OBJ_FROM_PTR(self_type) == native_type) {
|
||||
return self_in;
|
||||
} else if (self_type->parent != native_type) {
|
||||
// The self_in object is not a direct descendant of native_type, so fail the cast.
|
||||
// This is a very simple version of mp_obj_is_subclass_fast that could be improved.
|
||||
return MP_OBJ_NULL;
|
||||
} else {
|
||||
mp_obj_instance_t *self = (mp_obj_instance_t *)MP_OBJ_TO_PTR(self_in);
|
||||
return self->subobj[0];
|
||||
}
|
||||
}
|
||||
|
||||
static inline void *mp_obj_str_get_data_dyn(mp_obj_t o, size_t *l) {
|
||||
mp_buffer_info_t bufinfo;
|
||||
mp_get_buffer_raise(o, &bufinfo, MP_BUFFER_READ);
|
||||
if (l != NULL) {
|
||||
*l = bufinfo.len;
|
||||
}
|
||||
return bufinfo.buf;
|
||||
}
|
||||
|
||||
static inline mp_obj_t mp_obj_len_dyn(mp_obj_t o) {
|
||||
// If bytes implemented MP_UNARY_OP_LEN could use: mp_unary_op(MP_UNARY_OP_LEN, o)
|
||||
return mp_fun_table.call_function_n_kw(mp_fun_table.load_name(MP_QSTR_len), 1, &o);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
// General runtime functions
|
||||
|
||||
#define mp_load_name(qst) (mp_fun_table.load_name((qst)))
|
||||
#define mp_load_global(qst) (mp_fun_table.load_global((qst)))
|
||||
#define mp_load_attr(base, attr) (mp_fun_table.load_attr((base), (attr)))
|
||||
#define mp_load_method(base, attr, dest) (mp_fun_table.load_method((base), (attr), (dest)))
|
||||
#define mp_load_super_method(attr, dest) (mp_fun_table.load_super_method((attr), (dest)))
|
||||
#define mp_store_name(qst, obj) (mp_fun_table.store_name((qst), (obj)))
|
||||
#define mp_store_global(qst, obj) (mp_fun_table.store_global((qst), (obj)))
|
||||
#define mp_store_attr(base, attr, val) (mp_fun_table.store_attr((base), (attr), (val)))
|
||||
|
||||
#define mp_unary_op(op, obj) (mp_fun_table.unary_op((op), (obj)))
|
||||
#define mp_binary_op(op, lhs, rhs) (mp_fun_table.binary_op((op), (lhs), (rhs)))
|
||||
|
||||
#define mp_make_function_from_raw_code(rc, def_args, def_kw_args) \
|
||||
(mp_fun_table.make_function_from_raw_code((rc), (def_args), (def_kw_args)))
|
||||
|
||||
#define mp_call_function_n_kw(fun, n_args, n_kw, args) \
|
||||
(mp_fun_table.call_function_n_kw((fun), (n_args) | ((n_kw) << 8), args))
|
||||
|
||||
#define mp_arg_check_num(n_args, n_kw, n_args_min, n_args_max, takes_kw) \
|
||||
(mp_fun_table.arg_check_num_sig((n_args), (n_kw), MP_OBJ_FUN_MAKE_SIG((n_args_min), (n_args_max), (takes_kw))))
|
||||
|
||||
#define MP_DYNRUNTIME_INIT_ENTRY \
|
||||
mp_obj_t old_globals = mp_fun_table.swap_globals(self->globals); \
|
||||
mp_raw_code_t rc; \
|
||||
rc.kind = MP_CODE_NATIVE_VIPER; \
|
||||
rc.scope_flags = 0; \
|
||||
rc.const_table = (void *)self->const_table; \
|
||||
(void)rc;
|
||||
|
||||
#define MP_DYNRUNTIME_INIT_EXIT \
|
||||
mp_fun_table.swap_globals(old_globals); \
|
||||
return mp_const_none;
|
||||
|
||||
#define MP_DYNRUNTIME_MAKE_FUNCTION(f) \
|
||||
(mp_make_function_from_raw_code((rc.fun_data = (f), &rc), MP_OBJ_NULL, MP_OBJ_NULL))
|
||||
|
||||
#define mp_import_name(name, fromlist, level) \
|
||||
(mp_fun_table.import_name((name), (fromlist), (level)))
|
||||
#define mp_import_from(module, name) \
|
||||
(mp_fun_table.import_from((module), (name)))
|
||||
#define mp_import_all(module) \
|
||||
(mp_fun_table.import_all((module))
|
||||
|
||||
/******************************************************************************/
|
||||
// Exceptions
|
||||
|
||||
#define mp_obj_new_exception(o) ((mp_obj_t)(o)) // Assumes returned object will be raised, will create instance then
|
||||
#define mp_obj_new_exception_arg1(e_type, arg) (mp_obj_new_exception_arg1_dyn((e_type), (arg)))
|
||||
|
||||
#define nlr_raise(o) (mp_raise_dyn(o))
|
||||
#define mp_raise_msg(type, msg) (mp_fun_table.raise_msg((type), (msg)))
|
||||
#define mp_raise_OSError(er) (mp_raise_OSError_dyn(er))
|
||||
#define mp_raise_NotImplementedError(msg) (mp_raise_msg(&mp_type_NotImplementedError, (msg)))
|
||||
#define mp_raise_TypeError(msg) (mp_raise_msg(&mp_type_TypeError, (msg)))
|
||||
#define mp_raise_ValueError(msg) (mp_raise_msg(&mp_type_ValueError, (msg)))
|
||||
|
||||
static inline mp_obj_t mp_obj_new_exception_arg1_dyn(const mp_obj_type_t *exc_type, mp_obj_t arg) {
|
||||
mp_obj_t args[1] = { arg };
|
||||
return mp_call_function_n_kw(MP_OBJ_FROM_PTR(exc_type), 1, 0, &args[0]);
|
||||
}
|
||||
|
||||
static NORETURN inline void mp_raise_dyn(mp_obj_t o) {
|
||||
mp_fun_table.raise(o);
|
||||
for (;;) {
|
||||
}
|
||||
}
|
||||
|
||||
static inline void mp_raise_OSError_dyn(int er) {
|
||||
mp_obj_t args[1] = { MP_OBJ_NEW_SMALL_INT(er) };
|
||||
nlr_raise(mp_call_function_n_kw(mp_load_global(MP_QSTR_OSError), 1, 0, &args[0]));
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
// Floating point
|
||||
|
||||
#define mp_obj_new_float_from_f(f) (mp_fun_table.obj_new_float_from_f((f)))
|
||||
#define mp_obj_new_float_from_d(d) (mp_fun_table.obj_new_float_from_d((d)))
|
||||
#define mp_obj_get_float_to_f(o) (mp_fun_table.obj_get_float_to_f((o)))
|
||||
#define mp_obj_get_float_to_d(o) (mp_fun_table.obj_get_float_to_d((o)))
|
||||
|
||||
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
|
||||
#define mp_obj_new_float(f) (mp_obj_new_float_from_f((f)))
|
||||
#define mp_obj_get_float(o) (mp_obj_get_float_to_f((o)))
|
||||
#elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE
|
||||
#define mp_obj_new_float(f) (mp_obj_new_float_from_d((f)))
|
||||
#define mp_obj_get_float(o) (mp_obj_get_float_to_d((o)))
|
||||
#endif
|
||||
|
||||
#endif // MICROPY_INCLUDED_PY_DYNRUNTIME_H
|
||||
@@ -0,0 +1,145 @@
|
||||
# Makefile fragment for generating native .mpy files from C source
|
||||
# MPY_DIR must be set to the top of the MicroPython source tree
|
||||
|
||||
BUILD ?= build
|
||||
|
||||
ECHO = @echo
|
||||
RM = /bin/rm
|
||||
MKDIR = /bin/mkdir
|
||||
PYTHON = python3
|
||||
MPY_CROSS = $(MPY_DIR)/mpy-cross/mpy-cross
|
||||
MPY_TOOL = $(PYTHON) $(MPY_DIR)/tools/mpy-tool.py
|
||||
MPY_LD = $(PYTHON) $(MPY_DIR)/tools/mpy_ld.py
|
||||
|
||||
Q = @
|
||||
ifeq ("$(origin V)", "command line")
|
||||
ifeq ($(V),1)
|
||||
Q =
|
||||
MPY_LD += '-vvv'
|
||||
endif
|
||||
endif
|
||||
|
||||
ARCH_UPPER = $(shell echo $(ARCH) | tr '[:lower:]' '[:upper:]')
|
||||
CONFIG_H = $(BUILD)/$(MOD).config.h
|
||||
|
||||
CFLAGS += -I. -I$(MPY_DIR)
|
||||
CFLAGS += -std=c99
|
||||
CFLAGS += -Os
|
||||
CFLAGS += -Wall -Werror -DNDEBUG
|
||||
CFLAGS += -DNO_QSTR
|
||||
CFLAGS += -DMICROPY_ENABLE_DYNRUNTIME
|
||||
CFLAGS += -DMP_CONFIGFILE='<$(CONFIG_H)>'
|
||||
CFLAGS += -fpic -fno-common
|
||||
CFLAGS += -U _FORTIFY_SOURCE # prevent use of __*_chk libc functions
|
||||
#CFLAGS += -fdata-sections -ffunction-sections
|
||||
|
||||
MPY_CROSS_FLAGS += -march=$(ARCH)
|
||||
|
||||
SRC_O += $(addprefix $(BUILD)/, $(patsubst %.c,%.o,$(filter %.c,$(SRC))))
|
||||
SRC_MPY += $(addprefix $(BUILD)/, $(patsubst %.py,%.mpy,$(filter %.py,$(SRC))))
|
||||
|
||||
################################################################################
|
||||
# Architecture configuration
|
||||
|
||||
ifeq ($(ARCH),x86)
|
||||
|
||||
# x86
|
||||
CROSS =
|
||||
CFLAGS += -m32 -fno-stack-protector
|
||||
MPY_CROSS_FLAGS += -mcache-lookup-bc
|
||||
MICROPY_FLOAT_IMPL ?= double
|
||||
|
||||
else ifeq ($(ARCH),x64)
|
||||
|
||||
# x64
|
||||
CROSS =
|
||||
CFLAGS += -fno-stack-protector
|
||||
MPY_CROSS_FLAGS += -mcache-lookup-bc
|
||||
MICROPY_FLOAT_IMPL ?= double
|
||||
|
||||
else ifeq ($(ARCH),armv7m)
|
||||
|
||||
# thumb
|
||||
CROSS = arm-none-eabi-
|
||||
CFLAGS += -mthumb -mcpu=cortex-m3
|
||||
MICROPY_FLOAT_IMPL ?= none
|
||||
|
||||
else ifeq ($(ARCH),armv7emsp)
|
||||
|
||||
# thumb
|
||||
CROSS = arm-none-eabi-
|
||||
CFLAGS += -mthumb -mcpu=cortex-m4
|
||||
CFLAGS += -mfpu=fpv4-sp-d16 -mfloat-abi=hard
|
||||
MICROPY_FLOAT_IMPL ?= float
|
||||
|
||||
else ifeq ($(ARCH),armv7emdp)
|
||||
|
||||
# thumb
|
||||
CROSS = arm-none-eabi-
|
||||
CFLAGS += -mthumb -mcpu=cortex-m7
|
||||
CFLAGS += -mfpu=fpv5-d16 -mfloat-abi=hard
|
||||
MICROPY_FLOAT_IMPL ?= double
|
||||
|
||||
else ifeq ($(ARCH),xtensa)
|
||||
|
||||
# xtensa
|
||||
CROSS = xtensa-lx106-elf-
|
||||
CFLAGS += -mforce-l32
|
||||
MICROPY_FLOAT_IMPL ?= none
|
||||
|
||||
else ifeq ($(ARCH),xtensawin)
|
||||
|
||||
# xtensawin
|
||||
CROSS = xtensa-esp32-elf-
|
||||
CFLAGS +=
|
||||
MICROPY_FLOAT_IMPL ?= float
|
||||
|
||||
else
|
||||
$(error architecture '$(ARCH)' not supported)
|
||||
endif
|
||||
|
||||
MICROPY_FLOAT_IMPL_UPPER = $(shell echo $(MICROPY_FLOAT_IMPL) | tr '[:lower:]' '[:upper:]')
|
||||
CFLAGS += -DMICROPY_FLOAT_IMPL=MICROPY_FLOAT_IMPL_$(MICROPY_FLOAT_IMPL_UPPER)
|
||||
|
||||
CFLAGS += $(CFLAGS_EXTRA)
|
||||
|
||||
################################################################################
|
||||
# Build rules
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: $(MOD).mpy
|
||||
|
||||
clean:
|
||||
$(RM) -rf $(BUILD) $(CLEAN_EXTRA)
|
||||
|
||||
# Create build destination directories first
|
||||
BUILD_DIRS = $(sort $(dir $(CONFIG_H) $(SRC_O) $(SRC_MPY)))
|
||||
$(CONFIG_H) $(SRC_O) $(SRC_MPY): | $(BUILD_DIRS)
|
||||
$(BUILD_DIRS):
|
||||
$(Q)$(MKDIR) -p $@
|
||||
|
||||
# Preprocess all source files to generate $(CONFIG_H)
|
||||
$(CONFIG_H): $(SRC)
|
||||
$(ECHO) "GEN $@"
|
||||
$(Q)$(MPY_LD) --arch $(ARCH) --preprocess -o $@ $^
|
||||
|
||||
# Build .o from .c source files
|
||||
$(BUILD)/%.o: %.c $(CONFIG_H) Makefile
|
||||
$(ECHO) "CC $<"
|
||||
$(Q)$(CROSS)gcc $(CFLAGS) -o $@ -c $<
|
||||
|
||||
# Build .mpy from .py source files
|
||||
$(BUILD)/%.mpy: %.py
|
||||
$(ECHO) "MPY $<"
|
||||
$(Q)$(MPY_CROSS) $(MPY_CROSS_FLAGS) -o $@ $<
|
||||
|
||||
# Build native .mpy from object files
|
||||
$(BUILD)/$(MOD).native.mpy: $(SRC_O)
|
||||
$(ECHO) "LINK $<"
|
||||
$(Q)$(MPY_LD) --arch $(ARCH) --qstrs $(CONFIG_H) -o $@ $^
|
||||
|
||||
# Build final .mpy from all intermediate .mpy files
|
||||
$(MOD).mpy: $(BUILD)/$(MOD).native.mpy $(SRC_MPY)
|
||||
$(ECHO) "GEN $@"
|
||||
$(Q)$(MPY_TOOL) --merge -o $@ $^
|
||||
@@ -0,0 +1,288 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013, 2014 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef MICROPY_INCLUDED_PY_EMIT_H
|
||||
#define MICROPY_INCLUDED_PY_EMIT_H
|
||||
|
||||
#include "py/lexer.h"
|
||||
#include "py/scope.h"
|
||||
|
||||
/* Notes on passes:
|
||||
* We don't know exactly the opcodes in pass 1 because they depend on the
|
||||
* closing over of variables (LOAD_CLOSURE, BUILD_TUPLE, MAKE_CLOSURE), which
|
||||
* depends on determining the scope of variables in each function, and this
|
||||
* is not known until the end of pass 1.
|
||||
* As a consequence, we don't know the maximum stack size until the end of pass 2.
|
||||
* This is problematic for some emitters (x64) since they need to know the maximum
|
||||
* stack size to compile the entry to the function, and this affects code size.
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
MP_PASS_SCOPE = 1, // work out id's and their kind, and number of labels
|
||||
MP_PASS_STACK_SIZE = 2, // work out maximum stack size
|
||||
MP_PASS_CODE_SIZE = 3, // work out code size and label offsets
|
||||
MP_PASS_EMIT = 4, // emit code
|
||||
} pass_kind_t;
|
||||
|
||||
#define MP_EMIT_STAR_FLAG_SINGLE (0x01)
|
||||
#define MP_EMIT_STAR_FLAG_DOUBLE (0x02)
|
||||
|
||||
#define MP_EMIT_BREAK_FROM_FOR (0x8000)
|
||||
|
||||
// Kind for emit_id_ops->local()
|
||||
#define MP_EMIT_IDOP_LOCAL_FAST (0)
|
||||
#define MP_EMIT_IDOP_LOCAL_DEREF (1)
|
||||
|
||||
// Kind for emit_id_ops->global()
|
||||
#define MP_EMIT_IDOP_GLOBAL_NAME (0)
|
||||
#define MP_EMIT_IDOP_GLOBAL_GLOBAL (1)
|
||||
|
||||
// Kind for emit->import()
|
||||
#define MP_EMIT_IMPORT_NAME (0)
|
||||
#define MP_EMIT_IMPORT_FROM (1)
|
||||
#define MP_EMIT_IMPORT_STAR (2)
|
||||
|
||||
// Kind for emit->subscr()
|
||||
#define MP_EMIT_SUBSCR_LOAD (0)
|
||||
#define MP_EMIT_SUBSCR_STORE (1)
|
||||
#define MP_EMIT_SUBSCR_DELETE (2)
|
||||
|
||||
// Kind for emit->attr()
|
||||
#define MP_EMIT_ATTR_LOAD (0)
|
||||
#define MP_EMIT_ATTR_STORE (1)
|
||||
#define MP_EMIT_ATTR_DELETE (2)
|
||||
|
||||
// Kind for emit->setup_block()
|
||||
#define MP_EMIT_SETUP_BLOCK_WITH (0)
|
||||
#define MP_EMIT_SETUP_BLOCK_EXCEPT (1)
|
||||
#define MP_EMIT_SETUP_BLOCK_FINALLY (2)
|
||||
|
||||
// Kind for emit->build()
|
||||
#define MP_EMIT_BUILD_TUPLE (0)
|
||||
#define MP_EMIT_BUILD_LIST (1)
|
||||
#define MP_EMIT_BUILD_MAP (2)
|
||||
#define MP_EMIT_BUILD_SET (3)
|
||||
#define MP_EMIT_BUILD_SLICE (4)
|
||||
|
||||
// Kind for emit->yield()
|
||||
#define MP_EMIT_YIELD_VALUE (0)
|
||||
#define MP_EMIT_YIELD_FROM (1)
|
||||
|
||||
typedef struct _emit_t emit_t;
|
||||
|
||||
typedef struct _mp_emit_method_table_id_ops_t {
|
||||
void (*local)(emit_t *emit, qstr qst, mp_uint_t local_num, int kind);
|
||||
void (*global)(emit_t *emit, qstr qst, int kind);
|
||||
} mp_emit_method_table_id_ops_t;
|
||||
|
||||
typedef struct _emit_method_table_t {
|
||||
#if MICROPY_DYNAMIC_COMPILER
|
||||
emit_t *(*emit_new)(mp_obj_t * error_slot, uint *label_slot, mp_uint_t max_num_labels);
|
||||
void (*emit_free)(emit_t *emit);
|
||||
#endif
|
||||
|
||||
void (*start_pass)(emit_t *emit, pass_kind_t pass, scope_t *scope);
|
||||
void (*end_pass)(emit_t *emit);
|
||||
bool (*last_emit_was_return_value)(emit_t *emit);
|
||||
void (*adjust_stack_size)(emit_t *emit, mp_int_t delta);
|
||||
void (*set_source_line)(emit_t *emit, mp_uint_t line);
|
||||
|
||||
mp_emit_method_table_id_ops_t load_id;
|
||||
mp_emit_method_table_id_ops_t store_id;
|
||||
mp_emit_method_table_id_ops_t delete_id;
|
||||
|
||||
void (*label_assign)(emit_t *emit, mp_uint_t l);
|
||||
void (*import)(emit_t *emit, qstr qst, int kind);
|
||||
void (*load_const_tok)(emit_t *emit, mp_token_kind_t tok);
|
||||
void (*load_const_small_int)(emit_t *emit, mp_int_t arg);
|
||||
void (*load_const_str)(emit_t *emit, qstr qst);
|
||||
void (*load_const_obj)(emit_t *emit, mp_obj_t obj);
|
||||
void (*load_null)(emit_t *emit);
|
||||
void (*load_method)(emit_t *emit, qstr qst, bool is_super);
|
||||
void (*load_build_class)(emit_t *emit);
|
||||
void (*subscr)(emit_t *emit, int kind);
|
||||
void (*attr)(emit_t *emit, qstr qst, int kind);
|
||||
void (*dup_top)(emit_t *emit);
|
||||
void (*dup_top_two)(emit_t *emit);
|
||||
void (*pop_top)(emit_t *emit);
|
||||
void (*rot_two)(emit_t *emit);
|
||||
void (*rot_three)(emit_t *emit);
|
||||
void (*jump)(emit_t *emit, mp_uint_t label);
|
||||
void (*pop_jump_if)(emit_t *emit, bool cond, mp_uint_t label);
|
||||
void (*jump_if_or_pop)(emit_t *emit, bool cond, mp_uint_t label);
|
||||
void (*unwind_jump)(emit_t *emit, mp_uint_t label, mp_uint_t except_depth);
|
||||
void (*setup_block)(emit_t *emit, mp_uint_t label, int kind);
|
||||
void (*with_cleanup)(emit_t *emit, mp_uint_t label);
|
||||
void (*end_finally)(emit_t *emit);
|
||||
void (*get_iter)(emit_t *emit, bool use_stack);
|
||||
void (*for_iter)(emit_t *emit, mp_uint_t label);
|
||||
void (*for_iter_end)(emit_t *emit);
|
||||
void (*pop_except_jump)(emit_t *emit, mp_uint_t label, bool within_exc_handler);
|
||||
void (*unary_op)(emit_t *emit, mp_unary_op_t op);
|
||||
void (*binary_op)(emit_t *emit, mp_binary_op_t op);
|
||||
void (*build)(emit_t *emit, mp_uint_t n_args, int kind);
|
||||
void (*store_map)(emit_t *emit);
|
||||
void (*store_comp)(emit_t *emit, scope_kind_t kind, mp_uint_t set_stack_index);
|
||||
void (*unpack_sequence)(emit_t *emit, mp_uint_t n_args);
|
||||
void (*unpack_ex)(emit_t *emit, mp_uint_t n_left, mp_uint_t n_right);
|
||||
void (*make_function)(emit_t *emit, scope_t *scope, mp_uint_t n_pos_defaults, mp_uint_t n_kw_defaults);
|
||||
void (*make_closure)(emit_t *emit, scope_t *scope, mp_uint_t n_closed_over, mp_uint_t n_pos_defaults, mp_uint_t n_kw_defaults);
|
||||
void (*call_function)(emit_t *emit, mp_uint_t n_positional, mp_uint_t n_keyword, mp_uint_t star_flags);
|
||||
void (*call_method)(emit_t *emit, mp_uint_t n_positional, mp_uint_t n_keyword, mp_uint_t star_flags);
|
||||
void (*return_value)(emit_t *emit);
|
||||
void (*raise_varargs)(emit_t *emit, mp_uint_t n_args);
|
||||
void (*yield)(emit_t *emit, int kind);
|
||||
|
||||
// these methods are used to control entry to/exit from an exception handler
|
||||
// they may or may not emit code
|
||||
void (*start_except_handler)(emit_t *emit);
|
||||
void (*end_except_handler)(emit_t *emit);
|
||||
} emit_method_table_t;
|
||||
|
||||
static inline void mp_emit_common_get_id_for_load(scope_t *scope, qstr qst) {
|
||||
scope_find_or_add_id(scope, qst, ID_INFO_KIND_GLOBAL_IMPLICIT);
|
||||
}
|
||||
|
||||
void mp_emit_common_get_id_for_modification(scope_t *scope, qstr qst);
|
||||
void mp_emit_common_id_op(emit_t *emit, const mp_emit_method_table_id_ops_t *emit_method_table, scope_t *scope, qstr qst);
|
||||
|
||||
extern const emit_method_table_t emit_bc_method_table;
|
||||
extern const emit_method_table_t emit_native_x64_method_table;
|
||||
extern const emit_method_table_t emit_native_x86_method_table;
|
||||
extern const emit_method_table_t emit_native_thumb_method_table;
|
||||
extern const emit_method_table_t emit_native_arm_method_table;
|
||||
extern const emit_method_table_t emit_native_xtensa_method_table;
|
||||
extern const emit_method_table_t emit_native_xtensawin_method_table;
|
||||
|
||||
extern const mp_emit_method_table_id_ops_t mp_emit_bc_method_table_load_id_ops;
|
||||
extern const mp_emit_method_table_id_ops_t mp_emit_bc_method_table_store_id_ops;
|
||||
extern const mp_emit_method_table_id_ops_t mp_emit_bc_method_table_delete_id_ops;
|
||||
|
||||
emit_t *emit_bc_new(void);
|
||||
emit_t *emit_native_x64_new(mp_obj_t *error_slot, uint *label_slot, mp_uint_t max_num_labels);
|
||||
emit_t *emit_native_x86_new(mp_obj_t *error_slot, uint *label_slot, mp_uint_t max_num_labels);
|
||||
emit_t *emit_native_thumb_new(mp_obj_t *error_slot, uint *label_slot, mp_uint_t max_num_labels);
|
||||
emit_t *emit_native_arm_new(mp_obj_t *error_slot, uint *label_slot, mp_uint_t max_num_labels);
|
||||
emit_t *emit_native_xtensa_new(mp_obj_t *error_slot, uint *label_slot, mp_uint_t max_num_labels);
|
||||
emit_t *emit_native_xtensawin_new(mp_obj_t *error_slot, uint *label_slot, mp_uint_t max_num_labels);
|
||||
|
||||
void emit_bc_set_max_num_labels(emit_t *emit, mp_uint_t max_num_labels);
|
||||
|
||||
void emit_bc_free(emit_t *emit);
|
||||
void emit_native_x64_free(emit_t *emit);
|
||||
void emit_native_x86_free(emit_t *emit);
|
||||
void emit_native_thumb_free(emit_t *emit);
|
||||
void emit_native_arm_free(emit_t *emit);
|
||||
void emit_native_xtensa_free(emit_t *emit);
|
||||
void emit_native_xtensawin_free(emit_t *emit);
|
||||
|
||||
void mp_emit_bc_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scope);
|
||||
void mp_emit_bc_end_pass(emit_t *emit);
|
||||
bool mp_emit_bc_last_emit_was_return_value(emit_t *emit);
|
||||
void mp_emit_bc_adjust_stack_size(emit_t *emit, mp_int_t delta);
|
||||
void mp_emit_bc_set_source_line(emit_t *emit, mp_uint_t line);
|
||||
|
||||
void mp_emit_bc_load_local(emit_t *emit, qstr qst, mp_uint_t local_num, int kind);
|
||||
void mp_emit_bc_load_global(emit_t *emit, qstr qst, int kind);
|
||||
void mp_emit_bc_store_local(emit_t *emit, qstr qst, mp_uint_t local_num, int kind);
|
||||
void mp_emit_bc_store_global(emit_t *emit, qstr qst, int kind);
|
||||
void mp_emit_bc_delete_local(emit_t *emit, qstr qst, mp_uint_t local_num, int kind);
|
||||
void mp_emit_bc_delete_global(emit_t *emit, qstr qst, int kind);
|
||||
|
||||
void mp_emit_bc_label_assign(emit_t *emit, mp_uint_t l);
|
||||
void mp_emit_bc_import(emit_t *emit, qstr qst, int kind);
|
||||
void mp_emit_bc_load_const_tok(emit_t *emit, mp_token_kind_t tok);
|
||||
void mp_emit_bc_load_const_small_int(emit_t *emit, mp_int_t arg);
|
||||
void mp_emit_bc_load_const_str(emit_t *emit, qstr qst);
|
||||
void mp_emit_bc_load_const_obj(emit_t *emit, mp_obj_t obj);
|
||||
void mp_emit_bc_load_null(emit_t *emit);
|
||||
void mp_emit_bc_load_method(emit_t *emit, qstr qst, bool is_super);
|
||||
void mp_emit_bc_load_build_class(emit_t *emit);
|
||||
void mp_emit_bc_subscr(emit_t *emit, int kind);
|
||||
void mp_emit_bc_attr(emit_t *emit, qstr qst, int kind);
|
||||
void mp_emit_bc_dup_top(emit_t *emit);
|
||||
void mp_emit_bc_dup_top_two(emit_t *emit);
|
||||
void mp_emit_bc_pop_top(emit_t *emit);
|
||||
void mp_emit_bc_rot_two(emit_t *emit);
|
||||
void mp_emit_bc_rot_three(emit_t *emit);
|
||||
void mp_emit_bc_jump(emit_t *emit, mp_uint_t label);
|
||||
void mp_emit_bc_pop_jump_if(emit_t *emit, bool cond, mp_uint_t label);
|
||||
void mp_emit_bc_jump_if_or_pop(emit_t *emit, bool cond, mp_uint_t label);
|
||||
void mp_emit_bc_unwind_jump(emit_t *emit, mp_uint_t label, mp_uint_t except_depth);
|
||||
void mp_emit_bc_setup_block(emit_t *emit, mp_uint_t label, int kind);
|
||||
void mp_emit_bc_with_cleanup(emit_t *emit, mp_uint_t label);
|
||||
void mp_emit_bc_end_finally(emit_t *emit);
|
||||
void mp_emit_bc_get_iter(emit_t *emit, bool use_stack);
|
||||
void mp_emit_bc_for_iter(emit_t *emit, mp_uint_t label);
|
||||
void mp_emit_bc_for_iter_end(emit_t *emit);
|
||||
void mp_emit_bc_pop_except_jump(emit_t *emit, mp_uint_t label, bool within_exc_handler);
|
||||
void mp_emit_bc_unary_op(emit_t *emit, mp_unary_op_t op);
|
||||
void mp_emit_bc_binary_op(emit_t *emit, mp_binary_op_t op);
|
||||
void mp_emit_bc_build(emit_t *emit, mp_uint_t n_args, int kind);
|
||||
void mp_emit_bc_store_map(emit_t *emit);
|
||||
void mp_emit_bc_store_comp(emit_t *emit, scope_kind_t kind, mp_uint_t list_stack_index);
|
||||
void mp_emit_bc_unpack_sequence(emit_t *emit, mp_uint_t n_args);
|
||||
void mp_emit_bc_unpack_ex(emit_t *emit, mp_uint_t n_left, mp_uint_t n_right);
|
||||
void mp_emit_bc_make_function(emit_t *emit, scope_t *scope, mp_uint_t n_pos_defaults, mp_uint_t n_kw_defaults);
|
||||
void mp_emit_bc_make_closure(emit_t *emit, scope_t *scope, mp_uint_t n_closed_over, mp_uint_t n_pos_defaults, mp_uint_t n_kw_defaults);
|
||||
void mp_emit_bc_call_function(emit_t *emit, mp_uint_t n_positional, mp_uint_t n_keyword, mp_uint_t star_flags);
|
||||
void mp_emit_bc_call_method(emit_t *emit, mp_uint_t n_positional, mp_uint_t n_keyword, mp_uint_t star_flags);
|
||||
void mp_emit_bc_return_value(emit_t *emit);
|
||||
void mp_emit_bc_raise_varargs(emit_t *emit, mp_uint_t n_args);
|
||||
void mp_emit_bc_yield(emit_t *emit, int kind);
|
||||
void mp_emit_bc_start_except_handler(emit_t *emit);
|
||||
void mp_emit_bc_end_except_handler(emit_t *emit);
|
||||
|
||||
typedef struct _emit_inline_asm_t emit_inline_asm_t;
|
||||
|
||||
typedef struct _emit_inline_asm_method_table_t {
|
||||
#if MICROPY_DYNAMIC_COMPILER
|
||||
emit_inline_asm_t *(*asm_new)(mp_uint_t max_num_labels);
|
||||
void (*asm_free)(emit_inline_asm_t *emit);
|
||||
#endif
|
||||
|
||||
void (*start_pass)(emit_inline_asm_t *emit, pass_kind_t pass, mp_obj_t *error_slot);
|
||||
void (*end_pass)(emit_inline_asm_t *emit, mp_uint_t type_sig);
|
||||
mp_uint_t (*count_params)(emit_inline_asm_t *emit, mp_uint_t n_params, mp_parse_node_t *pn_params);
|
||||
bool (*label)(emit_inline_asm_t *emit, mp_uint_t label_num, qstr label_id);
|
||||
void (*op)(emit_inline_asm_t *emit, qstr op, mp_uint_t n_args, mp_parse_node_t *pn_args);
|
||||
} emit_inline_asm_method_table_t;
|
||||
|
||||
extern const emit_inline_asm_method_table_t emit_inline_thumb_method_table;
|
||||
extern const emit_inline_asm_method_table_t emit_inline_xtensa_method_table;
|
||||
|
||||
emit_inline_asm_t *emit_inline_thumb_new(mp_uint_t max_num_labels);
|
||||
emit_inline_asm_t *emit_inline_xtensa_new(mp_uint_t max_num_labels);
|
||||
|
||||
void emit_inline_thumb_free(emit_inline_asm_t *emit);
|
||||
void emit_inline_xtensa_free(emit_inline_asm_t *emit);
|
||||
|
||||
#if MICROPY_WARNINGS
|
||||
void mp_emitter_warning(pass_kind_t pass, const char *msg);
|
||||
#else
|
||||
#define mp_emitter_warning(pass, msg)
|
||||
#endif
|
||||
|
||||
#endif // MICROPY_INCLUDED_PY_EMIT_H
|
||||
@@ -0,0 +1,943 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013-2019 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "py/mpstate.h"
|
||||
#include "py/emit.h"
|
||||
#include "py/bc0.h"
|
||||
|
||||
#if MICROPY_ENABLE_COMPILER
|
||||
|
||||
#define BYTES_FOR_INT ((BYTES_PER_WORD * 8 + 6) / 7)
|
||||
#define DUMMY_DATA_SIZE (BYTES_FOR_INT)
|
||||
|
||||
struct _emit_t {
|
||||
// Accessed as mp_obj_t, so must be aligned as such, and we rely on the
|
||||
// memory allocator returning a suitably aligned pointer.
|
||||
// Should work for cases when mp_obj_t is 64-bit on a 32-bit machine.
|
||||
byte dummy_data[DUMMY_DATA_SIZE];
|
||||
|
||||
pass_kind_t pass : 8;
|
||||
mp_uint_t last_emit_was_return_value : 8;
|
||||
|
||||
int stack_size;
|
||||
|
||||
scope_t *scope;
|
||||
|
||||
mp_uint_t last_source_line_offset;
|
||||
mp_uint_t last_source_line;
|
||||
|
||||
mp_uint_t max_num_labels;
|
||||
mp_uint_t *label_offsets;
|
||||
|
||||
size_t code_info_offset;
|
||||
size_t code_info_size;
|
||||
size_t bytecode_offset;
|
||||
size_t bytecode_size;
|
||||
byte *code_base; // stores both byte code and code info
|
||||
|
||||
size_t n_info;
|
||||
size_t n_cell;
|
||||
|
||||
#if MICROPY_PERSISTENT_CODE
|
||||
uint16_t ct_cur_obj;
|
||||
uint16_t ct_num_obj;
|
||||
uint16_t ct_cur_raw_code;
|
||||
#endif
|
||||
mp_uint_t *const_table;
|
||||
};
|
||||
|
||||
emit_t *emit_bc_new(void) {
|
||||
emit_t *emit = m_new0(emit_t, 1);
|
||||
return emit;
|
||||
}
|
||||
|
||||
void emit_bc_set_max_num_labels(emit_t *emit, mp_uint_t max_num_labels) {
|
||||
emit->max_num_labels = max_num_labels;
|
||||
emit->label_offsets = m_new(mp_uint_t, emit->max_num_labels);
|
||||
}
|
||||
|
||||
void emit_bc_free(emit_t *emit) {
|
||||
m_del(mp_uint_t, emit->label_offsets, emit->max_num_labels);
|
||||
m_del_obj(emit_t, emit);
|
||||
}
|
||||
|
||||
typedef byte *(*emit_allocator_t)(emit_t *emit, int nbytes);
|
||||
|
||||
STATIC void emit_write_uint(emit_t *emit, emit_allocator_t allocator, mp_uint_t val) {
|
||||
// We store each 7 bits in a separate byte, and that's how many bytes needed
|
||||
byte buf[BYTES_FOR_INT];
|
||||
byte *p = buf + sizeof(buf);
|
||||
// We encode in little-ending order, but store in big-endian, to help decoding
|
||||
do {
|
||||
*--p = val & 0x7f;
|
||||
val >>= 7;
|
||||
} while (val != 0);
|
||||
byte *c = allocator(emit, buf + sizeof(buf) - p);
|
||||
while (p != buf + sizeof(buf) - 1) {
|
||||
*c++ = *p++ | 0x80;
|
||||
}
|
||||
*c = *p;
|
||||
}
|
||||
|
||||
// all functions must go through this one to emit code info
|
||||
STATIC byte *emit_get_cur_to_write_code_info(emit_t *emit, int num_bytes_to_write) {
|
||||
if (emit->pass < MP_PASS_EMIT) {
|
||||
emit->code_info_offset += num_bytes_to_write;
|
||||
return emit->dummy_data;
|
||||
} else {
|
||||
assert(emit->code_info_offset + num_bytes_to_write <= emit->code_info_size);
|
||||
byte *c = emit->code_base + emit->code_info_offset;
|
||||
emit->code_info_offset += num_bytes_to_write;
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
STATIC void emit_write_code_info_byte(emit_t *emit, byte val) {
|
||||
*emit_get_cur_to_write_code_info(emit, 1) = val;
|
||||
}
|
||||
|
||||
STATIC void emit_write_code_info_qstr(emit_t *emit, qstr qst) {
|
||||
#if MICROPY_PERSISTENT_CODE
|
||||
assert((qst >> 16) == 0);
|
||||
byte *c = emit_get_cur_to_write_code_info(emit, 2);
|
||||
c[0] = qst;
|
||||
c[1] = qst >> 8;
|
||||
#else
|
||||
emit_write_uint(emit, emit_get_cur_to_write_code_info, qst);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if MICROPY_ENABLE_SOURCE_LINE
|
||||
STATIC void emit_write_code_info_bytes_lines(emit_t *emit, mp_uint_t bytes_to_skip, mp_uint_t lines_to_skip) {
|
||||
assert(bytes_to_skip > 0 || lines_to_skip > 0);
|
||||
while (bytes_to_skip > 0 || lines_to_skip > 0) {
|
||||
mp_uint_t b, l;
|
||||
if (lines_to_skip <= 6 || bytes_to_skip > 0xf) {
|
||||
// use 0b0LLBBBBB encoding
|
||||
b = MIN(bytes_to_skip, 0x1f);
|
||||
if (b < bytes_to_skip) {
|
||||
// we can't skip any lines until we skip all the bytes
|
||||
l = 0;
|
||||
} else {
|
||||
l = MIN(lines_to_skip, 0x3);
|
||||
}
|
||||
*emit_get_cur_to_write_code_info(emit, 1) = b | (l << 5);
|
||||
} else {
|
||||
// use 0b1LLLBBBB 0bLLLLLLLL encoding (l's LSB in second byte)
|
||||
b = MIN(bytes_to_skip, 0xf);
|
||||
l = MIN(lines_to_skip, 0x7ff);
|
||||
byte *ci = emit_get_cur_to_write_code_info(emit, 2);
|
||||
ci[0] = 0x80 | b | ((l >> 4) & 0x70);
|
||||
ci[1] = l;
|
||||
}
|
||||
bytes_to_skip -= b;
|
||||
lines_to_skip -= l;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// all functions must go through this one to emit byte code
|
||||
STATIC byte *emit_get_cur_to_write_bytecode(emit_t *emit, int num_bytes_to_write) {
|
||||
if (emit->pass < MP_PASS_EMIT) {
|
||||
emit->bytecode_offset += num_bytes_to_write;
|
||||
return emit->dummy_data;
|
||||
} else {
|
||||
assert(emit->bytecode_offset + num_bytes_to_write <= emit->bytecode_size);
|
||||
byte *c = emit->code_base + emit->code_info_size + emit->bytecode_offset;
|
||||
emit->bytecode_offset += num_bytes_to_write;
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
STATIC void emit_write_bytecode_raw_byte(emit_t *emit, byte b1) {
|
||||
byte *c = emit_get_cur_to_write_bytecode(emit, 1);
|
||||
c[0] = b1;
|
||||
}
|
||||
|
||||
STATIC void emit_write_bytecode_byte(emit_t *emit, int stack_adj, byte b1) {
|
||||
mp_emit_bc_adjust_stack_size(emit, stack_adj);
|
||||
byte *c = emit_get_cur_to_write_bytecode(emit, 1);
|
||||
c[0] = b1;
|
||||
}
|
||||
|
||||
// Similar to emit_write_bytecode_uint(), just some extra handling to encode sign
|
||||
STATIC void emit_write_bytecode_byte_int(emit_t *emit, int stack_adj, byte b1, mp_int_t num) {
|
||||
emit_write_bytecode_byte(emit, stack_adj, b1);
|
||||
|
||||
// We store each 7 bits in a separate byte, and that's how many bytes needed
|
||||
byte buf[BYTES_FOR_INT];
|
||||
byte *p = buf + sizeof(buf);
|
||||
// We encode in little-ending order, but store in big-endian, to help decoding
|
||||
do {
|
||||
*--p = num & 0x7f;
|
||||
num >>= 7;
|
||||
} while (num != 0 && num != -1);
|
||||
// Make sure that highest bit we stored (mask 0x40) matches sign
|
||||
// of the number. If not, store extra byte just to encode sign
|
||||
if (num == -1 && (*p & 0x40) == 0) {
|
||||
*--p = 0x7f;
|
||||
} else if (num == 0 && (*p & 0x40) != 0) {
|
||||
*--p = 0;
|
||||
}
|
||||
|
||||
byte *c = emit_get_cur_to_write_bytecode(emit, buf + sizeof(buf) - p);
|
||||
while (p != buf + sizeof(buf) - 1) {
|
||||
*c++ = *p++ | 0x80;
|
||||
}
|
||||
*c = *p;
|
||||
}
|
||||
|
||||
STATIC void emit_write_bytecode_byte_uint(emit_t *emit, int stack_adj, byte b, mp_uint_t val) {
|
||||
emit_write_bytecode_byte(emit, stack_adj, b);
|
||||
emit_write_uint(emit, emit_get_cur_to_write_bytecode, val);
|
||||
}
|
||||
|
||||
#if MICROPY_PERSISTENT_CODE
|
||||
STATIC void emit_write_bytecode_byte_const(emit_t *emit, int stack_adj, byte b, mp_uint_t n, mp_uint_t c) {
|
||||
if (emit->pass == MP_PASS_EMIT) {
|
||||
emit->const_table[n] = c;
|
||||
}
|
||||
emit_write_bytecode_byte_uint(emit, stack_adj, b, n);
|
||||
}
|
||||
#endif
|
||||
|
||||
STATIC void emit_write_bytecode_byte_qstr(emit_t *emit, int stack_adj, byte b, qstr qst) {
|
||||
#if MICROPY_PERSISTENT_CODE
|
||||
assert((qst >> 16) == 0);
|
||||
mp_emit_bc_adjust_stack_size(emit, stack_adj);
|
||||
byte *c = emit_get_cur_to_write_bytecode(emit, 3);
|
||||
c[0] = b;
|
||||
c[1] = qst;
|
||||
c[2] = qst >> 8;
|
||||
#else
|
||||
emit_write_bytecode_byte_uint(emit, stack_adj, b, qst);
|
||||
#endif
|
||||
}
|
||||
|
||||
STATIC void emit_write_bytecode_byte_obj(emit_t *emit, int stack_adj, byte b, mp_obj_t obj) {
|
||||
#if MICROPY_PERSISTENT_CODE
|
||||
emit_write_bytecode_byte_const(emit, stack_adj, b,
|
||||
emit->scope->num_pos_args + emit->scope->num_kwonly_args
|
||||
+ emit->ct_cur_obj++, (mp_uint_t)obj);
|
||||
#else
|
||||
// aligns the pointer so it is friendly to GC
|
||||
emit_write_bytecode_byte(emit, stack_adj, b);
|
||||
emit->bytecode_offset = (size_t)MP_ALIGN(emit->bytecode_offset, sizeof(mp_obj_t));
|
||||
mp_obj_t *c = (mp_obj_t *)emit_get_cur_to_write_bytecode(emit, sizeof(mp_obj_t));
|
||||
// Verify thar c is already uint-aligned
|
||||
assert(c == MP_ALIGN(c, sizeof(mp_obj_t)));
|
||||
*c = obj;
|
||||
#endif
|
||||
}
|
||||
|
||||
STATIC void emit_write_bytecode_byte_raw_code(emit_t *emit, int stack_adj, byte b, mp_raw_code_t *rc) {
|
||||
#if MICROPY_PERSISTENT_CODE
|
||||
emit_write_bytecode_byte_const(emit, stack_adj, b,
|
||||
emit->scope->num_pos_args + emit->scope->num_kwonly_args
|
||||
+ emit->ct_num_obj + emit->ct_cur_raw_code++, (mp_uint_t)(uintptr_t)rc);
|
||||
#else
|
||||
// aligns the pointer so it is friendly to GC
|
||||
emit_write_bytecode_byte(emit, stack_adj, b);
|
||||
emit->bytecode_offset = (size_t)MP_ALIGN(emit->bytecode_offset, sizeof(void *));
|
||||
void **c = (void **)emit_get_cur_to_write_bytecode(emit, sizeof(void *));
|
||||
// Verify thar c is already uint-aligned
|
||||
assert(c == MP_ALIGN(c, sizeof(void *)));
|
||||
*c = rc;
|
||||
#endif
|
||||
#if MICROPY_PY_SYS_SETTRACE
|
||||
rc->line_of_definition = emit->last_source_line;
|
||||
#endif
|
||||
}
|
||||
|
||||
// unsigned labels are relative to ip following this instruction, stored as 16 bits
|
||||
STATIC void emit_write_bytecode_byte_unsigned_label(emit_t *emit, int stack_adj, byte b1, mp_uint_t label) {
|
||||
mp_emit_bc_adjust_stack_size(emit, stack_adj);
|
||||
mp_uint_t bytecode_offset;
|
||||
if (emit->pass < MP_PASS_EMIT) {
|
||||
bytecode_offset = 0;
|
||||
} else {
|
||||
bytecode_offset = emit->label_offsets[label] - emit->bytecode_offset - 3;
|
||||
}
|
||||
byte *c = emit_get_cur_to_write_bytecode(emit, 3);
|
||||
c[0] = b1;
|
||||
c[1] = bytecode_offset;
|
||||
c[2] = bytecode_offset >> 8;
|
||||
}
|
||||
|
||||
// signed labels are relative to ip following this instruction, stored as 16 bits, in excess
|
||||
STATIC void emit_write_bytecode_byte_signed_label(emit_t *emit, int stack_adj, byte b1, mp_uint_t label) {
|
||||
mp_emit_bc_adjust_stack_size(emit, stack_adj);
|
||||
int bytecode_offset;
|
||||
if (emit->pass < MP_PASS_EMIT) {
|
||||
bytecode_offset = 0;
|
||||
} else {
|
||||
bytecode_offset = emit->label_offsets[label] - emit->bytecode_offset - 3 + 0x8000;
|
||||
}
|
||||
byte *c = emit_get_cur_to_write_bytecode(emit, 3);
|
||||
c[0] = b1;
|
||||
c[1] = bytecode_offset;
|
||||
c[2] = bytecode_offset >> 8;
|
||||
}
|
||||
|
||||
void mp_emit_bc_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scope) {
|
||||
emit->pass = pass;
|
||||
emit->stack_size = 0;
|
||||
emit->last_emit_was_return_value = false;
|
||||
emit->scope = scope;
|
||||
emit->last_source_line_offset = 0;
|
||||
emit->last_source_line = 1;
|
||||
#ifndef NDEBUG
|
||||
// With debugging enabled labels are checked for unique assignment
|
||||
if (pass < MP_PASS_EMIT && emit->label_offsets != NULL) {
|
||||
memset(emit->label_offsets, -1, emit->max_num_labels * sizeof(mp_uint_t));
|
||||
}
|
||||
#endif
|
||||
emit->bytecode_offset = 0;
|
||||
emit->code_info_offset = 0;
|
||||
|
||||
// Write local state size, exception stack size, scope flags and number of arguments
|
||||
{
|
||||
mp_uint_t n_state = scope->num_locals + scope->stack_size;
|
||||
if (n_state == 0) {
|
||||
// Need at least 1 entry in the state, in the case an exception is
|
||||
// propagated through this function, the exception is returned in
|
||||
// the highest slot in the state (fastn[0], see vm.c).
|
||||
n_state = 1;
|
||||
}
|
||||
#if MICROPY_DEBUG_VM_STACK_OVERFLOW
|
||||
// An extra slot in the stack is needed to detect VM stack overflow
|
||||
n_state += 1;
|
||||
#endif
|
||||
|
||||
size_t n_exc_stack = scope->exc_stack_size;
|
||||
MP_BC_PRELUDE_SIG_ENCODE(n_state, n_exc_stack, scope, emit_write_code_info_byte, emit);
|
||||
}
|
||||
|
||||
// Write number of cells and size of the source code info
|
||||
if (pass >= MP_PASS_CODE_SIZE) {
|
||||
MP_BC_PRELUDE_SIZE_ENCODE(emit->n_info, emit->n_cell, emit_write_code_info_byte, emit);
|
||||
}
|
||||
|
||||
emit->n_info = emit->code_info_offset;
|
||||
|
||||
// Write the name and source file of this function.
|
||||
emit_write_code_info_qstr(emit, scope->simple_name);
|
||||
emit_write_code_info_qstr(emit, scope->source_file);
|
||||
|
||||
#if MICROPY_PERSISTENT_CODE
|
||||
emit->ct_cur_obj = 0;
|
||||
emit->ct_cur_raw_code = 0;
|
||||
#endif
|
||||
|
||||
if (pass == MP_PASS_EMIT) {
|
||||
// Write argument names (needed to resolve positional args passed as
|
||||
// keywords). We store them as full word-sized objects for efficient access
|
||||
// in mp_setup_code_state this is the start of the prelude and is guaranteed
|
||||
// to be aligned on a word boundary.
|
||||
|
||||
// For a given argument position (indexed by i) we need to find the
|
||||
// corresponding id_info which is a parameter, as it has the correct
|
||||
// qstr name to use as the argument name. Note that it's not a simple
|
||||
// 1-1 mapping (ie i!=j in general) because of possible closed-over
|
||||
// variables. In the case that the argument i has no corresponding
|
||||
// parameter we use "*" as its name (since no argument can ever be named
|
||||
// "*"). We could use a blank qstr but "*" is better for debugging.
|
||||
// Note: there is some wasted RAM here for the case of storing a qstr
|
||||
// for each closed-over variable, and maybe there is a better way to do
|
||||
// it, but that would require changes to mp_setup_code_state.
|
||||
for (int i = 0; i < scope->num_pos_args + scope->num_kwonly_args; i++) {
|
||||
qstr qst = MP_QSTR__star_;
|
||||
for (int j = 0; j < scope->id_info_len; ++j) {
|
||||
id_info_t *id = &scope->id_info[j];
|
||||
if ((id->flags & ID_FLAG_IS_PARAM) && id->local_num == i) {
|
||||
qst = id->qst;
|
||||
break;
|
||||
}
|
||||
}
|
||||
emit->const_table[i] = (mp_uint_t)MP_OBJ_NEW_QSTR(qst);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mp_emit_bc_end_pass(emit_t *emit) {
|
||||
if (emit->pass == MP_PASS_SCOPE) {
|
||||
return;
|
||||
}
|
||||
|
||||
// check stack is back to zero size
|
||||
assert(emit->stack_size == 0);
|
||||
|
||||
emit_write_code_info_byte(emit, 0); // end of line number info
|
||||
|
||||
// Calculate size of source code info section
|
||||
emit->n_info = emit->code_info_offset - emit->n_info;
|
||||
|
||||
// Emit closure section of prelude
|
||||
emit->n_cell = 0;
|
||||
for (size_t i = 0; i < emit->scope->id_info_len; ++i) {
|
||||
id_info_t *id = &emit->scope->id_info[i];
|
||||
if (id->kind == ID_INFO_KIND_CELL) {
|
||||
assert(id->local_num <= 255);
|
||||
emit_write_code_info_byte(emit, id->local_num); // write the local which should be converted to a cell
|
||||
++emit->n_cell;
|
||||
}
|
||||
}
|
||||
|
||||
#if MICROPY_PERSISTENT_CODE
|
||||
assert(emit->pass <= MP_PASS_STACK_SIZE || (emit->ct_num_obj == emit->ct_cur_obj));
|
||||
emit->ct_num_obj = emit->ct_cur_obj;
|
||||
#endif
|
||||
|
||||
if (emit->pass == MP_PASS_CODE_SIZE) {
|
||||
#if !MICROPY_PERSISTENT_CODE
|
||||
// so bytecode is aligned
|
||||
emit->code_info_offset = (size_t)MP_ALIGN(emit->code_info_offset, sizeof(mp_uint_t));
|
||||
#endif
|
||||
|
||||
// calculate size of total code-info + bytecode, in bytes
|
||||
emit->code_info_size = emit->code_info_offset;
|
||||
emit->bytecode_size = emit->bytecode_offset;
|
||||
emit->code_base = m_new0(byte, emit->code_info_size + emit->bytecode_size);
|
||||
|
||||
#if MICROPY_PERSISTENT_CODE
|
||||
emit->const_table = m_new0(mp_uint_t,
|
||||
emit->scope->num_pos_args + emit->scope->num_kwonly_args
|
||||
+ emit->ct_cur_obj + emit->ct_cur_raw_code);
|
||||
#else
|
||||
emit->const_table = m_new0(mp_uint_t,
|
||||
emit->scope->num_pos_args + emit->scope->num_kwonly_args);
|
||||
#endif
|
||||
|
||||
} else if (emit->pass == MP_PASS_EMIT) {
|
||||
mp_emit_glue_assign_bytecode(emit->scope->raw_code, emit->code_base,
|
||||
#if MICROPY_PERSISTENT_CODE_SAVE || MICROPY_DEBUG_PRINTERS
|
||||
emit->code_info_size + emit->bytecode_size,
|
||||
#endif
|
||||
emit->const_table,
|
||||
#if MICROPY_PERSISTENT_CODE_SAVE
|
||||
emit->ct_cur_obj, emit->ct_cur_raw_code,
|
||||
#endif
|
||||
emit->scope->scope_flags);
|
||||
}
|
||||
}
|
||||
|
||||
bool mp_emit_bc_last_emit_was_return_value(emit_t *emit) {
|
||||
return emit->last_emit_was_return_value;
|
||||
}
|
||||
|
||||
void mp_emit_bc_adjust_stack_size(emit_t *emit, mp_int_t delta) {
|
||||
if (emit->pass == MP_PASS_SCOPE) {
|
||||
return;
|
||||
}
|
||||
assert((mp_int_t)emit->stack_size + delta >= 0);
|
||||
emit->stack_size += delta;
|
||||
if (emit->stack_size > emit->scope->stack_size) {
|
||||
emit->scope->stack_size = emit->stack_size;
|
||||
}
|
||||
emit->last_emit_was_return_value = false;
|
||||
}
|
||||
|
||||
void mp_emit_bc_set_source_line(emit_t *emit, mp_uint_t source_line) {
|
||||
#if MICROPY_ENABLE_SOURCE_LINE
|
||||
if (MP_STATE_VM(mp_optimise_value) >= 3) {
|
||||
// If we compile with -O3, don't store line numbers.
|
||||
return;
|
||||
}
|
||||
if (source_line > emit->last_source_line) {
|
||||
mp_uint_t bytes_to_skip = emit->bytecode_offset - emit->last_source_line_offset;
|
||||
mp_uint_t lines_to_skip = source_line - emit->last_source_line;
|
||||
emit_write_code_info_bytes_lines(emit, bytes_to_skip, lines_to_skip);
|
||||
emit->last_source_line_offset = emit->bytecode_offset;
|
||||
emit->last_source_line = source_line;
|
||||
}
|
||||
#else
|
||||
(void)emit;
|
||||
(void)source_line;
|
||||
#endif
|
||||
}
|
||||
|
||||
void mp_emit_bc_label_assign(emit_t *emit, mp_uint_t l) {
|
||||
mp_emit_bc_adjust_stack_size(emit, 0);
|
||||
if (emit->pass == MP_PASS_SCOPE) {
|
||||
return;
|
||||
}
|
||||
assert(l < emit->max_num_labels);
|
||||
if (emit->pass < MP_PASS_EMIT) {
|
||||
// assign label offset
|
||||
assert(emit->label_offsets[l] == (mp_uint_t)-1);
|
||||
emit->label_offsets[l] = emit->bytecode_offset;
|
||||
} else {
|
||||
// ensure label offset has not changed from MP_PASS_CODE_SIZE to MP_PASS_EMIT
|
||||
assert(emit->label_offsets[l] == emit->bytecode_offset);
|
||||
}
|
||||
}
|
||||
|
||||
void mp_emit_bc_import(emit_t *emit, qstr qst, int kind) {
|
||||
MP_STATIC_ASSERT(MP_BC_IMPORT_NAME + MP_EMIT_IMPORT_NAME == MP_BC_IMPORT_NAME);
|
||||
MP_STATIC_ASSERT(MP_BC_IMPORT_NAME + MP_EMIT_IMPORT_FROM == MP_BC_IMPORT_FROM);
|
||||
int stack_adj = kind == MP_EMIT_IMPORT_FROM ? 1 : -1;
|
||||
if (kind == MP_EMIT_IMPORT_STAR) {
|
||||
emit_write_bytecode_byte(emit, stack_adj, MP_BC_IMPORT_STAR);
|
||||
} else {
|
||||
emit_write_bytecode_byte_qstr(emit, stack_adj, MP_BC_IMPORT_NAME + kind, qst);
|
||||
}
|
||||
}
|
||||
|
||||
void mp_emit_bc_load_const_tok(emit_t *emit, mp_token_kind_t tok) {
|
||||
MP_STATIC_ASSERT(MP_BC_LOAD_CONST_FALSE + (MP_TOKEN_KW_NONE - MP_TOKEN_KW_FALSE) == MP_BC_LOAD_CONST_NONE);
|
||||
MP_STATIC_ASSERT(MP_BC_LOAD_CONST_FALSE + (MP_TOKEN_KW_TRUE - MP_TOKEN_KW_FALSE) == MP_BC_LOAD_CONST_TRUE);
|
||||
if (tok == MP_TOKEN_ELLIPSIS) {
|
||||
emit_write_bytecode_byte_obj(emit, 1, MP_BC_LOAD_CONST_OBJ, MP_OBJ_FROM_PTR(&mp_const_ellipsis_obj));
|
||||
} else {
|
||||
emit_write_bytecode_byte(emit, 1, MP_BC_LOAD_CONST_FALSE + (tok - MP_TOKEN_KW_FALSE));
|
||||
}
|
||||
}
|
||||
|
||||
void mp_emit_bc_load_const_small_int(emit_t *emit, mp_int_t arg) {
|
||||
if (-MP_BC_LOAD_CONST_SMALL_INT_MULTI_EXCESS <= arg
|
||||
&& arg < MP_BC_LOAD_CONST_SMALL_INT_MULTI_NUM - MP_BC_LOAD_CONST_SMALL_INT_MULTI_EXCESS) {
|
||||
emit_write_bytecode_byte(emit, 1,
|
||||
MP_BC_LOAD_CONST_SMALL_INT_MULTI + MP_BC_LOAD_CONST_SMALL_INT_MULTI_EXCESS + arg);
|
||||
} else {
|
||||
emit_write_bytecode_byte_int(emit, 1, MP_BC_LOAD_CONST_SMALL_INT, arg);
|
||||
}
|
||||
}
|
||||
|
||||
void mp_emit_bc_load_const_str(emit_t *emit, qstr qst) {
|
||||
emit_write_bytecode_byte_qstr(emit, 1, MP_BC_LOAD_CONST_STRING, qst);
|
||||
}
|
||||
|
||||
void mp_emit_bc_load_const_obj(emit_t *emit, mp_obj_t obj) {
|
||||
emit_write_bytecode_byte_obj(emit, 1, MP_BC_LOAD_CONST_OBJ, obj);
|
||||
}
|
||||
|
||||
void mp_emit_bc_load_null(emit_t *emit) {
|
||||
emit_write_bytecode_byte(emit, 1, MP_BC_LOAD_NULL);
|
||||
}
|
||||
|
||||
void mp_emit_bc_load_local(emit_t *emit, qstr qst, mp_uint_t local_num, int kind) {
|
||||
MP_STATIC_ASSERT(MP_BC_LOAD_FAST_N + MP_EMIT_IDOP_LOCAL_FAST == MP_BC_LOAD_FAST_N);
|
||||
MP_STATIC_ASSERT(MP_BC_LOAD_FAST_N + MP_EMIT_IDOP_LOCAL_DEREF == MP_BC_LOAD_DEREF);
|
||||
(void)qst;
|
||||
if (kind == MP_EMIT_IDOP_LOCAL_FAST && local_num <= 15) {
|
||||
emit_write_bytecode_byte(emit, 1, MP_BC_LOAD_FAST_MULTI + local_num);
|
||||
} else {
|
||||
emit_write_bytecode_byte_uint(emit, 1, MP_BC_LOAD_FAST_N + kind, local_num);
|
||||
}
|
||||
}
|
||||
|
||||
void mp_emit_bc_load_global(emit_t *emit, qstr qst, int kind) {
|
||||
MP_STATIC_ASSERT(MP_BC_LOAD_NAME + MP_EMIT_IDOP_GLOBAL_NAME == MP_BC_LOAD_NAME);
|
||||
MP_STATIC_ASSERT(MP_BC_LOAD_NAME + MP_EMIT_IDOP_GLOBAL_GLOBAL == MP_BC_LOAD_GLOBAL);
|
||||
(void)qst;
|
||||
emit_write_bytecode_byte_qstr(emit, 1, MP_BC_LOAD_NAME + kind, qst);
|
||||
if (MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE_DYNAMIC) {
|
||||
emit_write_bytecode_raw_byte(emit, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void mp_emit_bc_load_method(emit_t *emit, qstr qst, bool is_super) {
|
||||
int stack_adj = 1 - 2 * is_super;
|
||||
emit_write_bytecode_byte_qstr(emit, stack_adj, is_super ? MP_BC_LOAD_SUPER_METHOD : MP_BC_LOAD_METHOD, qst);
|
||||
}
|
||||
|
||||
void mp_emit_bc_load_build_class(emit_t *emit) {
|
||||
emit_write_bytecode_byte(emit, 1, MP_BC_LOAD_BUILD_CLASS);
|
||||
}
|
||||
|
||||
void mp_emit_bc_subscr(emit_t *emit, int kind) {
|
||||
if (kind == MP_EMIT_SUBSCR_LOAD) {
|
||||
emit_write_bytecode_byte(emit, -1, MP_BC_LOAD_SUBSCR);
|
||||
} else {
|
||||
if (kind == MP_EMIT_SUBSCR_DELETE) {
|
||||
mp_emit_bc_load_null(emit);
|
||||
mp_emit_bc_rot_three(emit);
|
||||
}
|
||||
emit_write_bytecode_byte(emit, -3, MP_BC_STORE_SUBSCR);
|
||||
}
|
||||
}
|
||||
|
||||
void mp_emit_bc_attr(emit_t *emit, qstr qst, int kind) {
|
||||
if (kind == MP_EMIT_ATTR_LOAD) {
|
||||
emit_write_bytecode_byte_qstr(emit, 0, MP_BC_LOAD_ATTR, qst);
|
||||
} else {
|
||||
if (kind == MP_EMIT_ATTR_DELETE) {
|
||||
mp_emit_bc_load_null(emit);
|
||||
mp_emit_bc_rot_two(emit);
|
||||
}
|
||||
emit_write_bytecode_byte_qstr(emit, -2, MP_BC_STORE_ATTR, qst);
|
||||
}
|
||||
if (MICROPY_OPT_CACHE_MAP_LOOKUP_IN_BYTECODE_DYNAMIC) {
|
||||
emit_write_bytecode_raw_byte(emit, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void mp_emit_bc_store_local(emit_t *emit, qstr qst, mp_uint_t local_num, int kind) {
|
||||
MP_STATIC_ASSERT(MP_BC_STORE_FAST_N + MP_EMIT_IDOP_LOCAL_FAST == MP_BC_STORE_FAST_N);
|
||||
MP_STATIC_ASSERT(MP_BC_STORE_FAST_N + MP_EMIT_IDOP_LOCAL_DEREF == MP_BC_STORE_DEREF);
|
||||
(void)qst;
|
||||
if (kind == MP_EMIT_IDOP_LOCAL_FAST && local_num <= 15) {
|
||||
emit_write_bytecode_byte(emit, -1, MP_BC_STORE_FAST_MULTI + local_num);
|
||||
} else {
|
||||
emit_write_bytecode_byte_uint(emit, -1, MP_BC_STORE_FAST_N + kind, local_num);
|
||||
}
|
||||
}
|
||||
|
||||
void mp_emit_bc_store_global(emit_t *emit, qstr qst, int kind) {
|
||||
MP_STATIC_ASSERT(MP_BC_STORE_NAME + MP_EMIT_IDOP_GLOBAL_NAME == MP_BC_STORE_NAME);
|
||||
MP_STATIC_ASSERT(MP_BC_STORE_NAME + MP_EMIT_IDOP_GLOBAL_GLOBAL == MP_BC_STORE_GLOBAL);
|
||||
emit_write_bytecode_byte_qstr(emit, -1, MP_BC_STORE_NAME + kind, qst);
|
||||
}
|
||||
|
||||
void mp_emit_bc_delete_local(emit_t *emit, qstr qst, mp_uint_t local_num, int kind) {
|
||||
MP_STATIC_ASSERT(MP_BC_DELETE_FAST + MP_EMIT_IDOP_LOCAL_FAST == MP_BC_DELETE_FAST);
|
||||
MP_STATIC_ASSERT(MP_BC_DELETE_FAST + MP_EMIT_IDOP_LOCAL_DEREF == MP_BC_DELETE_DEREF);
|
||||
(void)qst;
|
||||
emit_write_bytecode_byte_uint(emit, 0, MP_BC_DELETE_FAST + kind, local_num);
|
||||
}
|
||||
|
||||
void mp_emit_bc_delete_global(emit_t *emit, qstr qst, int kind) {
|
||||
MP_STATIC_ASSERT(MP_BC_DELETE_NAME + MP_EMIT_IDOP_GLOBAL_NAME == MP_BC_DELETE_NAME);
|
||||
MP_STATIC_ASSERT(MP_BC_DELETE_NAME + MP_EMIT_IDOP_GLOBAL_GLOBAL == MP_BC_DELETE_GLOBAL);
|
||||
emit_write_bytecode_byte_qstr(emit, 0, MP_BC_DELETE_NAME + kind, qst);
|
||||
}
|
||||
|
||||
void mp_emit_bc_dup_top(emit_t *emit) {
|
||||
emit_write_bytecode_byte(emit, 1, MP_BC_DUP_TOP);
|
||||
}
|
||||
|
||||
void mp_emit_bc_dup_top_two(emit_t *emit) {
|
||||
emit_write_bytecode_byte(emit, 2, MP_BC_DUP_TOP_TWO);
|
||||
}
|
||||
|
||||
void mp_emit_bc_pop_top(emit_t *emit) {
|
||||
emit_write_bytecode_byte(emit, -1, MP_BC_POP_TOP);
|
||||
}
|
||||
|
||||
void mp_emit_bc_rot_two(emit_t *emit) {
|
||||
emit_write_bytecode_byte(emit, 0, MP_BC_ROT_TWO);
|
||||
}
|
||||
|
||||
void mp_emit_bc_rot_three(emit_t *emit) {
|
||||
emit_write_bytecode_byte(emit, 0, MP_BC_ROT_THREE);
|
||||
}
|
||||
|
||||
void mp_emit_bc_jump(emit_t *emit, mp_uint_t label) {
|
||||
emit_write_bytecode_byte_signed_label(emit, 0, MP_BC_JUMP, label);
|
||||
}
|
||||
|
||||
void mp_emit_bc_pop_jump_if(emit_t *emit, bool cond, mp_uint_t label) {
|
||||
if (cond) {
|
||||
emit_write_bytecode_byte_signed_label(emit, -1, MP_BC_POP_JUMP_IF_TRUE, label);
|
||||
} else {
|
||||
emit_write_bytecode_byte_signed_label(emit, -1, MP_BC_POP_JUMP_IF_FALSE, label);
|
||||
}
|
||||
}
|
||||
|
||||
void mp_emit_bc_jump_if_or_pop(emit_t *emit, bool cond, mp_uint_t label) {
|
||||
if (cond) {
|
||||
emit_write_bytecode_byte_signed_label(emit, -1, MP_BC_JUMP_IF_TRUE_OR_POP, label);
|
||||
} else {
|
||||
emit_write_bytecode_byte_signed_label(emit, -1, MP_BC_JUMP_IF_FALSE_OR_POP, label);
|
||||
}
|
||||
}
|
||||
|
||||
void mp_emit_bc_unwind_jump(emit_t *emit, mp_uint_t label, mp_uint_t except_depth) {
|
||||
if (except_depth == 0) {
|
||||
if (label & MP_EMIT_BREAK_FROM_FOR) {
|
||||
// need to pop the iterator if we are breaking out of a for loop
|
||||
emit_write_bytecode_raw_byte(emit, MP_BC_POP_TOP);
|
||||
// also pop the iter_buf
|
||||
for (size_t i = 0; i < MP_OBJ_ITER_BUF_NSLOTS - 1; ++i) {
|
||||
emit_write_bytecode_raw_byte(emit, MP_BC_POP_TOP);
|
||||
}
|
||||
}
|
||||
emit_write_bytecode_byte_signed_label(emit, 0, MP_BC_JUMP, label & ~MP_EMIT_BREAK_FROM_FOR);
|
||||
} else {
|
||||
emit_write_bytecode_byte_signed_label(emit, 0, MP_BC_UNWIND_JUMP, label & ~MP_EMIT_BREAK_FROM_FOR);
|
||||
emit_write_bytecode_raw_byte(emit, ((label & MP_EMIT_BREAK_FROM_FOR) ? 0x80 : 0) | except_depth);
|
||||
}
|
||||
}
|
||||
|
||||
void mp_emit_bc_setup_block(emit_t *emit, mp_uint_t label, int kind) {
|
||||
MP_STATIC_ASSERT(MP_BC_SETUP_WITH + MP_EMIT_SETUP_BLOCK_WITH == MP_BC_SETUP_WITH);
|
||||
MP_STATIC_ASSERT(MP_BC_SETUP_WITH + MP_EMIT_SETUP_BLOCK_EXCEPT == MP_BC_SETUP_EXCEPT);
|
||||
MP_STATIC_ASSERT(MP_BC_SETUP_WITH + MP_EMIT_SETUP_BLOCK_FINALLY == MP_BC_SETUP_FINALLY);
|
||||
// The SETUP_WITH opcode pops ctx_mgr from the top of the stack
|
||||
// and then pushes 3 entries: __exit__, ctx_mgr, as_value.
|
||||
int stack_adj = kind == MP_EMIT_SETUP_BLOCK_WITH ? 2 : 0;
|
||||
emit_write_bytecode_byte_unsigned_label(emit, stack_adj, MP_BC_SETUP_WITH + kind, label);
|
||||
}
|
||||
|
||||
void mp_emit_bc_with_cleanup(emit_t *emit, mp_uint_t label) {
|
||||
mp_emit_bc_load_const_tok(emit, MP_TOKEN_KW_NONE);
|
||||
mp_emit_bc_label_assign(emit, label);
|
||||
// The +2 is to ensure we have enough stack space to call the __exit__ method
|
||||
emit_write_bytecode_byte(emit, 2, MP_BC_WITH_CLEANUP);
|
||||
// Cancel the +2 above, plus the +2 from mp_emit_bc_setup_block(MP_EMIT_SETUP_BLOCK_WITH)
|
||||
mp_emit_bc_adjust_stack_size(emit, -4);
|
||||
}
|
||||
|
||||
void mp_emit_bc_end_finally(emit_t *emit) {
|
||||
emit_write_bytecode_byte(emit, -1, MP_BC_END_FINALLY);
|
||||
}
|
||||
|
||||
void mp_emit_bc_get_iter(emit_t *emit, bool use_stack) {
|
||||
int stack_adj = use_stack ? MP_OBJ_ITER_BUF_NSLOTS - 1 : 0;
|
||||
emit_write_bytecode_byte(emit, stack_adj, use_stack ? MP_BC_GET_ITER_STACK : MP_BC_GET_ITER);
|
||||
}
|
||||
|
||||
void mp_emit_bc_for_iter(emit_t *emit, mp_uint_t label) {
|
||||
emit_write_bytecode_byte_unsigned_label(emit, 1, MP_BC_FOR_ITER, label);
|
||||
}
|
||||
|
||||
void mp_emit_bc_for_iter_end(emit_t *emit) {
|
||||
mp_emit_bc_adjust_stack_size(emit, -MP_OBJ_ITER_BUF_NSLOTS);
|
||||
}
|
||||
|
||||
void mp_emit_bc_pop_except_jump(emit_t *emit, mp_uint_t label, bool within_exc_handler) {
|
||||
(void)within_exc_handler;
|
||||
emit_write_bytecode_byte_unsigned_label(emit, 0, MP_BC_POP_EXCEPT_JUMP, label);
|
||||
}
|
||||
|
||||
void mp_emit_bc_unary_op(emit_t *emit, mp_unary_op_t op) {
|
||||
emit_write_bytecode_byte(emit, 0, MP_BC_UNARY_OP_MULTI + op);
|
||||
}
|
||||
|
||||
void mp_emit_bc_binary_op(emit_t *emit, mp_binary_op_t op) {
|
||||
bool invert = false;
|
||||
if (op == MP_BINARY_OP_NOT_IN) {
|
||||
invert = true;
|
||||
op = MP_BINARY_OP_IN;
|
||||
} else if (op == MP_BINARY_OP_IS_NOT) {
|
||||
invert = true;
|
||||
op = MP_BINARY_OP_IS;
|
||||
}
|
||||
emit_write_bytecode_byte(emit, -1, MP_BC_BINARY_OP_MULTI + op);
|
||||
if (invert) {
|
||||
emit_write_bytecode_byte(emit, 0, MP_BC_UNARY_OP_MULTI + MP_UNARY_OP_NOT);
|
||||
}
|
||||
}
|
||||
|
||||
void mp_emit_bc_build(emit_t *emit, mp_uint_t n_args, int kind) {
|
||||
MP_STATIC_ASSERT(MP_BC_BUILD_TUPLE + MP_EMIT_BUILD_TUPLE == MP_BC_BUILD_TUPLE);
|
||||
MP_STATIC_ASSERT(MP_BC_BUILD_TUPLE + MP_EMIT_BUILD_LIST == MP_BC_BUILD_LIST);
|
||||
MP_STATIC_ASSERT(MP_BC_BUILD_TUPLE + MP_EMIT_BUILD_MAP == MP_BC_BUILD_MAP);
|
||||
MP_STATIC_ASSERT(MP_BC_BUILD_TUPLE + MP_EMIT_BUILD_SET == MP_BC_BUILD_SET);
|
||||
MP_STATIC_ASSERT(MP_BC_BUILD_TUPLE + MP_EMIT_BUILD_SLICE == MP_BC_BUILD_SLICE);
|
||||
int stack_adj = kind == MP_EMIT_BUILD_MAP ? 1 : 1 - n_args;
|
||||
emit_write_bytecode_byte_uint(emit, stack_adj, MP_BC_BUILD_TUPLE + kind, n_args);
|
||||
}
|
||||
|
||||
void mp_emit_bc_store_map(emit_t *emit) {
|
||||
emit_write_bytecode_byte(emit, -2, MP_BC_STORE_MAP);
|
||||
}
|
||||
|
||||
void mp_emit_bc_store_comp(emit_t *emit, scope_kind_t kind, mp_uint_t collection_stack_index) {
|
||||
int t;
|
||||
int n;
|
||||
if (kind == SCOPE_LIST_COMP) {
|
||||
n = 0;
|
||||
t = 0;
|
||||
} else if (!MICROPY_PY_BUILTINS_SET || kind == SCOPE_DICT_COMP) {
|
||||
n = 1;
|
||||
t = 1;
|
||||
} else if (MICROPY_PY_BUILTINS_SET) {
|
||||
n = 0;
|
||||
t = 2;
|
||||
}
|
||||
// the lower 2 bits of the opcode argument indicate the collection type
|
||||
emit_write_bytecode_byte_uint(emit, -1 - n, MP_BC_STORE_COMP, ((collection_stack_index + n) << 2) | t);
|
||||
}
|
||||
|
||||
void mp_emit_bc_unpack_sequence(emit_t *emit, mp_uint_t n_args) {
|
||||
emit_write_bytecode_byte_uint(emit, -1 + n_args, MP_BC_UNPACK_SEQUENCE, n_args);
|
||||
}
|
||||
|
||||
void mp_emit_bc_unpack_ex(emit_t *emit, mp_uint_t n_left, mp_uint_t n_right) {
|
||||
emit_write_bytecode_byte_uint(emit, -1 + n_left + n_right + 1, MP_BC_UNPACK_EX, n_left | (n_right << 8));
|
||||
}
|
||||
|
||||
void mp_emit_bc_make_function(emit_t *emit, scope_t *scope, mp_uint_t n_pos_defaults, mp_uint_t n_kw_defaults) {
|
||||
if (n_pos_defaults == 0 && n_kw_defaults == 0) {
|
||||
emit_write_bytecode_byte_raw_code(emit, 1, MP_BC_MAKE_FUNCTION, scope->raw_code);
|
||||
} else {
|
||||
emit_write_bytecode_byte_raw_code(emit, -1, MP_BC_MAKE_FUNCTION_DEFARGS, scope->raw_code);
|
||||
}
|
||||
}
|
||||
|
||||
void mp_emit_bc_make_closure(emit_t *emit, scope_t *scope, mp_uint_t n_closed_over, mp_uint_t n_pos_defaults, mp_uint_t n_kw_defaults) {
|
||||
if (n_pos_defaults == 0 && n_kw_defaults == 0) {
|
||||
int stack_adj = -n_closed_over + 1;
|
||||
emit_write_bytecode_byte_raw_code(emit, stack_adj, MP_BC_MAKE_CLOSURE, scope->raw_code);
|
||||
emit_write_bytecode_raw_byte(emit, n_closed_over);
|
||||
} else {
|
||||
assert(n_closed_over <= 255);
|
||||
int stack_adj = -2 - (mp_int_t)n_closed_over + 1;
|
||||
emit_write_bytecode_byte_raw_code(emit, stack_adj, MP_BC_MAKE_CLOSURE_DEFARGS, scope->raw_code);
|
||||
emit_write_bytecode_raw_byte(emit, n_closed_over);
|
||||
}
|
||||
}
|
||||
|
||||
STATIC void emit_bc_call_function_method_helper(emit_t *emit, int stack_adj, mp_uint_t bytecode_base, mp_uint_t n_positional, mp_uint_t n_keyword, mp_uint_t star_flags) {
|
||||
if (star_flags) {
|
||||
stack_adj -= (int)n_positional + 2 * (int)n_keyword + 2;
|
||||
emit_write_bytecode_byte_uint(emit, stack_adj, bytecode_base + 1, (n_keyword << 8) | n_positional); // TODO make it 2 separate uints?
|
||||
} else {
|
||||
stack_adj -= (int)n_positional + 2 * (int)n_keyword;
|
||||
emit_write_bytecode_byte_uint(emit, stack_adj, bytecode_base, (n_keyword << 8) | n_positional); // TODO make it 2 separate uints?
|
||||
}
|
||||
}
|
||||
|
||||
void mp_emit_bc_call_function(emit_t *emit, mp_uint_t n_positional, mp_uint_t n_keyword, mp_uint_t star_flags) {
|
||||
emit_bc_call_function_method_helper(emit, 0, MP_BC_CALL_FUNCTION, n_positional, n_keyword, star_flags);
|
||||
}
|
||||
|
||||
void mp_emit_bc_call_method(emit_t *emit, mp_uint_t n_positional, mp_uint_t n_keyword, mp_uint_t star_flags) {
|
||||
emit_bc_call_function_method_helper(emit, -1, MP_BC_CALL_METHOD, n_positional, n_keyword, star_flags);
|
||||
}
|
||||
|
||||
void mp_emit_bc_return_value(emit_t *emit) {
|
||||
emit_write_bytecode_byte(emit, -1, MP_BC_RETURN_VALUE);
|
||||
emit->last_emit_was_return_value = true;
|
||||
}
|
||||
|
||||
void mp_emit_bc_raise_varargs(emit_t *emit, mp_uint_t n_args) {
|
||||
MP_STATIC_ASSERT(MP_BC_RAISE_LAST + 1 == MP_BC_RAISE_OBJ);
|
||||
MP_STATIC_ASSERT(MP_BC_RAISE_LAST + 2 == MP_BC_RAISE_FROM);
|
||||
assert(n_args <= 2);
|
||||
emit_write_bytecode_byte(emit, -n_args, MP_BC_RAISE_LAST + n_args);
|
||||
}
|
||||
|
||||
void mp_emit_bc_yield(emit_t *emit, int kind) {
|
||||
MP_STATIC_ASSERT(MP_BC_YIELD_VALUE + 1 == MP_BC_YIELD_FROM);
|
||||
emit_write_bytecode_byte(emit, -kind, MP_BC_YIELD_VALUE + kind);
|
||||
emit->scope->scope_flags |= MP_SCOPE_FLAG_GENERATOR;
|
||||
}
|
||||
|
||||
void mp_emit_bc_start_except_handler(emit_t *emit) {
|
||||
mp_emit_bc_adjust_stack_size(emit, 4); // stack adjust for the exception instance, +3 for possible UNWIND_JUMP state
|
||||
}
|
||||
|
||||
void mp_emit_bc_end_except_handler(emit_t *emit) {
|
||||
mp_emit_bc_adjust_stack_size(emit, -3); // stack adjust
|
||||
}
|
||||
|
||||
#if MICROPY_EMIT_NATIVE
|
||||
const emit_method_table_t emit_bc_method_table = {
|
||||
#if MICROPY_DYNAMIC_COMPILER
|
||||
NULL,
|
||||
NULL,
|
||||
#endif
|
||||
|
||||
mp_emit_bc_start_pass,
|
||||
mp_emit_bc_end_pass,
|
||||
mp_emit_bc_last_emit_was_return_value,
|
||||
mp_emit_bc_adjust_stack_size,
|
||||
mp_emit_bc_set_source_line,
|
||||
|
||||
{
|
||||
mp_emit_bc_load_local,
|
||||
mp_emit_bc_load_global,
|
||||
},
|
||||
{
|
||||
mp_emit_bc_store_local,
|
||||
mp_emit_bc_store_global,
|
||||
},
|
||||
{
|
||||
mp_emit_bc_delete_local,
|
||||
mp_emit_bc_delete_global,
|
||||
},
|
||||
|
||||
mp_emit_bc_label_assign,
|
||||
mp_emit_bc_import,
|
||||
mp_emit_bc_load_const_tok,
|
||||
mp_emit_bc_load_const_small_int,
|
||||
mp_emit_bc_load_const_str,
|
||||
mp_emit_bc_load_const_obj,
|
||||
mp_emit_bc_load_null,
|
||||
mp_emit_bc_load_method,
|
||||
mp_emit_bc_load_build_class,
|
||||
mp_emit_bc_subscr,
|
||||
mp_emit_bc_attr,
|
||||
mp_emit_bc_dup_top,
|
||||
mp_emit_bc_dup_top_two,
|
||||
mp_emit_bc_pop_top,
|
||||
mp_emit_bc_rot_two,
|
||||
mp_emit_bc_rot_three,
|
||||
mp_emit_bc_jump,
|
||||
mp_emit_bc_pop_jump_if,
|
||||
mp_emit_bc_jump_if_or_pop,
|
||||
mp_emit_bc_unwind_jump,
|
||||
mp_emit_bc_setup_block,
|
||||
mp_emit_bc_with_cleanup,
|
||||
mp_emit_bc_end_finally,
|
||||
mp_emit_bc_get_iter,
|
||||
mp_emit_bc_for_iter,
|
||||
mp_emit_bc_for_iter_end,
|
||||
mp_emit_bc_pop_except_jump,
|
||||
mp_emit_bc_unary_op,
|
||||
mp_emit_bc_binary_op,
|
||||
mp_emit_bc_build,
|
||||
mp_emit_bc_store_map,
|
||||
mp_emit_bc_store_comp,
|
||||
mp_emit_bc_unpack_sequence,
|
||||
mp_emit_bc_unpack_ex,
|
||||
mp_emit_bc_make_function,
|
||||
mp_emit_bc_make_closure,
|
||||
mp_emit_bc_call_function,
|
||||
mp_emit_bc_call_method,
|
||||
mp_emit_bc_return_value,
|
||||
mp_emit_bc_raise_varargs,
|
||||
mp_emit_bc_yield,
|
||||
|
||||
mp_emit_bc_start_except_handler,
|
||||
mp_emit_bc_end_except_handler,
|
||||
};
|
||||
#else
|
||||
const mp_emit_method_table_id_ops_t mp_emit_bc_method_table_load_id_ops = {
|
||||
mp_emit_bc_load_local,
|
||||
mp_emit_bc_load_global,
|
||||
};
|
||||
|
||||
const mp_emit_method_table_id_ops_t mp_emit_bc_method_table_store_id_ops = {
|
||||
mp_emit_bc_store_local,
|
||||
mp_emit_bc_store_global,
|
||||
};
|
||||
|
||||
const mp_emit_method_table_id_ops_t mp_emit_bc_method_table_delete_id_ops = {
|
||||
mp_emit_bc_delete_local,
|
||||
mp_emit_bc_delete_global,
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif // MICROPY_ENABLE_COMPILER
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013, 2014 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "py/emit.h"
|
||||
|
||||
#if MICROPY_ENABLE_COMPILER
|
||||
|
||||
void mp_emit_common_get_id_for_modification(scope_t *scope, qstr qst) {
|
||||
// name adding/lookup
|
||||
id_info_t *id = scope_find_or_add_id(scope, qst, ID_INFO_KIND_GLOBAL_IMPLICIT);
|
||||
if (SCOPE_IS_FUNC_LIKE(scope->kind) && id->kind == ID_INFO_KIND_GLOBAL_IMPLICIT) {
|
||||
// rebind as a local variable
|
||||
id->kind = ID_INFO_KIND_LOCAL;
|
||||
}
|
||||
}
|
||||
|
||||
void mp_emit_common_id_op(emit_t *emit, const mp_emit_method_table_id_ops_t *emit_method_table, scope_t *scope, qstr qst) {
|
||||
// assumes pass is greater than 1, ie that all identifiers are defined in the scope
|
||||
|
||||
id_info_t *id = scope_find(scope, qst);
|
||||
assert(id != NULL);
|
||||
|
||||
// call the emit backend with the correct code
|
||||
if (id->kind == ID_INFO_KIND_GLOBAL_IMPLICIT) {
|
||||
emit_method_table->global(emit, qst, MP_EMIT_IDOP_GLOBAL_NAME);
|
||||
} else if (id->kind == ID_INFO_KIND_GLOBAL_EXPLICIT) {
|
||||
emit_method_table->global(emit, qst, MP_EMIT_IDOP_GLOBAL_GLOBAL);
|
||||
} else if (id->kind == ID_INFO_KIND_LOCAL) {
|
||||
emit_method_table->local(emit, qst, id->local_num, MP_EMIT_IDOP_LOCAL_FAST);
|
||||
} else {
|
||||
assert(id->kind == ID_INFO_KIND_CELL || id->kind == ID_INFO_KIND_FREE);
|
||||
emit_method_table->local(emit, qst, id->local_num, MP_EMIT_IDOP_LOCAL_DEREF);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // MICROPY_ENABLE_COMPILER
|
||||
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013, 2014 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
// This code glues the code emitters to the runtime.
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "py/emitglue.h"
|
||||
#include "py/runtime0.h"
|
||||
#include "py/bc.h"
|
||||
#include "py/profile.h"
|
||||
|
||||
#if MICROPY_DEBUG_VERBOSE // print debugging info
|
||||
#define DEBUG_PRINT (1)
|
||||
#define WRITE_CODE (1)
|
||||
#define DEBUG_printf DEBUG_printf
|
||||
#define DEBUG_OP_printf(...) DEBUG_printf(__VA_ARGS__)
|
||||
#else // don't print debugging info
|
||||
#define DEBUG_printf(...) (void)0
|
||||
#define DEBUG_OP_printf(...) (void)0
|
||||
#endif
|
||||
|
||||
#if MICROPY_DEBUG_PRINTERS
|
||||
mp_uint_t mp_verbose_flag = 0;
|
||||
#endif
|
||||
|
||||
mp_raw_code_t *mp_emit_glue_new_raw_code(void) {
|
||||
mp_raw_code_t *rc = m_new0(mp_raw_code_t, 1);
|
||||
rc->kind = MP_CODE_RESERVED;
|
||||
#if MICROPY_PY_SYS_SETTRACE
|
||||
rc->line_of_definition = 0;
|
||||
#endif
|
||||
return rc;
|
||||
}
|
||||
|
||||
void mp_emit_glue_assign_bytecode(mp_raw_code_t *rc, const byte *code,
|
||||
#if MICROPY_PERSISTENT_CODE_SAVE || MICROPY_DEBUG_PRINTERS
|
||||
size_t len,
|
||||
#endif
|
||||
const mp_uint_t *const_table,
|
||||
#if MICROPY_PERSISTENT_CODE_SAVE
|
||||
uint16_t n_obj, uint16_t n_raw_code,
|
||||
#endif
|
||||
mp_uint_t scope_flags) {
|
||||
|
||||
rc->kind = MP_CODE_BYTECODE;
|
||||
rc->scope_flags = scope_flags;
|
||||
rc->fun_data = code;
|
||||
rc->const_table = const_table;
|
||||
#if MICROPY_PERSISTENT_CODE_SAVE
|
||||
rc->fun_data_len = len;
|
||||
rc->n_obj = n_obj;
|
||||
rc->n_raw_code = n_raw_code;
|
||||
#endif
|
||||
|
||||
#if MICROPY_PY_SYS_SETTRACE
|
||||
mp_bytecode_prelude_t *prelude = &rc->prelude;
|
||||
mp_prof_extract_prelude(code, prelude);
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_PRINT
|
||||
#if !MICROPY_DEBUG_PRINTERS
|
||||
const size_t len = 0;
|
||||
#endif
|
||||
DEBUG_printf("assign byte code: code=%p len=" UINT_FMT " flags=%x\n", code, len, (uint)scope_flags);
|
||||
#endif
|
||||
#if MICROPY_DEBUG_PRINTERS
|
||||
if (mp_verbose_flag >= 2) {
|
||||
mp_bytecode_print(&mp_plat_print, rc, code, len, const_table);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if MICROPY_EMIT_MACHINE_CODE
|
||||
void mp_emit_glue_assign_native(mp_raw_code_t *rc, mp_raw_code_kind_t kind, void *fun_data, mp_uint_t fun_len, const mp_uint_t *const_table,
|
||||
#if MICROPY_PERSISTENT_CODE_SAVE
|
||||
uint16_t prelude_offset,
|
||||
uint16_t n_obj, uint16_t n_raw_code,
|
||||
uint16_t n_qstr, mp_qstr_link_entry_t *qstr_link,
|
||||
#endif
|
||||
mp_uint_t n_pos_args, mp_uint_t scope_flags, mp_uint_t type_sig) {
|
||||
|
||||
assert(kind == MP_CODE_NATIVE_PY || kind == MP_CODE_NATIVE_VIPER || kind == MP_CODE_NATIVE_ASM);
|
||||
|
||||
rc->kind = kind;
|
||||
rc->scope_flags = scope_flags;
|
||||
rc->n_pos_args = n_pos_args;
|
||||
rc->fun_data = fun_data;
|
||||
rc->const_table = const_table;
|
||||
rc->type_sig = type_sig;
|
||||
|
||||
#if MICROPY_PERSISTENT_CODE_SAVE
|
||||
rc->fun_data_len = fun_len;
|
||||
rc->prelude_offset = prelude_offset;
|
||||
rc->n_obj = n_obj;
|
||||
rc->n_raw_code = n_raw_code;
|
||||
rc->n_qstr = n_qstr;
|
||||
rc->qstr_link = qstr_link;
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_PRINT
|
||||
DEBUG_printf("assign native: kind=%d fun=%p len=" UINT_FMT " n_pos_args=" UINT_FMT " flags=%x\n", kind, fun_data, fun_len, n_pos_args, (uint)scope_flags);
|
||||
for (mp_uint_t i = 0; i < fun_len; i++) {
|
||||
if (i > 0 && i % 16 == 0) {
|
||||
DEBUG_printf("\n");
|
||||
}
|
||||
DEBUG_printf(" %02x", ((byte *)fun_data)[i]);
|
||||
}
|
||||
DEBUG_printf("\n");
|
||||
|
||||
#ifdef WRITE_CODE
|
||||
FILE *fp_write_code = fopen("out-code", "wb");
|
||||
fwrite(fun_data, fun_len, 1, fp_write_code);
|
||||
fclose(fp_write_code);
|
||||
#endif
|
||||
#else
|
||||
(void)fun_len;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
mp_obj_t mp_make_function_from_raw_code(const mp_raw_code_t *rc, mp_obj_t def_args, mp_obj_t def_kw_args) {
|
||||
DEBUG_OP_printf("make_function_from_raw_code %p\n", rc);
|
||||
assert(rc != NULL);
|
||||
|
||||
// def_args must be MP_OBJ_NULL or a tuple
|
||||
assert(def_args == MP_OBJ_NULL || mp_obj_is_type(def_args, &mp_type_tuple));
|
||||
|
||||
// def_kw_args must be MP_OBJ_NULL or a dict
|
||||
assert(def_kw_args == MP_OBJ_NULL || mp_obj_is_type(def_kw_args, &mp_type_dict));
|
||||
|
||||
// make the function, depending on the raw code kind
|
||||
mp_obj_t fun;
|
||||
switch (rc->kind) {
|
||||
#if MICROPY_EMIT_NATIVE
|
||||
case MP_CODE_NATIVE_PY:
|
||||
case MP_CODE_NATIVE_VIPER:
|
||||
fun = mp_obj_new_fun_native(def_args, def_kw_args, rc->fun_data, rc->const_table);
|
||||
// Check for a generator function, and if so change the type of the object
|
||||
if ((rc->scope_flags & MP_SCOPE_FLAG_GENERATOR) != 0) {
|
||||
((mp_obj_base_t *)MP_OBJ_TO_PTR(fun))->type = &mp_type_native_gen_wrap;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
#if MICROPY_EMIT_INLINE_ASM
|
||||
case MP_CODE_NATIVE_ASM:
|
||||
fun = mp_obj_new_fun_asm(rc->n_pos_args, rc->fun_data, rc->type_sig);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
// rc->kind should always be set and BYTECODE is the only remaining case
|
||||
assert(rc->kind == MP_CODE_BYTECODE);
|
||||
fun = mp_obj_new_fun_bc(def_args, def_kw_args, rc->fun_data, rc->const_table);
|
||||
// check for generator functions and if so change the type of the object
|
||||
if ((rc->scope_flags & MP_SCOPE_FLAG_GENERATOR) != 0) {
|
||||
((mp_obj_base_t *)MP_OBJ_TO_PTR(fun))->type = &mp_type_gen_wrap;
|
||||
}
|
||||
|
||||
#if MICROPY_PY_SYS_SETTRACE
|
||||
mp_obj_fun_bc_t *self_fun = (mp_obj_fun_bc_t *)MP_OBJ_TO_PTR(fun);
|
||||
self_fun->rc = rc;
|
||||
#endif
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return fun;
|
||||
}
|
||||
|
||||
mp_obj_t mp_make_closure_from_raw_code(const mp_raw_code_t *rc, mp_uint_t n_closed_over, const mp_obj_t *args) {
|
||||
DEBUG_OP_printf("make_closure_from_raw_code %p " UINT_FMT " %p\n", rc, n_closed_over, args);
|
||||
// make function object
|
||||
mp_obj_t ffun;
|
||||
if (n_closed_over & 0x100) {
|
||||
// default positional and keyword args given
|
||||
ffun = mp_make_function_from_raw_code(rc, args[0], args[1]);
|
||||
} else {
|
||||
// default positional and keyword args not given
|
||||
ffun = mp_make_function_from_raw_code(rc, MP_OBJ_NULL, MP_OBJ_NULL);
|
||||
}
|
||||
// wrap function in closure object
|
||||
return mp_obj_new_closure(ffun, n_closed_over & 0xff, args + ((n_closed_over >> 7) & 2));
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013, 2014 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef MICROPY_INCLUDED_PY_EMITGLUE_H
|
||||
#define MICROPY_INCLUDED_PY_EMITGLUE_H
|
||||
|
||||
#include "py/obj.h"
|
||||
#include "py/bc.h"
|
||||
|
||||
// These variables and functions glue the code emitters to the runtime.
|
||||
|
||||
// These must fit in 8 bits; see scope.h
|
||||
enum {
|
||||
MP_EMIT_OPT_NONE,
|
||||
MP_EMIT_OPT_BYTECODE,
|
||||
MP_EMIT_OPT_NATIVE_PYTHON,
|
||||
MP_EMIT_OPT_VIPER,
|
||||
MP_EMIT_OPT_ASM,
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
MP_CODE_UNUSED,
|
||||
MP_CODE_RESERVED,
|
||||
MP_CODE_BYTECODE,
|
||||
MP_CODE_NATIVE_PY,
|
||||
MP_CODE_NATIVE_VIPER,
|
||||
MP_CODE_NATIVE_ASM,
|
||||
} mp_raw_code_kind_t;
|
||||
|
||||
typedef struct _mp_qstr_link_entry_t {
|
||||
uint16_t off;
|
||||
uint16_t qst;
|
||||
} mp_qstr_link_entry_t;
|
||||
|
||||
typedef struct _mp_raw_code_t {
|
||||
mp_uint_t kind : 3; // of type mp_raw_code_kind_t
|
||||
mp_uint_t scope_flags : 7;
|
||||
mp_uint_t n_pos_args : 11;
|
||||
const void *fun_data;
|
||||
const mp_uint_t *const_table;
|
||||
#if MICROPY_PERSISTENT_CODE_SAVE
|
||||
size_t fun_data_len;
|
||||
uint16_t n_obj;
|
||||
uint16_t n_raw_code;
|
||||
#if MICROPY_PY_SYS_SETTRACE
|
||||
mp_bytecode_prelude_t prelude;
|
||||
// line_of_definition is a Python source line where the raw_code was
|
||||
// created e.g. MP_BC_MAKE_FUNCTION. This is different from lineno info
|
||||
// stored in prelude, which provides line number for first statement of
|
||||
// a function. Required to properly implement "call" trace event.
|
||||
mp_uint_t line_of_definition;
|
||||
#endif
|
||||
#if MICROPY_EMIT_MACHINE_CODE
|
||||
uint16_t prelude_offset;
|
||||
uint16_t n_qstr;
|
||||
mp_qstr_link_entry_t *qstr_link;
|
||||
#endif
|
||||
#endif
|
||||
#if MICROPY_EMIT_MACHINE_CODE
|
||||
mp_uint_t type_sig; // for viper, compressed as 2-bit types; ret is MSB, then arg0, arg1, etc
|
||||
#endif
|
||||
} mp_raw_code_t;
|
||||
|
||||
mp_raw_code_t *mp_emit_glue_new_raw_code(void);
|
||||
|
||||
void mp_emit_glue_assign_bytecode(mp_raw_code_t *rc, const byte *code,
|
||||
#if MICROPY_PERSISTENT_CODE_SAVE || MICROPY_DEBUG_PRINTERS
|
||||
size_t len,
|
||||
#endif
|
||||
const mp_uint_t *const_table,
|
||||
#if MICROPY_PERSISTENT_CODE_SAVE
|
||||
uint16_t n_obj, uint16_t n_raw_code,
|
||||
#endif
|
||||
mp_uint_t scope_flags);
|
||||
|
||||
void mp_emit_glue_assign_native(mp_raw_code_t *rc, mp_raw_code_kind_t kind, void *fun_data, mp_uint_t fun_len,
|
||||
const mp_uint_t *const_table,
|
||||
#if MICROPY_PERSISTENT_CODE_SAVE
|
||||
uint16_t prelude_offset,
|
||||
uint16_t n_obj, uint16_t n_raw_code,
|
||||
uint16_t n_qstr, mp_qstr_link_entry_t *qstr_link,
|
||||
#endif
|
||||
mp_uint_t n_pos_args, mp_uint_t scope_flags, mp_uint_t type_sig);
|
||||
|
||||
mp_obj_t mp_make_function_from_raw_code(const mp_raw_code_t *rc, mp_obj_t def_args, mp_obj_t def_kw_args);
|
||||
mp_obj_t mp_make_closure_from_raw_code(const mp_raw_code_t *rc, mp_uint_t n_closed_over, const mp_obj_t *args);
|
||||
|
||||
#endif // MICROPY_INCLUDED_PY_EMITGLUE_H
|
||||
@@ -0,0 +1,841 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013, 2014 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "py/emit.h"
|
||||
#include "py/asmthumb.h"
|
||||
|
||||
#if MICROPY_EMIT_INLINE_THUMB
|
||||
|
||||
typedef enum {
|
||||
// define rules with a compile function
|
||||
#define DEF_RULE(rule, comp, kind, ...) PN_##rule,
|
||||
#define DEF_RULE_NC(rule, kind, ...)
|
||||
#include "py/grammar.h"
|
||||
#undef DEF_RULE
|
||||
#undef DEF_RULE_NC
|
||||
PN_const_object, // special node for a constant, generic Python object
|
||||
// define rules without a compile function
|
||||
#define DEF_RULE(rule, comp, kind, ...)
|
||||
#define DEF_RULE_NC(rule, kind, ...) PN_##rule,
|
||||
#include "py/grammar.h"
|
||||
#undef DEF_RULE
|
||||
#undef DEF_RULE_NC
|
||||
} pn_kind_t;
|
||||
|
||||
struct _emit_inline_asm_t {
|
||||
asm_thumb_t as;
|
||||
uint16_t pass;
|
||||
mp_obj_t *error_slot;
|
||||
mp_uint_t max_num_labels;
|
||||
qstr *label_lookup;
|
||||
};
|
||||
|
||||
STATIC void emit_inline_thumb_error_msg(emit_inline_asm_t *emit, mp_rom_error_text_t msg) {
|
||||
*emit->error_slot = mp_obj_new_exception_msg(&mp_type_SyntaxError, msg);
|
||||
}
|
||||
|
||||
STATIC void emit_inline_thumb_error_exc(emit_inline_asm_t *emit, mp_obj_t exc) {
|
||||
*emit->error_slot = exc;
|
||||
}
|
||||
|
||||
emit_inline_asm_t *emit_inline_thumb_new(mp_uint_t max_num_labels) {
|
||||
emit_inline_asm_t *emit = m_new_obj(emit_inline_asm_t);
|
||||
memset(&emit->as, 0, sizeof(emit->as));
|
||||
mp_asm_base_init(&emit->as.base, max_num_labels);
|
||||
emit->max_num_labels = max_num_labels;
|
||||
emit->label_lookup = m_new(qstr, max_num_labels);
|
||||
return emit;
|
||||
}
|
||||
|
||||
void emit_inline_thumb_free(emit_inline_asm_t *emit) {
|
||||
m_del(qstr, emit->label_lookup, emit->max_num_labels);
|
||||
mp_asm_base_deinit(&emit->as.base, false);
|
||||
m_del_obj(emit_inline_asm_t, emit);
|
||||
}
|
||||
|
||||
STATIC void emit_inline_thumb_start_pass(emit_inline_asm_t *emit, pass_kind_t pass, mp_obj_t *error_slot) {
|
||||
emit->pass = pass;
|
||||
emit->error_slot = error_slot;
|
||||
if (emit->pass == MP_PASS_CODE_SIZE) {
|
||||
memset(emit->label_lookup, 0, emit->max_num_labels * sizeof(qstr));
|
||||
}
|
||||
mp_asm_base_start_pass(&emit->as.base, pass == MP_PASS_EMIT ? MP_ASM_PASS_EMIT : MP_ASM_PASS_COMPUTE);
|
||||
asm_thumb_entry(&emit->as, 0);
|
||||
}
|
||||
|
||||
STATIC void emit_inline_thumb_end_pass(emit_inline_asm_t *emit, mp_uint_t type_sig) {
|
||||
asm_thumb_exit(&emit->as);
|
||||
asm_thumb_end_pass(&emit->as);
|
||||
}
|
||||
|
||||
STATIC mp_uint_t emit_inline_thumb_count_params(emit_inline_asm_t *emit, mp_uint_t n_params, mp_parse_node_t *pn_params) {
|
||||
if (n_params > 4) {
|
||||
emit_inline_thumb_error_msg(emit, MP_ERROR_TEXT("can only have up to 4 parameters to Thumb assembly"));
|
||||
return 0;
|
||||
}
|
||||
for (mp_uint_t i = 0; i < n_params; i++) {
|
||||
if (!MP_PARSE_NODE_IS_ID(pn_params[i])) {
|
||||
emit_inline_thumb_error_msg(emit, MP_ERROR_TEXT("parameters must be registers in sequence r0 to r3"));
|
||||
return 0;
|
||||
}
|
||||
const char *p = qstr_str(MP_PARSE_NODE_LEAF_ARG(pn_params[i]));
|
||||
if (!(strlen(p) == 2 && p[0] == 'r' && (mp_uint_t)p[1] == '0' + i)) {
|
||||
emit_inline_thumb_error_msg(emit, MP_ERROR_TEXT("parameters must be registers in sequence r0 to r3"));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return n_params;
|
||||
}
|
||||
|
||||
STATIC bool emit_inline_thumb_label(emit_inline_asm_t *emit, mp_uint_t label_num, qstr label_id) {
|
||||
assert(label_num < emit->max_num_labels);
|
||||
if (emit->pass == MP_PASS_CODE_SIZE) {
|
||||
// check for duplicate label on first pass
|
||||
for (uint i = 0; i < emit->max_num_labels; i++) {
|
||||
if (emit->label_lookup[i] == label_id) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
emit->label_lookup[label_num] = label_id;
|
||||
mp_asm_base_label_assign(&emit->as.base, label_num);
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef struct _reg_name_t { byte reg;
|
||||
byte name[3];
|
||||
} reg_name_t;
|
||||
STATIC const reg_name_t reg_name_table[] = {
|
||||
{0, "r0\0"},
|
||||
{1, "r1\0"},
|
||||
{2, "r2\0"},
|
||||
{3, "r3\0"},
|
||||
{4, "r4\0"},
|
||||
{5, "r5\0"},
|
||||
{6, "r6\0"},
|
||||
{7, "r7\0"},
|
||||
{8, "r8\0"},
|
||||
{9, "r9\0"},
|
||||
{10, "r10"},
|
||||
{11, "r11"},
|
||||
{12, "r12"},
|
||||
{13, "r13"},
|
||||
{14, "r14"},
|
||||
{15, "r15"},
|
||||
{10, "sl\0"},
|
||||
{11, "fp\0"},
|
||||
{13, "sp\0"},
|
||||
{14, "lr\0"},
|
||||
{15, "pc\0"},
|
||||
};
|
||||
|
||||
#define MAX_SPECIAL_REGISTER_NAME_LENGTH 7
|
||||
typedef struct _special_reg_name_t { byte reg;
|
||||
char name[MAX_SPECIAL_REGISTER_NAME_LENGTH + 1];
|
||||
} special_reg_name_t;
|
||||
STATIC const special_reg_name_t special_reg_name_table[] = {
|
||||
{5, "IPSR"},
|
||||
{17, "BASEPRI"},
|
||||
};
|
||||
|
||||
// return empty string in case of error, so we can attempt to parse the string
|
||||
// without a special check if it was in fact a string
|
||||
STATIC const char *get_arg_str(mp_parse_node_t pn) {
|
||||
if (MP_PARSE_NODE_IS_ID(pn)) {
|
||||
qstr qst = MP_PARSE_NODE_LEAF_ARG(pn);
|
||||
return qstr_str(qst);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
STATIC mp_uint_t get_arg_reg(emit_inline_asm_t *emit, const char *op, mp_parse_node_t pn, mp_uint_t max_reg) {
|
||||
const char *reg_str = get_arg_str(pn);
|
||||
for (mp_uint_t i = 0; i < MP_ARRAY_SIZE(reg_name_table); i++) {
|
||||
const reg_name_t *r = ®_name_table[i];
|
||||
if (reg_str[0] == r->name[0]
|
||||
&& reg_str[1] == r->name[1]
|
||||
&& reg_str[2] == r->name[2]
|
||||
&& (reg_str[2] == '\0' || reg_str[3] == '\0')) {
|
||||
if (r->reg > max_reg) {
|
||||
emit_inline_thumb_error_exc(emit,
|
||||
mp_obj_new_exception_msg_varg(&mp_type_SyntaxError,
|
||||
MP_ERROR_TEXT("'%s' expects at most r%d"), op, max_reg));
|
||||
return 0;
|
||||
} else {
|
||||
return r->reg;
|
||||
}
|
||||
}
|
||||
}
|
||||
emit_inline_thumb_error_exc(emit,
|
||||
mp_obj_new_exception_msg_varg(&mp_type_SyntaxError,
|
||||
MP_ERROR_TEXT("'%s' expects a register"), op));
|
||||
return 0;
|
||||
}
|
||||
|
||||
STATIC mp_uint_t get_arg_special_reg(emit_inline_asm_t *emit, const char *op, mp_parse_node_t pn) {
|
||||
const char *reg_str = get_arg_str(pn);
|
||||
for (mp_uint_t i = 0; i < MP_ARRAY_SIZE(special_reg_name_table); i++) {
|
||||
const special_reg_name_t *r = &special_reg_name_table[i];
|
||||
if (strcmp(r->name, reg_str) == 0) {
|
||||
return r->reg;
|
||||
}
|
||||
}
|
||||
emit_inline_thumb_error_exc(emit,
|
||||
mp_obj_new_exception_msg_varg(&mp_type_SyntaxError,
|
||||
MP_ERROR_TEXT("'%s' expects a special register"), op));
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if MICROPY_EMIT_INLINE_THUMB_FLOAT
|
||||
STATIC mp_uint_t get_arg_vfpreg(emit_inline_asm_t *emit, const char *op, mp_parse_node_t pn) {
|
||||
const char *reg_str = get_arg_str(pn);
|
||||
if (reg_str[0] == 's' && reg_str[1] != '\0') {
|
||||
mp_uint_t regno = 0;
|
||||
for (++reg_str; *reg_str; ++reg_str) {
|
||||
mp_uint_t v = *reg_str;
|
||||
if (!('0' <= v && v <= '9')) {
|
||||
goto malformed;
|
||||
}
|
||||
regno = 10 * regno + v - '0';
|
||||
}
|
||||
if (regno > 31) {
|
||||
emit_inline_thumb_error_exc(emit,
|
||||
mp_obj_new_exception_msg_varg(&mp_type_SyntaxError,
|
||||
MP_ERROR_TEXT("'%s' expects at most r%d"), op, 31));
|
||||
return 0;
|
||||
} else {
|
||||
return regno;
|
||||
}
|
||||
}
|
||||
malformed:
|
||||
emit_inline_thumb_error_exc(emit,
|
||||
mp_obj_new_exception_msg_varg(&mp_type_SyntaxError,
|
||||
MP_ERROR_TEXT("'%s' expects an FPU register"), op));
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
STATIC mp_uint_t get_arg_reglist(emit_inline_asm_t *emit, const char *op, mp_parse_node_t pn) {
|
||||
// a register list looks like {r0, r1, r2} and is parsed as a Python set
|
||||
|
||||
if (!MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_atom_brace)) {
|
||||
goto bad_arg;
|
||||
}
|
||||
|
||||
mp_parse_node_struct_t *pns = (mp_parse_node_struct_t *)pn;
|
||||
assert(MP_PARSE_NODE_STRUCT_NUM_NODES(pns) == 1); // should always be
|
||||
pn = pns->nodes[0];
|
||||
|
||||
mp_uint_t reglist = 0;
|
||||
|
||||
if (MP_PARSE_NODE_IS_ID(pn)) {
|
||||
// set with one element
|
||||
reglist |= 1 << get_arg_reg(emit, op, pn, 15);
|
||||
} else if (MP_PARSE_NODE_IS_STRUCT(pn)) {
|
||||
pns = (mp_parse_node_struct_t *)pn;
|
||||
if (MP_PARSE_NODE_STRUCT_KIND(pns) == PN_dictorsetmaker) {
|
||||
assert(MP_PARSE_NODE_IS_STRUCT(pns->nodes[1])); // should succeed
|
||||
mp_parse_node_struct_t *pns1 = (mp_parse_node_struct_t *)pns->nodes[1];
|
||||
if (MP_PARSE_NODE_STRUCT_KIND(pns1) == PN_dictorsetmaker_list) {
|
||||
// set with multiple elements
|
||||
|
||||
// get first element of set (we rely on get_arg_reg to catch syntax errors)
|
||||
reglist |= 1 << get_arg_reg(emit, op, pns->nodes[0], 15);
|
||||
|
||||
// get tail elements (2nd, 3rd, ...)
|
||||
mp_parse_node_t *nodes;
|
||||
int n = mp_parse_node_extract_list(&pns1->nodes[0], PN_dictorsetmaker_list2, &nodes);
|
||||
|
||||
// process rest of elements
|
||||
for (int i = 0; i < n; i++) {
|
||||
reglist |= 1 << get_arg_reg(emit, op, nodes[i], 15);
|
||||
}
|
||||
} else {
|
||||
goto bad_arg;
|
||||
}
|
||||
} else {
|
||||
goto bad_arg;
|
||||
}
|
||||
} else {
|
||||
goto bad_arg;
|
||||
}
|
||||
|
||||
return reglist;
|
||||
|
||||
bad_arg:
|
||||
emit_inline_thumb_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, MP_ERROR_TEXT("'%s' expects {r0, r1, ...}"), op));
|
||||
return 0;
|
||||
}
|
||||
|
||||
STATIC uint32_t get_arg_i(emit_inline_asm_t *emit, const char *op, mp_parse_node_t pn, uint32_t fit_mask) {
|
||||
mp_obj_t o;
|
||||
if (!mp_parse_node_get_int_maybe(pn, &o)) {
|
||||
emit_inline_thumb_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, MP_ERROR_TEXT("'%s' expects an integer"), op));
|
||||
return 0;
|
||||
}
|
||||
uint32_t i = mp_obj_get_int_truncated(o);
|
||||
if ((i & (~fit_mask)) != 0) {
|
||||
emit_inline_thumb_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, MP_ERROR_TEXT("'%s' integer 0x%x doesn't fit in mask 0x%x"), op, i, fit_mask));
|
||||
return 0;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
STATIC bool get_arg_addr(emit_inline_asm_t *emit, const char *op, mp_parse_node_t pn, mp_parse_node_t *pn_base, mp_parse_node_t *pn_offset) {
|
||||
if (!MP_PARSE_NODE_IS_STRUCT_KIND(pn, PN_atom_bracket)) {
|
||||
goto bad_arg;
|
||||
}
|
||||
mp_parse_node_struct_t *pns = (mp_parse_node_struct_t *)pn;
|
||||
if (!MP_PARSE_NODE_IS_STRUCT_KIND(pns->nodes[0], PN_testlist_comp)) {
|
||||
goto bad_arg;
|
||||
}
|
||||
pns = (mp_parse_node_struct_t *)pns->nodes[0];
|
||||
if (MP_PARSE_NODE_STRUCT_NUM_NODES(pns) != 2) {
|
||||
goto bad_arg;
|
||||
}
|
||||
|
||||
*pn_base = pns->nodes[0];
|
||||
*pn_offset = pns->nodes[1];
|
||||
return true;
|
||||
|
||||
bad_arg:
|
||||
emit_inline_thumb_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, MP_ERROR_TEXT("'%s' expects an address of the form [a, b]"), op));
|
||||
return false;
|
||||
}
|
||||
|
||||
STATIC int get_arg_label(emit_inline_asm_t *emit, const char *op, mp_parse_node_t pn) {
|
||||
if (!MP_PARSE_NODE_IS_ID(pn)) {
|
||||
emit_inline_thumb_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, MP_ERROR_TEXT("'%s' expects a label"), op));
|
||||
return 0;
|
||||
}
|
||||
qstr label_qstr = MP_PARSE_NODE_LEAF_ARG(pn);
|
||||
for (uint i = 0; i < emit->max_num_labels; i++) {
|
||||
if (emit->label_lookup[i] == label_qstr) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
// only need to have the labels on the last pass
|
||||
if (emit->pass == MP_PASS_EMIT) {
|
||||
emit_inline_thumb_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, MP_ERROR_TEXT("label '%q' not defined"), label_qstr));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef struct _cc_name_t { byte cc;
|
||||
byte name[2];
|
||||
} cc_name_t;
|
||||
STATIC const cc_name_t cc_name_table[] = {
|
||||
{ ASM_THUMB_CC_EQ, "eq" },
|
||||
{ ASM_THUMB_CC_NE, "ne" },
|
||||
{ ASM_THUMB_CC_CS, "cs" },
|
||||
{ ASM_THUMB_CC_CC, "cc" },
|
||||
{ ASM_THUMB_CC_MI, "mi" },
|
||||
{ ASM_THUMB_CC_PL, "pl" },
|
||||
{ ASM_THUMB_CC_VS, "vs" },
|
||||
{ ASM_THUMB_CC_VC, "vc" },
|
||||
{ ASM_THUMB_CC_HI, "hi" },
|
||||
{ ASM_THUMB_CC_LS, "ls" },
|
||||
{ ASM_THUMB_CC_GE, "ge" },
|
||||
{ ASM_THUMB_CC_LT, "lt" },
|
||||
{ ASM_THUMB_CC_GT, "gt" },
|
||||
{ ASM_THUMB_CC_LE, "le" },
|
||||
};
|
||||
|
||||
typedef struct _format_4_op_t { byte op;
|
||||
char name[3];
|
||||
} format_4_op_t;
|
||||
#define X(x) (((x) >> 4) & 0xff) // only need 1 byte to distinguish these ops
|
||||
STATIC const format_4_op_t format_4_op_table[] = {
|
||||
{ X(ASM_THUMB_FORMAT_4_EOR), "eor" },
|
||||
{ X(ASM_THUMB_FORMAT_4_LSL), "lsl" },
|
||||
{ X(ASM_THUMB_FORMAT_4_LSR), "lsr" },
|
||||
{ X(ASM_THUMB_FORMAT_4_ASR), "asr" },
|
||||
{ X(ASM_THUMB_FORMAT_4_ADC), "adc" },
|
||||
{ X(ASM_THUMB_FORMAT_4_SBC), "sbc" },
|
||||
{ X(ASM_THUMB_FORMAT_4_ROR), "ror" },
|
||||
{ X(ASM_THUMB_FORMAT_4_TST), "tst" },
|
||||
{ X(ASM_THUMB_FORMAT_4_NEG), "neg" },
|
||||
{ X(ASM_THUMB_FORMAT_4_CMP), "cmp" },
|
||||
{ X(ASM_THUMB_FORMAT_4_CMN), "cmn" },
|
||||
{ X(ASM_THUMB_FORMAT_4_ORR), "orr" },
|
||||
{ X(ASM_THUMB_FORMAT_4_MUL), "mul" },
|
||||
{ X(ASM_THUMB_FORMAT_4_BIC), "bic" },
|
||||
{ X(ASM_THUMB_FORMAT_4_MVN), "mvn" },
|
||||
};
|
||||
#undef X
|
||||
|
||||
// name is actually a qstr, which should fit in 16 bits
|
||||
typedef struct _format_9_10_op_t { uint16_t op;
|
||||
uint16_t name;
|
||||
} format_9_10_op_t;
|
||||
#define X(x) (x)
|
||||
STATIC const format_9_10_op_t format_9_10_op_table[] = {
|
||||
{ X(ASM_THUMB_FORMAT_9_LDR | ASM_THUMB_FORMAT_9_WORD_TRANSFER), MP_QSTR_ldr },
|
||||
{ X(ASM_THUMB_FORMAT_9_LDR | ASM_THUMB_FORMAT_9_BYTE_TRANSFER), MP_QSTR_ldrb },
|
||||
{ X(ASM_THUMB_FORMAT_10_LDRH), MP_QSTR_ldrh },
|
||||
{ X(ASM_THUMB_FORMAT_9_STR | ASM_THUMB_FORMAT_9_WORD_TRANSFER), MP_QSTR_str },
|
||||
{ X(ASM_THUMB_FORMAT_9_STR | ASM_THUMB_FORMAT_9_BYTE_TRANSFER), MP_QSTR_strb },
|
||||
{ X(ASM_THUMB_FORMAT_10_STRH), MP_QSTR_strh },
|
||||
};
|
||||
#undef X
|
||||
|
||||
#if MICROPY_EMIT_INLINE_THUMB_FLOAT
|
||||
// actual opcodes are: 0xee00 | op.hi_nibble, 0x0a00 | op.lo_nibble
|
||||
typedef struct _format_vfp_op_t { byte op;
|
||||
char name[3];
|
||||
} format_vfp_op_t;
|
||||
STATIC const format_vfp_op_t format_vfp_op_table[] = {
|
||||
{ 0x30, "add" },
|
||||
{ 0x34, "sub" },
|
||||
{ 0x20, "mul" },
|
||||
{ 0x80, "div" },
|
||||
};
|
||||
#endif
|
||||
|
||||
// shorthand alias for whether we allow ARMv7-M instructions
|
||||
#define ARMV7M MICROPY_EMIT_INLINE_THUMB_ARMV7M
|
||||
|
||||
STATIC void emit_inline_thumb_op(emit_inline_asm_t *emit, qstr op, mp_uint_t n_args, mp_parse_node_t *pn_args) {
|
||||
// TODO perhaps make two tables:
|
||||
// one_args =
|
||||
// "b", LAB, asm_thumb_b_n,
|
||||
// "bgt", LAB, asm_thumb_bgt_n,
|
||||
// two_args =
|
||||
// "movs", RLO, I8, asm_thumb_movs_reg_i8
|
||||
// "movw", REG, REG, asm_thumb_movw_reg_i16
|
||||
// three_args =
|
||||
// "subs", RLO, RLO, I3, asm_thumb_subs_reg_reg_i3
|
||||
|
||||
size_t op_len;
|
||||
const char *op_str = (const char *)qstr_data(op, &op_len);
|
||||
|
||||
#if MICROPY_EMIT_INLINE_THUMB_FLOAT
|
||||
if (op_str[0] == 'v') {
|
||||
// floating point operations
|
||||
if (n_args == 2) {
|
||||
mp_uint_t op_code = 0x0ac0, op_code_hi;
|
||||
if (op == MP_QSTR_vcmp) {
|
||||
op_code_hi = 0xeeb4;
|
||||
op_vfp_twoargs:;
|
||||
mp_uint_t vd = get_arg_vfpreg(emit, op_str, pn_args[0]);
|
||||
mp_uint_t vm = get_arg_vfpreg(emit, op_str, pn_args[1]);
|
||||
asm_thumb_op32(&emit->as,
|
||||
op_code_hi | ((vd & 1) << 6),
|
||||
op_code | ((vd & 0x1e) << 11) | ((vm & 1) << 5) | (vm & 0x1e) >> 1);
|
||||
} else if (op == MP_QSTR_vsqrt) {
|
||||
op_code_hi = 0xeeb1;
|
||||
goto op_vfp_twoargs;
|
||||
} else if (op == MP_QSTR_vneg) {
|
||||
op_code_hi = 0xeeb1;
|
||||
op_code = 0x0a40;
|
||||
goto op_vfp_twoargs;
|
||||
} else if (op == MP_QSTR_vcvt_f32_s32) {
|
||||
op_code_hi = 0xeeb8; // int to float
|
||||
goto op_vfp_twoargs;
|
||||
} else if (op == MP_QSTR_vcvt_s32_f32) {
|
||||
op_code_hi = 0xeebd; // float to int
|
||||
goto op_vfp_twoargs;
|
||||
} else if (op == MP_QSTR_vmrs) {
|
||||
mp_uint_t reg_dest;
|
||||
const char *reg_str0 = get_arg_str(pn_args[0]);
|
||||
if (strcmp(reg_str0, "APSR_nzcv") == 0) {
|
||||
reg_dest = 15;
|
||||
} else {
|
||||
reg_dest = get_arg_reg(emit, op_str, pn_args[0], 15);
|
||||
}
|
||||
const char *reg_str1 = get_arg_str(pn_args[1]);
|
||||
if (strcmp(reg_str1, "FPSCR") == 0) {
|
||||
// FP status to ARM reg
|
||||
asm_thumb_op32(&emit->as, 0xeef1, 0x0a10 | (reg_dest << 12));
|
||||
} else {
|
||||
goto unknown_op;
|
||||
}
|
||||
} else if (op == MP_QSTR_vmov) {
|
||||
op_code_hi = 0xee00;
|
||||
mp_uint_t r_arm, vm;
|
||||
const char *reg_str = get_arg_str(pn_args[0]);
|
||||
if (reg_str[0] == 'r') {
|
||||
r_arm = get_arg_reg(emit, op_str, pn_args[0], 15);
|
||||
vm = get_arg_vfpreg(emit, op_str, pn_args[1]);
|
||||
op_code_hi |= 0x10;
|
||||
} else {
|
||||
vm = get_arg_vfpreg(emit, op_str, pn_args[0]);
|
||||
r_arm = get_arg_reg(emit, op_str, pn_args[1], 15);
|
||||
}
|
||||
asm_thumb_op32(&emit->as,
|
||||
op_code_hi | ((vm & 0x1e) >> 1),
|
||||
0x0a10 | (r_arm << 12) | ((vm & 1) << 7));
|
||||
} else if (op == MP_QSTR_vldr) {
|
||||
op_code_hi = 0xed90;
|
||||
op_vldr_vstr:;
|
||||
mp_uint_t vd = get_arg_vfpreg(emit, op_str, pn_args[0]);
|
||||
mp_parse_node_t pn_base, pn_offset;
|
||||
if (get_arg_addr(emit, op_str, pn_args[1], &pn_base, &pn_offset)) {
|
||||
mp_uint_t rlo_base = get_arg_reg(emit, op_str, pn_base, 7);
|
||||
mp_uint_t i8;
|
||||
i8 = get_arg_i(emit, op_str, pn_offset, 0x3fc) >> 2;
|
||||
asm_thumb_op32(&emit->as,
|
||||
op_code_hi | rlo_base | ((vd & 1) << 6),
|
||||
0x0a00 | ((vd & 0x1e) << 11) | i8);
|
||||
}
|
||||
} else if (op == MP_QSTR_vstr) {
|
||||
op_code_hi = 0xed80;
|
||||
goto op_vldr_vstr;
|
||||
} else {
|
||||
goto unknown_op;
|
||||
}
|
||||
} else if (n_args == 3) {
|
||||
// search table for arith ops
|
||||
for (mp_uint_t i = 0; i < MP_ARRAY_SIZE(format_vfp_op_table); i++) {
|
||||
if (strncmp(op_str + 1, format_vfp_op_table[i].name, 3) == 0 && op_str[4] == '\0') {
|
||||
mp_uint_t op_code_hi = 0xee00 | (format_vfp_op_table[i].op & 0xf0);
|
||||
mp_uint_t op_code = 0x0a00 | ((format_vfp_op_table[i].op & 0x0f) << 4);
|
||||
mp_uint_t vd = get_arg_vfpreg(emit, op_str, pn_args[0]);
|
||||
mp_uint_t vn = get_arg_vfpreg(emit, op_str, pn_args[1]);
|
||||
mp_uint_t vm = get_arg_vfpreg(emit, op_str, pn_args[2]);
|
||||
asm_thumb_op32(&emit->as,
|
||||
op_code_hi | ((vd & 1) << 6) | (vn >> 1),
|
||||
op_code | (vm >> 1) | ((vm & 1) << 5) | ((vd & 0x1e) << 11) | ((vn & 1) << 7));
|
||||
return;
|
||||
}
|
||||
}
|
||||
goto unknown_op;
|
||||
} else {
|
||||
goto unknown_op;
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (n_args == 0) {
|
||||
if (op == MP_QSTR_nop) {
|
||||
asm_thumb_op16(&emit->as, ASM_THUMB_OP_NOP);
|
||||
} else if (op == MP_QSTR_wfi) {
|
||||
asm_thumb_op16(&emit->as, ASM_THUMB_OP_WFI);
|
||||
} else {
|
||||
goto unknown_op;
|
||||
}
|
||||
|
||||
} else if (n_args == 1) {
|
||||
if (op == MP_QSTR_b) {
|
||||
int label_num = get_arg_label(emit, op_str, pn_args[0]);
|
||||
if (!asm_thumb_b_n_label(&emit->as, label_num)) {
|
||||
goto branch_not_in_range;
|
||||
}
|
||||
} else if (op == MP_QSTR_bl) {
|
||||
int label_num = get_arg_label(emit, op_str, pn_args[0]);
|
||||
if (!asm_thumb_bl_label(&emit->as, label_num)) {
|
||||
goto branch_not_in_range;
|
||||
}
|
||||
} else if (op == MP_QSTR_bx) {
|
||||
mp_uint_t r = get_arg_reg(emit, op_str, pn_args[0], 15);
|
||||
asm_thumb_op16(&emit->as, 0x4700 | (r << 3));
|
||||
} else if (op_str[0] == 'b' && (op_len == 3
|
||||
|| (op_len == 5 && op_str[3] == '_'
|
||||
&& (op_str[4] == 'n' || (ARMV7M && op_str[4] == 'w'))))) {
|
||||
mp_uint_t cc = -1;
|
||||
for (mp_uint_t i = 0; i < MP_ARRAY_SIZE(cc_name_table); i++) {
|
||||
if (op_str[1] == cc_name_table[i].name[0] && op_str[2] == cc_name_table[i].name[1]) {
|
||||
cc = cc_name_table[i].cc;
|
||||
}
|
||||
}
|
||||
if (cc == (mp_uint_t)-1) {
|
||||
goto unknown_op;
|
||||
}
|
||||
int label_num = get_arg_label(emit, op_str, pn_args[0]);
|
||||
if (!asm_thumb_bcc_nw_label(&emit->as, cc, label_num, op_len == 5 && op_str[4] == 'w')) {
|
||||
goto branch_not_in_range;
|
||||
}
|
||||
} else if (ARMV7M && op_str[0] == 'i' && op_str[1] == 't') {
|
||||
const char *arg_str = get_arg_str(pn_args[0]);
|
||||
mp_uint_t cc = -1;
|
||||
for (mp_uint_t i = 0; i < MP_ARRAY_SIZE(cc_name_table); i++) {
|
||||
if (arg_str[0] == cc_name_table[i].name[0]
|
||||
&& arg_str[1] == cc_name_table[i].name[1]
|
||||
&& arg_str[2] == '\0') {
|
||||
cc = cc_name_table[i].cc;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (cc == (mp_uint_t)-1) {
|
||||
goto unknown_op;
|
||||
}
|
||||
const char *os = op_str + 2;
|
||||
while (*os != '\0') {
|
||||
os++;
|
||||
}
|
||||
if (os > op_str + 5) {
|
||||
goto unknown_op;
|
||||
}
|
||||
mp_uint_t it_mask = 8;
|
||||
while (--os >= op_str + 2) {
|
||||
it_mask >>= 1;
|
||||
if (*os == 't') {
|
||||
it_mask |= (cc & 1) << 3;
|
||||
} else if (*os == 'e') {
|
||||
it_mask |= ((~cc) & 1) << 3;
|
||||
} else {
|
||||
goto unknown_op;
|
||||
}
|
||||
}
|
||||
asm_thumb_it_cc(&emit->as, cc, it_mask);
|
||||
} else if (op == MP_QSTR_cpsid) {
|
||||
// TODO check pn_args[0] == i
|
||||
asm_thumb_op16(&emit->as, ASM_THUMB_OP_CPSID_I);
|
||||
} else if (op == MP_QSTR_cpsie) {
|
||||
// TODO check pn_args[0] == i
|
||||
asm_thumb_op16(&emit->as, ASM_THUMB_OP_CPSIE_I);
|
||||
} else if (op == MP_QSTR_push) {
|
||||
mp_uint_t reglist = get_arg_reglist(emit, op_str, pn_args[0]);
|
||||
if ((reglist & 0xff00) == 0) {
|
||||
asm_thumb_op16(&emit->as, 0xb400 | reglist);
|
||||
} else {
|
||||
if (!ARMV7M) {
|
||||
goto unknown_op;
|
||||
}
|
||||
asm_thumb_op32(&emit->as, 0xe92d, reglist);
|
||||
}
|
||||
} else if (op == MP_QSTR_pop) {
|
||||
mp_uint_t reglist = get_arg_reglist(emit, op_str, pn_args[0]);
|
||||
if ((reglist & 0xff00) == 0) {
|
||||
asm_thumb_op16(&emit->as, 0xbc00 | reglist);
|
||||
} else {
|
||||
if (!ARMV7M) {
|
||||
goto unknown_op;
|
||||
}
|
||||
asm_thumb_op32(&emit->as, 0xe8bd, reglist);
|
||||
}
|
||||
} else {
|
||||
goto unknown_op;
|
||||
}
|
||||
|
||||
} else if (n_args == 2) {
|
||||
if (MP_PARSE_NODE_IS_ID(pn_args[1])) {
|
||||
// second arg is a register (or should be)
|
||||
mp_uint_t op_code, op_code_hi;
|
||||
if (op == MP_QSTR_mov) {
|
||||
mp_uint_t reg_dest = get_arg_reg(emit, op_str, pn_args[0], 15);
|
||||
mp_uint_t reg_src = get_arg_reg(emit, op_str, pn_args[1], 15);
|
||||
asm_thumb_mov_reg_reg(&emit->as, reg_dest, reg_src);
|
||||
} else if (ARMV7M && op == MP_QSTR_clz) {
|
||||
op_code_hi = 0xfab0;
|
||||
op_code = 0xf080;
|
||||
mp_uint_t rd, rm;
|
||||
op_clz_rbit:
|
||||
rd = get_arg_reg(emit, op_str, pn_args[0], 15);
|
||||
rm = get_arg_reg(emit, op_str, pn_args[1], 15);
|
||||
asm_thumb_op32(&emit->as, op_code_hi | rm, op_code | (rd << 8) | rm);
|
||||
} else if (ARMV7M && op == MP_QSTR_rbit) {
|
||||
op_code_hi = 0xfa90;
|
||||
op_code = 0xf0a0;
|
||||
goto op_clz_rbit;
|
||||
} else if (ARMV7M && op == MP_QSTR_mrs) {
|
||||
mp_uint_t reg_dest = get_arg_reg(emit, op_str, pn_args[0], 12);
|
||||
mp_uint_t reg_src = get_arg_special_reg(emit, op_str, pn_args[1]);
|
||||
asm_thumb_op32(&emit->as, 0xf3ef, 0x8000 | (reg_dest << 8) | reg_src);
|
||||
} else {
|
||||
if (op == MP_QSTR_and_) {
|
||||
op_code = ASM_THUMB_FORMAT_4_AND;
|
||||
mp_uint_t reg_dest, reg_src;
|
||||
op_format_4:
|
||||
reg_dest = get_arg_reg(emit, op_str, pn_args[0], 7);
|
||||
reg_src = get_arg_reg(emit, op_str, pn_args[1], 7);
|
||||
asm_thumb_format_4(&emit->as, op_code, reg_dest, reg_src);
|
||||
return;
|
||||
}
|
||||
// search table for ALU ops
|
||||
for (mp_uint_t i = 0; i < MP_ARRAY_SIZE(format_4_op_table); i++) {
|
||||
if (strncmp(op_str, format_4_op_table[i].name, 3) == 0 && op_str[3] == '\0') {
|
||||
op_code = 0x4000 | (format_4_op_table[i].op << 4);
|
||||
goto op_format_4;
|
||||
}
|
||||
}
|
||||
goto unknown_op;
|
||||
}
|
||||
} else {
|
||||
// second arg is not a register
|
||||
mp_uint_t op_code;
|
||||
if (op == MP_QSTR_mov) {
|
||||
op_code = ASM_THUMB_FORMAT_3_MOV;
|
||||
mp_uint_t rlo_dest, i8_src;
|
||||
op_format_3:
|
||||
rlo_dest = get_arg_reg(emit, op_str, pn_args[0], 7);
|
||||
i8_src = get_arg_i(emit, op_str, pn_args[1], 0xff);
|
||||
asm_thumb_format_3(&emit->as, op_code, rlo_dest, i8_src);
|
||||
} else if (op == MP_QSTR_cmp) {
|
||||
op_code = ASM_THUMB_FORMAT_3_CMP;
|
||||
goto op_format_3;
|
||||
} else if (op == MP_QSTR_add) {
|
||||
op_code = ASM_THUMB_FORMAT_3_ADD;
|
||||
goto op_format_3;
|
||||
} else if (op == MP_QSTR_sub) {
|
||||
op_code = ASM_THUMB_FORMAT_3_SUB;
|
||||
goto op_format_3;
|
||||
} else if (ARMV7M && op == MP_QSTR_movw) {
|
||||
op_code = ASM_THUMB_OP_MOVW;
|
||||
mp_uint_t reg_dest;
|
||||
op_movw_movt:
|
||||
reg_dest = get_arg_reg(emit, op_str, pn_args[0], 15);
|
||||
int i_src = get_arg_i(emit, op_str, pn_args[1], 0xffff);
|
||||
asm_thumb_mov_reg_i16(&emit->as, op_code, reg_dest, i_src);
|
||||
} else if (ARMV7M && op == MP_QSTR_movt) {
|
||||
op_code = ASM_THUMB_OP_MOVT;
|
||||
goto op_movw_movt;
|
||||
} else if (ARMV7M && op == MP_QSTR_movwt) {
|
||||
// this is a convenience instruction
|
||||
mp_uint_t reg_dest = get_arg_reg(emit, op_str, pn_args[0], 15);
|
||||
uint32_t i_src = get_arg_i(emit, op_str, pn_args[1], 0xffffffff);
|
||||
asm_thumb_mov_reg_i16(&emit->as, ASM_THUMB_OP_MOVW, reg_dest, i_src & 0xffff);
|
||||
asm_thumb_mov_reg_i16(&emit->as, ASM_THUMB_OP_MOVT, reg_dest, (i_src >> 16) & 0xffff);
|
||||
} else if (ARMV7M && op == MP_QSTR_ldrex) {
|
||||
mp_uint_t r_dest = get_arg_reg(emit, op_str, pn_args[0], 15);
|
||||
mp_parse_node_t pn_base, pn_offset;
|
||||
if (get_arg_addr(emit, op_str, pn_args[1], &pn_base, &pn_offset)) {
|
||||
mp_uint_t r_base = get_arg_reg(emit, op_str, pn_base, 15);
|
||||
mp_uint_t i8 = get_arg_i(emit, op_str, pn_offset, 0xff) >> 2;
|
||||
asm_thumb_op32(&emit->as, 0xe850 | r_base, 0x0f00 | (r_dest << 12) | i8);
|
||||
}
|
||||
} else {
|
||||
// search table for ldr/str instructions
|
||||
for (mp_uint_t i = 0; i < MP_ARRAY_SIZE(format_9_10_op_table); i++) {
|
||||
if (op == format_9_10_op_table[i].name) {
|
||||
op_code = format_9_10_op_table[i].op;
|
||||
mp_parse_node_t pn_base, pn_offset;
|
||||
mp_uint_t rlo_dest = get_arg_reg(emit, op_str, pn_args[0], 7);
|
||||
if (get_arg_addr(emit, op_str, pn_args[1], &pn_base, &pn_offset)) {
|
||||
mp_uint_t rlo_base = get_arg_reg(emit, op_str, pn_base, 7);
|
||||
mp_uint_t i5;
|
||||
if (op_code & ASM_THUMB_FORMAT_9_BYTE_TRANSFER) {
|
||||
i5 = get_arg_i(emit, op_str, pn_offset, 0x1f);
|
||||
} else if (op_code & ASM_THUMB_FORMAT_10_STRH) { // also catches LDRH
|
||||
i5 = get_arg_i(emit, op_str, pn_offset, 0x3e) >> 1;
|
||||
} else {
|
||||
i5 = get_arg_i(emit, op_str, pn_offset, 0x7c) >> 2;
|
||||
}
|
||||
asm_thumb_format_9_10(&emit->as, op_code, rlo_dest, rlo_base, i5);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
goto unknown_op;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (n_args == 3) {
|
||||
mp_uint_t op_code;
|
||||
if (op == MP_QSTR_lsl) {
|
||||
op_code = ASM_THUMB_FORMAT_1_LSL;
|
||||
mp_uint_t rlo_dest, rlo_src, i5;
|
||||
op_format_1:
|
||||
rlo_dest = get_arg_reg(emit, op_str, pn_args[0], 7);
|
||||
rlo_src = get_arg_reg(emit, op_str, pn_args[1], 7);
|
||||
i5 = get_arg_i(emit, op_str, pn_args[2], 0x1f);
|
||||
asm_thumb_format_1(&emit->as, op_code, rlo_dest, rlo_src, i5);
|
||||
} else if (op == MP_QSTR_lsr) {
|
||||
op_code = ASM_THUMB_FORMAT_1_LSR;
|
||||
goto op_format_1;
|
||||
} else if (op == MP_QSTR_asr) {
|
||||
op_code = ASM_THUMB_FORMAT_1_ASR;
|
||||
goto op_format_1;
|
||||
} else if (op == MP_QSTR_add) {
|
||||
op_code = ASM_THUMB_FORMAT_2_ADD;
|
||||
mp_uint_t rlo_dest, rlo_src;
|
||||
op_format_2:
|
||||
rlo_dest = get_arg_reg(emit, op_str, pn_args[0], 7);
|
||||
rlo_src = get_arg_reg(emit, op_str, pn_args[1], 7);
|
||||
int src_b;
|
||||
if (MP_PARSE_NODE_IS_ID(pn_args[2])) {
|
||||
op_code |= ASM_THUMB_FORMAT_2_REG_OPERAND;
|
||||
src_b = get_arg_reg(emit, op_str, pn_args[2], 7);
|
||||
} else {
|
||||
op_code |= ASM_THUMB_FORMAT_2_IMM_OPERAND;
|
||||
src_b = get_arg_i(emit, op_str, pn_args[2], 0x7);
|
||||
}
|
||||
asm_thumb_format_2(&emit->as, op_code, rlo_dest, rlo_src, src_b);
|
||||
} else if (ARMV7M && op == MP_QSTR_sdiv) {
|
||||
op_code = 0xfb90; // sdiv high part
|
||||
mp_uint_t rd, rn, rm;
|
||||
op_sdiv_udiv:
|
||||
rd = get_arg_reg(emit, op_str, pn_args[0], 15);
|
||||
rn = get_arg_reg(emit, op_str, pn_args[1], 15);
|
||||
rm = get_arg_reg(emit, op_str, pn_args[2], 15);
|
||||
asm_thumb_op32(&emit->as, op_code | rn, 0xf0f0 | (rd << 8) | rm);
|
||||
} else if (ARMV7M && op == MP_QSTR_udiv) {
|
||||
op_code = 0xfbb0; // udiv high part
|
||||
goto op_sdiv_udiv;
|
||||
} else if (op == MP_QSTR_sub) {
|
||||
op_code = ASM_THUMB_FORMAT_2_SUB;
|
||||
goto op_format_2;
|
||||
} else if (ARMV7M && op == MP_QSTR_strex) {
|
||||
mp_uint_t r_dest = get_arg_reg(emit, op_str, pn_args[0], 15);
|
||||
mp_uint_t r_src = get_arg_reg(emit, op_str, pn_args[1], 15);
|
||||
mp_parse_node_t pn_base, pn_offset;
|
||||
if (get_arg_addr(emit, op_str, pn_args[2], &pn_base, &pn_offset)) {
|
||||
mp_uint_t r_base = get_arg_reg(emit, op_str, pn_base, 15);
|
||||
mp_uint_t i8 = get_arg_i(emit, op_str, pn_offset, 0xff) >> 2;
|
||||
asm_thumb_op32(&emit->as, 0xe840 | r_base, (r_src << 12) | (r_dest << 8) | i8);
|
||||
}
|
||||
} else {
|
||||
goto unknown_op;
|
||||
}
|
||||
|
||||
} else {
|
||||
goto unknown_op;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
unknown_op:
|
||||
emit_inline_thumb_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, MP_ERROR_TEXT("unsupported Thumb instruction '%s' with %d arguments"), op_str, n_args));
|
||||
return;
|
||||
|
||||
branch_not_in_range:
|
||||
emit_inline_thumb_error_msg(emit, MP_ERROR_TEXT("branch not in range"));
|
||||
return;
|
||||
}
|
||||
|
||||
const emit_inline_asm_method_table_t emit_inline_thumb_method_table = {
|
||||
#if MICROPY_DYNAMIC_COMPILER
|
||||
emit_inline_thumb_new,
|
||||
emit_inline_thumb_free,
|
||||
#endif
|
||||
|
||||
emit_inline_thumb_start_pass,
|
||||
emit_inline_thumb_end_pass,
|
||||
emit_inline_thumb_count_params,
|
||||
emit_inline_thumb_label,
|
||||
emit_inline_thumb_op,
|
||||
};
|
||||
|
||||
#endif // MICROPY_EMIT_INLINE_THUMB
|
||||
@@ -0,0 +1,352 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013-2016 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdarg.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "py/emit.h"
|
||||
#include "py/asmxtensa.h"
|
||||
|
||||
#if MICROPY_EMIT_INLINE_XTENSA
|
||||
|
||||
struct _emit_inline_asm_t {
|
||||
asm_xtensa_t as;
|
||||
uint16_t pass;
|
||||
mp_obj_t *error_slot;
|
||||
mp_uint_t max_num_labels;
|
||||
qstr *label_lookup;
|
||||
};
|
||||
|
||||
STATIC void emit_inline_xtensa_error_msg(emit_inline_asm_t *emit, mp_rom_error_text_t msg) {
|
||||
*emit->error_slot = mp_obj_new_exception_msg(&mp_type_SyntaxError, msg);
|
||||
}
|
||||
|
||||
STATIC void emit_inline_xtensa_error_exc(emit_inline_asm_t *emit, mp_obj_t exc) {
|
||||
*emit->error_slot = exc;
|
||||
}
|
||||
|
||||
emit_inline_asm_t *emit_inline_xtensa_new(mp_uint_t max_num_labels) {
|
||||
emit_inline_asm_t *emit = m_new_obj(emit_inline_asm_t);
|
||||
memset(&emit->as, 0, sizeof(emit->as));
|
||||
mp_asm_base_init(&emit->as.base, max_num_labels);
|
||||
emit->max_num_labels = max_num_labels;
|
||||
emit->label_lookup = m_new(qstr, max_num_labels);
|
||||
return emit;
|
||||
}
|
||||
|
||||
void emit_inline_xtensa_free(emit_inline_asm_t *emit) {
|
||||
m_del(qstr, emit->label_lookup, emit->max_num_labels);
|
||||
mp_asm_base_deinit(&emit->as.base, false);
|
||||
m_del_obj(emit_inline_asm_t, emit);
|
||||
}
|
||||
|
||||
STATIC void emit_inline_xtensa_start_pass(emit_inline_asm_t *emit, pass_kind_t pass, mp_obj_t *error_slot) {
|
||||
emit->pass = pass;
|
||||
emit->error_slot = error_slot;
|
||||
if (emit->pass == MP_PASS_CODE_SIZE) {
|
||||
memset(emit->label_lookup, 0, emit->max_num_labels * sizeof(qstr));
|
||||
}
|
||||
mp_asm_base_start_pass(&emit->as.base, pass == MP_PASS_EMIT ? MP_ASM_PASS_EMIT : MP_ASM_PASS_COMPUTE);
|
||||
asm_xtensa_entry(&emit->as, 0);
|
||||
}
|
||||
|
||||
STATIC void emit_inline_xtensa_end_pass(emit_inline_asm_t *emit, mp_uint_t type_sig) {
|
||||
asm_xtensa_exit(&emit->as);
|
||||
asm_xtensa_end_pass(&emit->as);
|
||||
}
|
||||
|
||||
STATIC mp_uint_t emit_inline_xtensa_count_params(emit_inline_asm_t *emit, mp_uint_t n_params, mp_parse_node_t *pn_params) {
|
||||
if (n_params > 4) {
|
||||
emit_inline_xtensa_error_msg(emit, MP_ERROR_TEXT("can only have up to 4 parameters to Xtensa assembly"));
|
||||
return 0;
|
||||
}
|
||||
for (mp_uint_t i = 0; i < n_params; i++) {
|
||||
if (!MP_PARSE_NODE_IS_ID(pn_params[i])) {
|
||||
emit_inline_xtensa_error_msg(emit, MP_ERROR_TEXT("parameters must be registers in sequence a2 to a5"));
|
||||
return 0;
|
||||
}
|
||||
const char *p = qstr_str(MP_PARSE_NODE_LEAF_ARG(pn_params[i]));
|
||||
if (!(strlen(p) == 2 && p[0] == 'a' && (mp_uint_t)p[1] == '2' + i)) {
|
||||
emit_inline_xtensa_error_msg(emit, MP_ERROR_TEXT("parameters must be registers in sequence a2 to a5"));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return n_params;
|
||||
}
|
||||
|
||||
STATIC bool emit_inline_xtensa_label(emit_inline_asm_t *emit, mp_uint_t label_num, qstr label_id) {
|
||||
assert(label_num < emit->max_num_labels);
|
||||
if (emit->pass == MP_PASS_CODE_SIZE) {
|
||||
// check for duplicate label on first pass
|
||||
for (uint i = 0; i < emit->max_num_labels; i++) {
|
||||
if (emit->label_lookup[i] == label_id) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
emit->label_lookup[label_num] = label_id;
|
||||
mp_asm_base_label_assign(&emit->as.base, label_num);
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef struct _reg_name_t { byte reg;
|
||||
byte name[3];
|
||||
} reg_name_t;
|
||||
STATIC const reg_name_t reg_name_table[] = {
|
||||
{0, "a0\0"},
|
||||
{1, "a1\0"},
|
||||
{2, "a2\0"},
|
||||
{3, "a3\0"},
|
||||
{4, "a4\0"},
|
||||
{5, "a5\0"},
|
||||
{6, "a6\0"},
|
||||
{7, "a7\0"},
|
||||
{8, "a8\0"},
|
||||
{9, "a9\0"},
|
||||
{10, "a10"},
|
||||
{11, "a11"},
|
||||
{12, "a12"},
|
||||
{13, "a13"},
|
||||
{14, "a14"},
|
||||
{15, "a15"},
|
||||
};
|
||||
|
||||
// return empty string in case of error, so we can attempt to parse the string
|
||||
// without a special check if it was in fact a string
|
||||
STATIC const char *get_arg_str(mp_parse_node_t pn) {
|
||||
if (MP_PARSE_NODE_IS_ID(pn)) {
|
||||
qstr qst = MP_PARSE_NODE_LEAF_ARG(pn);
|
||||
return qstr_str(qst);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
STATIC mp_uint_t get_arg_reg(emit_inline_asm_t *emit, const char *op, mp_parse_node_t pn) {
|
||||
const char *reg_str = get_arg_str(pn);
|
||||
for (mp_uint_t i = 0; i < MP_ARRAY_SIZE(reg_name_table); i++) {
|
||||
const reg_name_t *r = ®_name_table[i];
|
||||
if (reg_str[0] == r->name[0]
|
||||
&& reg_str[1] == r->name[1]
|
||||
&& reg_str[2] == r->name[2]
|
||||
&& (reg_str[2] == '\0' || reg_str[3] == '\0')) {
|
||||
return r->reg;
|
||||
}
|
||||
}
|
||||
emit_inline_xtensa_error_exc(emit,
|
||||
mp_obj_new_exception_msg_varg(&mp_type_SyntaxError,
|
||||
MP_ERROR_TEXT("'%s' expects a register"), op));
|
||||
return 0;
|
||||
}
|
||||
|
||||
STATIC uint32_t get_arg_i(emit_inline_asm_t *emit, const char *op, mp_parse_node_t pn, int min, int max) {
|
||||
mp_obj_t o;
|
||||
if (!mp_parse_node_get_int_maybe(pn, &o)) {
|
||||
emit_inline_xtensa_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, MP_ERROR_TEXT("'%s' expects an integer"), op));
|
||||
return 0;
|
||||
}
|
||||
uint32_t i = mp_obj_get_int_truncated(o);
|
||||
if (min != max && ((int)i < min || (int)i > max)) {
|
||||
emit_inline_xtensa_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, MP_ERROR_TEXT("'%s' integer %d isn't within range %d..%d"), op, i, min, max));
|
||||
return 0;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
STATIC int get_arg_label(emit_inline_asm_t *emit, const char *op, mp_parse_node_t pn) {
|
||||
if (!MP_PARSE_NODE_IS_ID(pn)) {
|
||||
emit_inline_xtensa_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, MP_ERROR_TEXT("'%s' expects a label"), op));
|
||||
return 0;
|
||||
}
|
||||
qstr label_qstr = MP_PARSE_NODE_LEAF_ARG(pn);
|
||||
for (uint i = 0; i < emit->max_num_labels; i++) {
|
||||
if (emit->label_lookup[i] == label_qstr) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
// only need to have the labels on the last pass
|
||||
if (emit->pass == MP_PASS_EMIT) {
|
||||
emit_inline_xtensa_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, MP_ERROR_TEXT("label '%q' not defined"), label_qstr));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define RRR (0)
|
||||
#define RRI8 (1)
|
||||
#define RRI8_B (2)
|
||||
|
||||
typedef struct _opcode_table_3arg_t {
|
||||
uint16_t name; // actually a qstr, which should fit in 16 bits
|
||||
uint8_t type;
|
||||
uint8_t a0 : 4;
|
||||
uint8_t a1 : 4;
|
||||
} opcode_table_3arg_t;
|
||||
|
||||
STATIC const opcode_table_3arg_t opcode_table_3arg[] = {
|
||||
// arithmetic opcodes: reg, reg, reg
|
||||
{MP_QSTR_and_, RRR, 0, 1},
|
||||
{MP_QSTR_or_, RRR, 0, 2},
|
||||
{MP_QSTR_xor, RRR, 0, 3},
|
||||
{MP_QSTR_add, RRR, 0, 8},
|
||||
{MP_QSTR_sub, RRR, 0, 12},
|
||||
{MP_QSTR_mull, RRR, 2, 8},
|
||||
|
||||
// load/store/addi opcodes: reg, reg, imm
|
||||
// upper nibble of type encodes the range of the immediate arg
|
||||
{MP_QSTR_l8ui, RRI8 | 0x10, 2, 0},
|
||||
{MP_QSTR_l16ui, RRI8 | 0x30, 2, 1},
|
||||
{MP_QSTR_l32i, RRI8 | 0x50, 2, 2},
|
||||
{MP_QSTR_s8i, RRI8 | 0x10, 2, 4},
|
||||
{MP_QSTR_s16i, RRI8 | 0x30, 2, 5},
|
||||
{MP_QSTR_s32i, RRI8 | 0x50, 2, 6},
|
||||
{MP_QSTR_l16si, RRI8 | 0x30, 2, 9},
|
||||
{MP_QSTR_addi, RRI8 | 0x00, 2, 12},
|
||||
|
||||
// branch opcodes: reg, reg, label
|
||||
{MP_QSTR_ball, RRI8_B, ASM_XTENSA_CC_ALL, 0},
|
||||
{MP_QSTR_bany, RRI8_B, ASM_XTENSA_CC_ANY, 0},
|
||||
{MP_QSTR_bbc, RRI8_B, ASM_XTENSA_CC_BC, 0},
|
||||
{MP_QSTR_bbs, RRI8_B, ASM_XTENSA_CC_BS, 0},
|
||||
{MP_QSTR_beq, RRI8_B, ASM_XTENSA_CC_EQ, 0},
|
||||
{MP_QSTR_bge, RRI8_B, ASM_XTENSA_CC_GE, 0},
|
||||
{MP_QSTR_bgeu, RRI8_B, ASM_XTENSA_CC_GEU, 0},
|
||||
{MP_QSTR_blt, RRI8_B, ASM_XTENSA_CC_LT, 0},
|
||||
{MP_QSTR_bnall, RRI8_B, ASM_XTENSA_CC_NALL, 0},
|
||||
{MP_QSTR_bne, RRI8_B, ASM_XTENSA_CC_NE, 0},
|
||||
{MP_QSTR_bnone, RRI8_B, ASM_XTENSA_CC_NONE, 0},
|
||||
};
|
||||
|
||||
STATIC void emit_inline_xtensa_op(emit_inline_asm_t *emit, qstr op, mp_uint_t n_args, mp_parse_node_t *pn_args) {
|
||||
size_t op_len;
|
||||
const char *op_str = (const char *)qstr_data(op, &op_len);
|
||||
|
||||
if (n_args == 0) {
|
||||
if (op == MP_QSTR_ret_n) {
|
||||
asm_xtensa_op_ret_n(&emit->as);
|
||||
} else {
|
||||
goto unknown_op;
|
||||
}
|
||||
|
||||
} else if (n_args == 1) {
|
||||
if (op == MP_QSTR_callx0) {
|
||||
uint r0 = get_arg_reg(emit, op_str, pn_args[0]);
|
||||
asm_xtensa_op_callx0(&emit->as, r0);
|
||||
} else if (op == MP_QSTR_j) {
|
||||
int label = get_arg_label(emit, op_str, pn_args[0]);
|
||||
asm_xtensa_j_label(&emit->as, label);
|
||||
} else if (op == MP_QSTR_jx) {
|
||||
uint r0 = get_arg_reg(emit, op_str, pn_args[0]);
|
||||
asm_xtensa_op_jx(&emit->as, r0);
|
||||
} else {
|
||||
goto unknown_op;
|
||||
}
|
||||
|
||||
} else if (n_args == 2) {
|
||||
uint r0 = get_arg_reg(emit, op_str, pn_args[0]);
|
||||
if (op == MP_QSTR_beqz) {
|
||||
int label = get_arg_label(emit, op_str, pn_args[1]);
|
||||
asm_xtensa_bccz_reg_label(&emit->as, ASM_XTENSA_CCZ_EQ, r0, label);
|
||||
} else if (op == MP_QSTR_bnez) {
|
||||
int label = get_arg_label(emit, op_str, pn_args[1]);
|
||||
asm_xtensa_bccz_reg_label(&emit->as, ASM_XTENSA_CCZ_NE, r0, label);
|
||||
} else if (op == MP_QSTR_mov || op == MP_QSTR_mov_n) {
|
||||
// we emit mov.n for both "mov" and "mov_n" opcodes
|
||||
uint r1 = get_arg_reg(emit, op_str, pn_args[1]);
|
||||
asm_xtensa_op_mov_n(&emit->as, r0, r1);
|
||||
} else if (op == MP_QSTR_movi) {
|
||||
// for convenience we emit l32r if the integer doesn't fit in movi
|
||||
uint32_t imm = get_arg_i(emit, op_str, pn_args[1], 0, 0);
|
||||
asm_xtensa_mov_reg_i32(&emit->as, r0, imm);
|
||||
} else {
|
||||
goto unknown_op;
|
||||
}
|
||||
|
||||
} else if (n_args == 3) {
|
||||
// search table for 3 arg instructions
|
||||
for (uint i = 0; i < MP_ARRAY_SIZE(opcode_table_3arg); i++) {
|
||||
const opcode_table_3arg_t *o = &opcode_table_3arg[i];
|
||||
if (op == o->name) {
|
||||
uint r0 = get_arg_reg(emit, op_str, pn_args[0]);
|
||||
uint r1 = get_arg_reg(emit, op_str, pn_args[1]);
|
||||
if (o->type == RRR) {
|
||||
uint r2 = get_arg_reg(emit, op_str, pn_args[2]);
|
||||
asm_xtensa_op24(&emit->as, ASM_XTENSA_ENCODE_RRR(0, o->a0, o->a1, r0, r1, r2));
|
||||
} else if (o->type == RRI8_B) {
|
||||
int label = get_arg_label(emit, op_str, pn_args[2]);
|
||||
asm_xtensa_bcc_reg_reg_label(&emit->as, o->a0, r0, r1, label);
|
||||
} else {
|
||||
int shift, min, max;
|
||||
if ((o->type & 0xf0) == 0) {
|
||||
shift = 0;
|
||||
min = -128;
|
||||
max = 127;
|
||||
} else {
|
||||
shift = (o->type & 0xf0) >> 5;
|
||||
min = 0;
|
||||
max = 0xff << shift;
|
||||
}
|
||||
uint32_t imm = get_arg_i(emit, op_str, pn_args[2], min, max);
|
||||
asm_xtensa_op24(&emit->as, ASM_XTENSA_ENCODE_RRI8(o->a0, o->a1, r1, r0, (imm >> shift) & 0xff));
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
goto unknown_op;
|
||||
|
||||
} else {
|
||||
goto unknown_op;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
unknown_op:
|
||||
emit_inline_xtensa_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, MP_ERROR_TEXT("unsupported Xtensa instruction '%s' with %d arguments"), op_str, n_args));
|
||||
return;
|
||||
|
||||
/*
|
||||
branch_not_in_range:
|
||||
emit_inline_xtensa_error_msg(emit, MP_ERROR_TEXT("branch not in range"));
|
||||
return;
|
||||
*/
|
||||
}
|
||||
|
||||
const emit_inline_asm_method_table_t emit_inline_xtensa_method_table = {
|
||||
#if MICROPY_DYNAMIC_COMPILER
|
||||
emit_inline_xtensa_new,
|
||||
emit_inline_xtensa_free,
|
||||
#endif
|
||||
|
||||
emit_inline_xtensa_start_pass,
|
||||
emit_inline_xtensa_end_pass,
|
||||
emit_inline_xtensa_count_params,
|
||||
emit_inline_xtensa_label,
|
||||
emit_inline_xtensa_op,
|
||||
};
|
||||
|
||||
#endif // MICROPY_EMIT_INLINE_XTENSA
|
||||
@@ -0,0 +1,20 @@
|
||||
// ARM specific stuff
|
||||
|
||||
#include "py/mpconfig.h"
|
||||
|
||||
#if MICROPY_EMIT_ARM
|
||||
|
||||
// This is defined so that the assembler exports generic assembler API macros
|
||||
#define GENERIC_ASM_API (1)
|
||||
#include "py/asmarm.h"
|
||||
|
||||
// Word indices of REG_LOCAL_x in nlr_buf_t
|
||||
#define NLR_BUF_IDX_LOCAL_1 (3) // r4
|
||||
#define NLR_BUF_IDX_LOCAL_2 (4) // r5
|
||||
#define NLR_BUF_IDX_LOCAL_3 (5) // r6
|
||||
|
||||
#define N_ARM (1)
|
||||
#define EXPORT_FUN(name) emit_native_arm_##name
|
||||
#include "py/emitnative.c"
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,20 @@
|
||||
// thumb specific stuff
|
||||
|
||||
#include "py/mpconfig.h"
|
||||
|
||||
#if MICROPY_EMIT_THUMB
|
||||
|
||||
// this is defined so that the assembler exports generic assembler API macros
|
||||
#define GENERIC_ASM_API (1)
|
||||
#include "py/asmthumb.h"
|
||||
|
||||
// Word indices of REG_LOCAL_x in nlr_buf_t
|
||||
#define NLR_BUF_IDX_LOCAL_1 (3) // r4
|
||||
#define NLR_BUF_IDX_LOCAL_2 (4) // r5
|
||||
#define NLR_BUF_IDX_LOCAL_3 (5) // r6
|
||||
|
||||
#define N_THUMB (1)
|
||||
#define EXPORT_FUN(name) emit_native_thumb_##name
|
||||
#include "py/emitnative.c"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,20 @@
|
||||
// x64 specific stuff
|
||||
|
||||
#include "py/mpconfig.h"
|
||||
|
||||
#if MICROPY_EMIT_X64
|
||||
|
||||
// This is defined so that the assembler exports generic assembler API macros
|
||||
#define GENERIC_ASM_API (1)
|
||||
#include "py/asmx64.h"
|
||||
|
||||
// Word indices of REG_LOCAL_x in nlr_buf_t
|
||||
#define NLR_BUF_IDX_LOCAL_1 (5) // rbx
|
||||
#define NLR_BUF_IDX_LOCAL_2 (6) // r12
|
||||
#define NLR_BUF_IDX_LOCAL_3 (7) // r13
|
||||
|
||||
#define N_X64 (1)
|
||||
#define EXPORT_FUN(name) emit_native_x64_##name
|
||||
#include "py/emitnative.c"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,72 @@
|
||||
// x86 specific stuff
|
||||
|
||||
#include "py/mpconfig.h"
|
||||
#include "py/nativeglue.h"
|
||||
|
||||
#if MICROPY_EMIT_X86
|
||||
|
||||
// This is defined so that the assembler exports generic assembler API macros
|
||||
#define GENERIC_ASM_API (1)
|
||||
#include "py/asmx86.h"
|
||||
|
||||
// Word indices of REG_LOCAL_x in nlr_buf_t
|
||||
#define NLR_BUF_IDX_LOCAL_1 (5) // ebx
|
||||
#define NLR_BUF_IDX_LOCAL_2 (7) // esi
|
||||
#define NLR_BUF_IDX_LOCAL_3 (6) // edi
|
||||
|
||||
// x86 needs a table to know how many args a given function has
|
||||
STATIC byte mp_f_n_args[MP_F_NUMBER_OF] = {
|
||||
[MP_F_CONVERT_OBJ_TO_NATIVE] = 2,
|
||||
[MP_F_CONVERT_NATIVE_TO_OBJ] = 2,
|
||||
[MP_F_NATIVE_SWAP_GLOBALS] = 1,
|
||||
[MP_F_LOAD_NAME] = 1,
|
||||
[MP_F_LOAD_GLOBAL] = 1,
|
||||
[MP_F_LOAD_BUILD_CLASS] = 0,
|
||||
[MP_F_LOAD_ATTR] = 2,
|
||||
[MP_F_LOAD_METHOD] = 3,
|
||||
[MP_F_LOAD_SUPER_METHOD] = 2,
|
||||
[MP_F_STORE_NAME] = 2,
|
||||
[MP_F_STORE_GLOBAL] = 2,
|
||||
[MP_F_STORE_ATTR] = 3,
|
||||
[MP_F_OBJ_SUBSCR] = 3,
|
||||
[MP_F_OBJ_IS_TRUE] = 1,
|
||||
[MP_F_UNARY_OP] = 2,
|
||||
[MP_F_BINARY_OP] = 3,
|
||||
[MP_F_BUILD_TUPLE] = 2,
|
||||
[MP_F_BUILD_LIST] = 2,
|
||||
[MP_F_BUILD_MAP] = 1,
|
||||
[MP_F_BUILD_SET] = 2,
|
||||
[MP_F_STORE_SET] = 2,
|
||||
[MP_F_LIST_APPEND] = 2,
|
||||
[MP_F_STORE_MAP] = 3,
|
||||
[MP_F_MAKE_FUNCTION_FROM_RAW_CODE] = 3,
|
||||
[MP_F_NATIVE_CALL_FUNCTION_N_KW] = 3,
|
||||
[MP_F_CALL_METHOD_N_KW] = 3,
|
||||
[MP_F_CALL_METHOD_N_KW_VAR] = 3,
|
||||
[MP_F_NATIVE_GETITER] = 2,
|
||||
[MP_F_NATIVE_ITERNEXT] = 1,
|
||||
[MP_F_NLR_PUSH] = 1,
|
||||
[MP_F_NLR_POP] = 0,
|
||||
[MP_F_NATIVE_RAISE] = 1,
|
||||
[MP_F_IMPORT_NAME] = 3,
|
||||
[MP_F_IMPORT_FROM] = 2,
|
||||
[MP_F_IMPORT_ALL] = 1,
|
||||
[MP_F_NEW_SLICE] = 3,
|
||||
[MP_F_UNPACK_SEQUENCE] = 3,
|
||||
[MP_F_UNPACK_EX] = 3,
|
||||
[MP_F_DELETE_NAME] = 1,
|
||||
[MP_F_DELETE_GLOBAL] = 1,
|
||||
[MP_F_MAKE_CLOSURE_FROM_RAW_CODE] = 3,
|
||||
[MP_F_ARG_CHECK_NUM_SIG] = 3,
|
||||
[MP_F_SETUP_CODE_STATE] = 4,
|
||||
[MP_F_SMALL_INT_FLOOR_DIVIDE] = 2,
|
||||
[MP_F_SMALL_INT_MODULO] = 2,
|
||||
[MP_F_NATIVE_YIELD_FROM] = 3,
|
||||
[MP_F_SETJMP] = 1,
|
||||
};
|
||||
|
||||
#define N_X86 (1)
|
||||
#define EXPORT_FUN(name) emit_native_x86_##name
|
||||
#include "py/emitnative.c"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,20 @@
|
||||
// Xtensa specific stuff
|
||||
|
||||
#include "py/mpconfig.h"
|
||||
|
||||
#if MICROPY_EMIT_XTENSA
|
||||
|
||||
// this is defined so that the assembler exports generic assembler API macros
|
||||
#define GENERIC_ASM_API (1)
|
||||
#include "py/asmxtensa.h"
|
||||
|
||||
// Word indices of REG_LOCAL_x in nlr_buf_t
|
||||
#define NLR_BUF_IDX_LOCAL_1 (8) // a12
|
||||
#define NLR_BUF_IDX_LOCAL_2 (9) // a13
|
||||
#define NLR_BUF_IDX_LOCAL_3 (10) // a14
|
||||
|
||||
#define N_XTENSA (1)
|
||||
#define EXPORT_FUN(name) emit_native_xtensa_##name
|
||||
#include "py/emitnative.c"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,23 @@
|
||||
// Xtensa-Windowed specific stuff
|
||||
|
||||
#include "py/mpconfig.h"
|
||||
|
||||
#if MICROPY_EMIT_XTENSAWIN
|
||||
|
||||
// this is defined so that the assembler exports generic assembler API macros
|
||||
#define GENERIC_ASM_API (1)
|
||||
#define GENERIC_ASM_API_WIN (1)
|
||||
#include "py/asmxtensa.h"
|
||||
|
||||
// Word indices of REG_LOCAL_x in nlr_buf_t
|
||||
#define NLR_BUF_IDX_LOCAL_1 (2 + 4) // a4
|
||||
#define NLR_BUF_IDX_LOCAL_2 (2 + 5) // a5
|
||||
#define NLR_BUF_IDX_LOCAL_3 (2 + 6) // a6
|
||||
|
||||
#define N_NLR_SETJMP (1)
|
||||
#define N_PRELUDE_AS_BYTES_OBJ (1)
|
||||
#define N_XTENSAWIN (1)
|
||||
#define EXPORT_FUN(name) emit_native_xtensawin_##name
|
||||
#include "py/emitnative.c"
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,438 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013, 2014 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "py/mpconfig.h"
|
||||
#if MICROPY_FLOAT_IMPL != MICROPY_FLOAT_IMPL_NONE
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <math.h>
|
||||
#include "py/formatfloat.h"
|
||||
|
||||
/***********************************************************************
|
||||
|
||||
Routine for converting a arbitrary floating
|
||||
point number into a string.
|
||||
|
||||
The code in this funcion was inspired from Fred Bayer's pdouble.c.
|
||||
Since pdouble.c was released as Public Domain, I'm releasing this
|
||||
code as public domain as well.
|
||||
|
||||
The original code can be found in https://github.com/dhylands/format-float
|
||||
|
||||
Dave Hylands
|
||||
|
||||
***********************************************************************/
|
||||
|
||||
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
|
||||
// 1 sign bit, 8 exponent bits, and 23 mantissa bits.
|
||||
// exponent values 0 and 255 are reserved, exponent can be 1 to 254.
|
||||
// exponent is stored with a bias of 127.
|
||||
// The min and max floats are on the order of 1x10^37 and 1x10^-37
|
||||
|
||||
#define FPTYPE float
|
||||
#define FPCONST(x) x##F
|
||||
#define FPROUND_TO_ONE 0.9999995F
|
||||
#define FPDECEXP 32
|
||||
#define FPMIN_BUF_SIZE 6 // +9e+99
|
||||
|
||||
#define FLT_SIGN_MASK 0x80000000
|
||||
#define FLT_EXP_MASK 0x7F800000
|
||||
#define FLT_MAN_MASK 0x007FFFFF
|
||||
|
||||
union floatbits {
|
||||
float f;
|
||||
uint32_t u;
|
||||
};
|
||||
static inline int fp_signbit(float x) {
|
||||
union floatbits fb = {x};
|
||||
return fb.u & FLT_SIGN_MASK;
|
||||
}
|
||||
#define fp_isnan(x) isnan(x)
|
||||
#define fp_isinf(x) isinf(x)
|
||||
static inline int fp_iszero(float x) {
|
||||
union floatbits fb = {x};
|
||||
return fb.u == 0;
|
||||
}
|
||||
static inline int fp_isless1(float x) {
|
||||
union floatbits fb = {x};
|
||||
return fb.u < 0x3f800000;
|
||||
}
|
||||
|
||||
#elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE
|
||||
|
||||
#define FPTYPE double
|
||||
#define FPCONST(x) x
|
||||
#define FPROUND_TO_ONE 0.999999999995
|
||||
#define FPDECEXP 256
|
||||
#define FPMIN_BUF_SIZE 7 // +9e+199
|
||||
#define fp_signbit(x) signbit(x)
|
||||
#define fp_isnan(x) isnan(x)
|
||||
#define fp_isinf(x) isinf(x)
|
||||
#define fp_iszero(x) (x == 0)
|
||||
#define fp_isless1(x) (x < 1.0)
|
||||
|
||||
#endif
|
||||
|
||||
static const FPTYPE g_pos_pow[] = {
|
||||
#if FPDECEXP > 32
|
||||
MICROPY_FLOAT_CONST(1e256), MICROPY_FLOAT_CONST(1e128), MICROPY_FLOAT_CONST(1e64),
|
||||
#endif
|
||||
MICROPY_FLOAT_CONST(1e32), MICROPY_FLOAT_CONST(1e16), MICROPY_FLOAT_CONST(1e8), MICROPY_FLOAT_CONST(1e4), MICROPY_FLOAT_CONST(1e2), MICROPY_FLOAT_CONST(1e1)
|
||||
};
|
||||
static const FPTYPE g_neg_pow[] = {
|
||||
#if FPDECEXP > 32
|
||||
MICROPY_FLOAT_CONST(1e-256), MICROPY_FLOAT_CONST(1e-128), MICROPY_FLOAT_CONST(1e-64),
|
||||
#endif
|
||||
MICROPY_FLOAT_CONST(1e-32), MICROPY_FLOAT_CONST(1e-16), MICROPY_FLOAT_CONST(1e-8), MICROPY_FLOAT_CONST(1e-4), MICROPY_FLOAT_CONST(1e-2), MICROPY_FLOAT_CONST(1e-1)
|
||||
};
|
||||
|
||||
int mp_format_float(FPTYPE f, char *buf, size_t buf_size, char fmt, int prec, char sign) {
|
||||
|
||||
char *s = buf;
|
||||
|
||||
if (buf_size <= FPMIN_BUF_SIZE) {
|
||||
// FPMIN_BUF_SIZE is the minimum size needed to store any FP number.
|
||||
// If the buffer does not have enough room for this (plus null terminator)
|
||||
// then don't try to format the float.
|
||||
|
||||
if (buf_size >= 2) {
|
||||
*s++ = '?';
|
||||
}
|
||||
if (buf_size >= 1) {
|
||||
*s = '\0';
|
||||
}
|
||||
return buf_size >= 2;
|
||||
}
|
||||
if (fp_signbit(f) && !fp_isnan(f)) {
|
||||
*s++ = '-';
|
||||
f = -f;
|
||||
} else {
|
||||
if (sign) {
|
||||
*s++ = sign;
|
||||
}
|
||||
}
|
||||
|
||||
// buf_remaining contains bytes available for digits and exponent.
|
||||
// It is buf_size minus room for the sign and null byte.
|
||||
int buf_remaining = buf_size - 1 - (s - buf);
|
||||
|
||||
{
|
||||
char uc = fmt & 0x20;
|
||||
if (fp_isinf(f)) {
|
||||
*s++ = 'I' ^ uc;
|
||||
*s++ = 'N' ^ uc;
|
||||
*s++ = 'F' ^ uc;
|
||||
goto ret;
|
||||
} else if (fp_isnan(f)) {
|
||||
*s++ = 'N' ^ uc;
|
||||
*s++ = 'A' ^ uc;
|
||||
*s++ = 'N' ^ uc;
|
||||
ret:
|
||||
*s = '\0';
|
||||
return s - buf;
|
||||
}
|
||||
}
|
||||
|
||||
if (prec < 0) {
|
||||
prec = 6;
|
||||
}
|
||||
char e_char = 'E' | (fmt & 0x20); // e_char will match case of fmt
|
||||
fmt |= 0x20; // Force fmt to be lowercase
|
||||
char org_fmt = fmt;
|
||||
if (fmt == 'g' && prec == 0) {
|
||||
prec = 1;
|
||||
}
|
||||
int e, e1;
|
||||
int dec = 0;
|
||||
char e_sign = '\0';
|
||||
int num_digits = 0;
|
||||
const FPTYPE *pos_pow = g_pos_pow;
|
||||
const FPTYPE *neg_pow = g_neg_pow;
|
||||
|
||||
if (fp_iszero(f)) {
|
||||
e = 0;
|
||||
if (fmt == 'f') {
|
||||
// Truncate precision to prevent buffer overflow
|
||||
if (prec + 2 > buf_remaining) {
|
||||
prec = buf_remaining - 2;
|
||||
}
|
||||
num_digits = prec + 1;
|
||||
} else {
|
||||
// Truncate precision to prevent buffer overflow
|
||||
if (prec + 6 > buf_remaining) {
|
||||
prec = buf_remaining - 6;
|
||||
}
|
||||
if (fmt == 'e') {
|
||||
e_sign = '+';
|
||||
}
|
||||
}
|
||||
} else if (fp_isless1(f)) {
|
||||
// We need to figure out what an integer digit will be used
|
||||
// in case 'f' is used (or we revert other format to it below).
|
||||
// As we just tested number to be <1, this is obviously 0,
|
||||
// but we can round it up to 1 below.
|
||||
char first_dig = '0';
|
||||
if (f >= FPROUND_TO_ONE) {
|
||||
first_dig = '1';
|
||||
}
|
||||
|
||||
// Build negative exponent
|
||||
for (e = 0, e1 = FPDECEXP; e1; e1 >>= 1, pos_pow++, neg_pow++) {
|
||||
if (*neg_pow > f) {
|
||||
e += e1;
|
||||
f *= *pos_pow;
|
||||
}
|
||||
}
|
||||
char e_sign_char = '-';
|
||||
if (fp_isless1(f) && f >= FPROUND_TO_ONE) {
|
||||
f = FPCONST(1.0);
|
||||
if (e == 0) {
|
||||
e_sign_char = '+';
|
||||
}
|
||||
} else if (fp_isless1(f)) {
|
||||
e++;
|
||||
f *= FPCONST(10.0);
|
||||
}
|
||||
|
||||
// If the user specified 'g' format, and e is <= 4, then we'll switch
|
||||
// to the fixed format ('f')
|
||||
|
||||
if (fmt == 'f' || (fmt == 'g' && e <= 4)) {
|
||||
fmt = 'f';
|
||||
dec = -1;
|
||||
*s++ = first_dig;
|
||||
|
||||
if (org_fmt == 'g') {
|
||||
prec += (e - 1);
|
||||
}
|
||||
|
||||
// truncate precision to prevent buffer overflow
|
||||
if (prec + 2 > buf_remaining) {
|
||||
prec = buf_remaining - 2;
|
||||
}
|
||||
|
||||
num_digits = prec;
|
||||
if (num_digits) {
|
||||
*s++ = '.';
|
||||
while (--e && num_digits) {
|
||||
*s++ = '0';
|
||||
num_digits--;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// For e & g formats, we'll be printing the exponent, so set the
|
||||
// sign.
|
||||
e_sign = e_sign_char;
|
||||
dec = 0;
|
||||
|
||||
if (prec > (buf_remaining - FPMIN_BUF_SIZE)) {
|
||||
prec = buf_remaining - FPMIN_BUF_SIZE;
|
||||
if (fmt == 'g') {
|
||||
prec++;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Build positive exponent
|
||||
for (e = 0, e1 = FPDECEXP; e1; e1 >>= 1, pos_pow++, neg_pow++) {
|
||||
if (*pos_pow <= f) {
|
||||
e += e1;
|
||||
f *= *neg_pow;
|
||||
}
|
||||
}
|
||||
|
||||
// It can be that f was right on the edge of an entry in pos_pow needs to be reduced
|
||||
if ((int)f >= 10) {
|
||||
e += 1;
|
||||
f *= FPCONST(0.1);
|
||||
}
|
||||
|
||||
// If the user specified fixed format (fmt == 'f') and e makes the
|
||||
// number too big to fit into the available buffer, then we'll
|
||||
// switch to the 'e' format.
|
||||
|
||||
if (fmt == 'f') {
|
||||
if (e >= buf_remaining) {
|
||||
fmt = 'e';
|
||||
} else if ((e + prec + 2) > buf_remaining) {
|
||||
prec = buf_remaining - e - 2;
|
||||
if (prec < 0) {
|
||||
// This means no decimal point, so we can add one back
|
||||
// for the decimal.
|
||||
prec++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fmt == 'e' && prec > (buf_remaining - FPMIN_BUF_SIZE)) {
|
||||
prec = buf_remaining - FPMIN_BUF_SIZE;
|
||||
}
|
||||
if (fmt == 'g') {
|
||||
// Truncate precision to prevent buffer overflow
|
||||
if (prec + (FPMIN_BUF_SIZE - 1) > buf_remaining) {
|
||||
prec = buf_remaining - (FPMIN_BUF_SIZE - 1);
|
||||
}
|
||||
}
|
||||
// If the user specified 'g' format, and e is < prec, then we'll switch
|
||||
// to the fixed format.
|
||||
|
||||
if (fmt == 'g' && e < prec) {
|
||||
fmt = 'f';
|
||||
prec -= (e + 1);
|
||||
}
|
||||
if (fmt == 'f') {
|
||||
dec = e;
|
||||
num_digits = prec + e + 1;
|
||||
} else {
|
||||
e_sign = '+';
|
||||
}
|
||||
}
|
||||
if (prec < 0) {
|
||||
// This can happen when the prec is trimmed to prevent buffer overflow
|
||||
prec = 0;
|
||||
}
|
||||
|
||||
// We now have num.f as a floating point number between >= 1 and < 10
|
||||
// (or equal to zero), and e contains the absolute value of the power of
|
||||
// 10 exponent. and (dec + 1) == the number of dgits before the decimal.
|
||||
|
||||
// For e, prec is # digits after the decimal
|
||||
// For f, prec is # digits after the decimal
|
||||
// For g, prec is the max number of significant digits
|
||||
//
|
||||
// For e & g there will be a single digit before the decimal
|
||||
// for f there will be e digits before the decimal
|
||||
|
||||
if (fmt == 'e') {
|
||||
num_digits = prec + 1;
|
||||
} else if (fmt == 'g') {
|
||||
if (prec == 0) {
|
||||
prec = 1;
|
||||
}
|
||||
num_digits = prec;
|
||||
}
|
||||
|
||||
// Print the digits of the mantissa
|
||||
for (int i = 0; i < num_digits; ++i, --dec) {
|
||||
int32_t d = (int32_t)f;
|
||||
if (d < 0) {
|
||||
*s++ = '0';
|
||||
} else {
|
||||
*s++ = '0' + d;
|
||||
}
|
||||
if (dec == 0 && prec > 0) {
|
||||
*s++ = '.';
|
||||
}
|
||||
f -= (FPTYPE)d;
|
||||
f *= FPCONST(10.0);
|
||||
}
|
||||
|
||||
// Round
|
||||
// If we print non-exponential format (i.e. 'f'), but a digit we're going
|
||||
// to round by (e) is too far away, then there's nothing to round.
|
||||
if ((org_fmt != 'f' || e <= num_digits) && f >= FPCONST(5.0)) {
|
||||
char *rs = s;
|
||||
rs--;
|
||||
while (1) {
|
||||
if (*rs == '.') {
|
||||
rs--;
|
||||
continue;
|
||||
}
|
||||
if (*rs < '0' || *rs > '9') {
|
||||
// + or -
|
||||
rs++; // So we sit on the digit to the right of the sign
|
||||
break;
|
||||
}
|
||||
if (*rs < '9') {
|
||||
(*rs)++;
|
||||
break;
|
||||
}
|
||||
*rs = '0';
|
||||
if (rs == buf) {
|
||||
break;
|
||||
}
|
||||
rs--;
|
||||
}
|
||||
if (*rs == '0') {
|
||||
// We need to insert a 1
|
||||
if (rs[1] == '.' && fmt != 'f') {
|
||||
// We're going to round 9.99 to 10.00
|
||||
// Move the decimal point
|
||||
rs[0] = '.';
|
||||
rs[1] = '0';
|
||||
if (e_sign == '-') {
|
||||
e--;
|
||||
if (e == 0) {
|
||||
e_sign = '+';
|
||||
}
|
||||
} else {
|
||||
e++;
|
||||
}
|
||||
} else {
|
||||
// Need at extra digit at the end to make room for the leading '1'
|
||||
s++;
|
||||
}
|
||||
char *ss = s;
|
||||
while (ss > rs) {
|
||||
*ss = ss[-1];
|
||||
ss--;
|
||||
}
|
||||
*rs = '1';
|
||||
}
|
||||
}
|
||||
|
||||
// verify that we did not overrun the input buffer so far
|
||||
assert((size_t)(s + 1 - buf) <= buf_size);
|
||||
|
||||
if (org_fmt == 'g' && prec > 0) {
|
||||
// Remove trailing zeros and a trailing decimal point
|
||||
while (s[-1] == '0') {
|
||||
s--;
|
||||
}
|
||||
if (s[-1] == '.') {
|
||||
s--;
|
||||
}
|
||||
}
|
||||
// Append the exponent
|
||||
if (e_sign) {
|
||||
*s++ = e_char;
|
||||
*s++ = e_sign;
|
||||
if (FPMIN_BUF_SIZE == 7 && e >= 100) {
|
||||
*s++ = '0' + (e / 100);
|
||||
}
|
||||
*s++ = '0' + ((e / 10) % 10);
|
||||
*s++ = '0' + (e % 10);
|
||||
}
|
||||
*s = '\0';
|
||||
|
||||
// verify that we did not overrun the input buffer
|
||||
assert((size_t)(s + 1 - buf) <= buf_size);
|
||||
|
||||
return s - buf;
|
||||
}
|
||||
|
||||
#endif // MICROPY_FLOAT_IMPL != MICROPY_FLOAT_IMPL_NONE
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013, 2014 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef MICROPY_INCLUDED_PY_FORMATFLOAT_H
|
||||
#define MICROPY_INCLUDED_PY_FORMATFLOAT_H
|
||||
|
||||
#include "py/mpconfig.h"
|
||||
|
||||
#if MICROPY_PY_BUILTINS_FLOAT
|
||||
int mp_format_float(mp_float_t f, char *buf, size_t bufSize, char fmt, int prec, char sign);
|
||||
#endif
|
||||
|
||||
#endif // MICROPY_INCLUDED_PY_FORMATFLOAT_H
|
||||
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015 Paul Sokolovsky
|
||||
* Copyright (c) 2016 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "py/lexer.h"
|
||||
#include "py/frozenmod.h"
|
||||
|
||||
#if MICROPY_MODULE_FROZEN_STR
|
||||
|
||||
#ifndef MICROPY_MODULE_FROZEN_LEXER
|
||||
#define MICROPY_MODULE_FROZEN_LEXER mp_lexer_new_from_str_len
|
||||
#else
|
||||
mp_lexer_t *MICROPY_MODULE_FROZEN_LEXER(qstr src_name, const char *str, mp_uint_t len, mp_uint_t free_len);
|
||||
#endif
|
||||
|
||||
extern const char mp_frozen_str_names[];
|
||||
extern const uint32_t mp_frozen_str_sizes[];
|
||||
extern const char mp_frozen_str_content[];
|
||||
|
||||
// On input, *len contains size of name, on output - size of content
|
||||
const char *mp_find_frozen_str(const char *str, size_t *len) {
|
||||
const char *name = mp_frozen_str_names;
|
||||
|
||||
size_t offset = 0;
|
||||
for (int i = 0; *name != 0; i++) {
|
||||
size_t l = strlen(name);
|
||||
if (l == *len && !memcmp(str, name, l)) {
|
||||
*len = mp_frozen_str_sizes[i];
|
||||
return mp_frozen_str_content + offset;
|
||||
}
|
||||
name += l + 1;
|
||||
offset += mp_frozen_str_sizes[i] + 1;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
STATIC mp_lexer_t *mp_lexer_frozen_str(const char *str, size_t len) {
|
||||
size_t name_len = len;
|
||||
const char *content = mp_find_frozen_str(str, &len);
|
||||
|
||||
if (content == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
qstr source = qstr_from_strn(str, name_len);
|
||||
mp_lexer_t *lex = MICROPY_MODULE_FROZEN_LEXER(source, content, len, 0);
|
||||
return lex;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if MICROPY_MODULE_FROZEN_MPY
|
||||
|
||||
#include "py/emitglue.h"
|
||||
|
||||
extern const char mp_frozen_mpy_names[];
|
||||
extern const mp_raw_code_t *const mp_frozen_mpy_content[];
|
||||
|
||||
STATIC const mp_raw_code_t *mp_find_frozen_mpy(const char *str, size_t len) {
|
||||
const char *name = mp_frozen_mpy_names;
|
||||
for (size_t i = 0; *name != 0; i++) {
|
||||
size_t l = strlen(name);
|
||||
if (l == len && !memcmp(str, name, l)) {
|
||||
return mp_frozen_mpy_content[i];
|
||||
}
|
||||
name += l + 1;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if MICROPY_MODULE_FROZEN
|
||||
|
||||
STATIC mp_import_stat_t mp_frozen_stat_helper(const char *name, const char *str) {
|
||||
size_t len = strlen(str);
|
||||
|
||||
for (int i = 0; *name != 0; i++) {
|
||||
size_t l = strlen(name);
|
||||
if (l >= len && !memcmp(str, name, len)) {
|
||||
if (name[len] == 0) {
|
||||
return MP_IMPORT_STAT_FILE;
|
||||
} else if (name[len] == '/') {
|
||||
return MP_IMPORT_STAT_DIR;
|
||||
}
|
||||
}
|
||||
name += l + 1;
|
||||
}
|
||||
return MP_IMPORT_STAT_NO_EXIST;
|
||||
}
|
||||
|
||||
mp_import_stat_t mp_frozen_stat(const char *str) {
|
||||
mp_import_stat_t stat;
|
||||
|
||||
#if MICROPY_MODULE_FROZEN_STR
|
||||
stat = mp_frozen_stat_helper(mp_frozen_str_names, str);
|
||||
if (stat != MP_IMPORT_STAT_NO_EXIST) {
|
||||
return stat;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if MICROPY_MODULE_FROZEN_MPY
|
||||
stat = mp_frozen_stat_helper(mp_frozen_mpy_names, str);
|
||||
if (stat != MP_IMPORT_STAT_NO_EXIST) {
|
||||
return stat;
|
||||
}
|
||||
#endif
|
||||
|
||||
return MP_IMPORT_STAT_NO_EXIST;
|
||||
}
|
||||
|
||||
int mp_find_frozen_module(const char *str, size_t len, void **data) {
|
||||
#if MICROPY_MODULE_FROZEN_STR
|
||||
mp_lexer_t *lex = mp_lexer_frozen_str(str, len);
|
||||
if (lex != NULL) {
|
||||
*data = lex;
|
||||
return MP_FROZEN_STR;
|
||||
}
|
||||
#endif
|
||||
#if MICROPY_MODULE_FROZEN_MPY
|
||||
const mp_raw_code_t *rc = mp_find_frozen_mpy(str, len);
|
||||
if (rc != NULL) {
|
||||
*data = (void *)rc;
|
||||
return MP_FROZEN_MPY;
|
||||
}
|
||||
#endif
|
||||
return MP_FROZEN_NONE;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015 Paul Sokolovsky
|
||||
* Copyright (c) 2016 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef MICROPY_INCLUDED_PY_FROZENMOD_H
|
||||
#define MICROPY_INCLUDED_PY_FROZENMOD_H
|
||||
|
||||
#include "py/lexer.h"
|
||||
|
||||
enum {
|
||||
MP_FROZEN_NONE,
|
||||
MP_FROZEN_STR,
|
||||
MP_FROZEN_MPY,
|
||||
};
|
||||
|
||||
int mp_find_frozen_module(const char *str, size_t len, void **data);
|
||||
const char *mp_find_frozen_str(const char *str, size_t *len);
|
||||
mp_import_stat_t mp_frozen_stat(const char *str);
|
||||
|
||||
#endif // MICROPY_INCLUDED_PY_FROZENMOD_H
|
||||
@@ -0,0 +1,975 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013, 2014 Damien P. George
|
||||
* Copyright (c) 2014 Paul Sokolovsky
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "py/gc.h"
|
||||
#include "py/runtime.h"
|
||||
|
||||
#if MICROPY_ENABLE_GC
|
||||
|
||||
#if MICROPY_DEBUG_VERBOSE // print debugging info
|
||||
#define DEBUG_PRINT (1)
|
||||
#define DEBUG_printf DEBUG_printf
|
||||
#else // don't print debugging info
|
||||
#define DEBUG_PRINT (0)
|
||||
#define DEBUG_printf(...) (void)0
|
||||
#endif
|
||||
|
||||
// make this 1 to dump the heap each time it changes
|
||||
#define EXTENSIVE_HEAP_PROFILING (0)
|
||||
|
||||
// make this 1 to zero out swept memory to more eagerly
|
||||
// detect untraced object still in use
|
||||
#define CLEAR_ON_SWEEP (0)
|
||||
|
||||
#define WORDS_PER_BLOCK ((MICROPY_BYTES_PER_GC_BLOCK) / BYTES_PER_WORD)
|
||||
#define BYTES_PER_BLOCK (MICROPY_BYTES_PER_GC_BLOCK)
|
||||
|
||||
// ATB = allocation table byte
|
||||
// 0b00 = FREE -- free block
|
||||
// 0b01 = HEAD -- head of a chain of blocks
|
||||
// 0b10 = TAIL -- in the tail of a chain of blocks
|
||||
// 0b11 = MARK -- marked head block
|
||||
|
||||
#define AT_FREE (0)
|
||||
#define AT_HEAD (1)
|
||||
#define AT_TAIL (2)
|
||||
#define AT_MARK (3)
|
||||
|
||||
#define BLOCKS_PER_ATB (4)
|
||||
#define ATB_MASK_0 (0x03)
|
||||
#define ATB_MASK_1 (0x0c)
|
||||
#define ATB_MASK_2 (0x30)
|
||||
#define ATB_MASK_3 (0xc0)
|
||||
|
||||
#define ATB_0_IS_FREE(a) (((a) & ATB_MASK_0) == 0)
|
||||
#define ATB_1_IS_FREE(a) (((a) & ATB_MASK_1) == 0)
|
||||
#define ATB_2_IS_FREE(a) (((a) & ATB_MASK_2) == 0)
|
||||
#define ATB_3_IS_FREE(a) (((a) & ATB_MASK_3) == 0)
|
||||
|
||||
#define BLOCK_SHIFT(block) (2 * ((block) & (BLOCKS_PER_ATB - 1)))
|
||||
#define ATB_GET_KIND(block) ((MP_STATE_MEM(gc_alloc_table_start)[(block) / BLOCKS_PER_ATB] >> BLOCK_SHIFT(block)) & 3)
|
||||
#define ATB_ANY_TO_FREE(block) do { MP_STATE_MEM(gc_alloc_table_start)[(block) / BLOCKS_PER_ATB] &= (~(AT_MARK << BLOCK_SHIFT(block))); } while (0)
|
||||
#define ATB_FREE_TO_HEAD(block) do { MP_STATE_MEM(gc_alloc_table_start)[(block) / BLOCKS_PER_ATB] |= (AT_HEAD << BLOCK_SHIFT(block)); } while (0)
|
||||
#define ATB_FREE_TO_TAIL(block) do { MP_STATE_MEM(gc_alloc_table_start)[(block) / BLOCKS_PER_ATB] |= (AT_TAIL << BLOCK_SHIFT(block)); } while (0)
|
||||
#define ATB_HEAD_TO_MARK(block) do { MP_STATE_MEM(gc_alloc_table_start)[(block) / BLOCKS_PER_ATB] |= (AT_MARK << BLOCK_SHIFT(block)); } while (0)
|
||||
#define ATB_MARK_TO_HEAD(block) do { MP_STATE_MEM(gc_alloc_table_start)[(block) / BLOCKS_PER_ATB] &= (~(AT_TAIL << BLOCK_SHIFT(block))); } while (0)
|
||||
|
||||
#define BLOCK_FROM_PTR(ptr) (((byte *)(ptr) - MP_STATE_MEM(gc_pool_start)) / BYTES_PER_BLOCK)
|
||||
#define PTR_FROM_BLOCK(block) (((block) * BYTES_PER_BLOCK + (uintptr_t)MP_STATE_MEM(gc_pool_start)))
|
||||
#define ATB_FROM_BLOCK(bl) ((bl) / BLOCKS_PER_ATB)
|
||||
|
||||
#if MICROPY_ENABLE_FINALISER
|
||||
// FTB = finaliser table byte
|
||||
// if set, then the corresponding block may have a finaliser
|
||||
|
||||
#define BLOCKS_PER_FTB (8)
|
||||
|
||||
#define FTB_GET(block) ((MP_STATE_MEM(gc_finaliser_table_start)[(block) / BLOCKS_PER_FTB] >> ((block) & 7)) & 1)
|
||||
#define FTB_SET(block) do { MP_STATE_MEM(gc_finaliser_table_start)[(block) / BLOCKS_PER_FTB] |= (1 << ((block) & 7)); } while (0)
|
||||
#define FTB_CLEAR(block) do { MP_STATE_MEM(gc_finaliser_table_start)[(block) / BLOCKS_PER_FTB] &= (~(1 << ((block) & 7))); } while (0)
|
||||
#endif
|
||||
|
||||
#if MICROPY_PY_THREAD && !MICROPY_PY_THREAD_GIL
|
||||
#define GC_ENTER() mp_thread_mutex_lock(&MP_STATE_MEM(gc_mutex), 1)
|
||||
#define GC_EXIT() mp_thread_mutex_unlock(&MP_STATE_MEM(gc_mutex))
|
||||
#else
|
||||
#define GC_ENTER()
|
||||
#define GC_EXIT()
|
||||
#endif
|
||||
|
||||
// TODO waste less memory; currently requires that all entries in alloc_table have a corresponding block in pool
|
||||
void gc_init(void *start, void *end) {
|
||||
// align end pointer on block boundary
|
||||
end = (void *)((uintptr_t)end & (~(BYTES_PER_BLOCK - 1)));
|
||||
DEBUG_printf("Initializing GC heap: %p..%p = " UINT_FMT " bytes\n", start, end, (byte *)end - (byte *)start);
|
||||
|
||||
// calculate parameters for GC (T=total, A=alloc table, F=finaliser table, P=pool; all in bytes):
|
||||
// T = A + F + P
|
||||
// F = A * BLOCKS_PER_ATB / BLOCKS_PER_FTB
|
||||
// P = A * BLOCKS_PER_ATB * BYTES_PER_BLOCK
|
||||
// => T = A * (1 + BLOCKS_PER_ATB / BLOCKS_PER_FTB + BLOCKS_PER_ATB * BYTES_PER_BLOCK)
|
||||
size_t total_byte_len = (byte *)end - (byte *)start;
|
||||
#if MICROPY_ENABLE_FINALISER
|
||||
MP_STATE_MEM(gc_alloc_table_byte_len) = total_byte_len * BITS_PER_BYTE / (BITS_PER_BYTE + BITS_PER_BYTE * BLOCKS_PER_ATB / BLOCKS_PER_FTB + BITS_PER_BYTE * BLOCKS_PER_ATB * BYTES_PER_BLOCK);
|
||||
#else
|
||||
MP_STATE_MEM(gc_alloc_table_byte_len) = total_byte_len / (1 + BITS_PER_BYTE / 2 * BYTES_PER_BLOCK);
|
||||
#endif
|
||||
|
||||
MP_STATE_MEM(gc_alloc_table_start) = (byte *)start;
|
||||
|
||||
#if MICROPY_ENABLE_FINALISER
|
||||
size_t gc_finaliser_table_byte_len = (MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB + BLOCKS_PER_FTB - 1) / BLOCKS_PER_FTB;
|
||||
MP_STATE_MEM(gc_finaliser_table_start) = MP_STATE_MEM(gc_alloc_table_start) + MP_STATE_MEM(gc_alloc_table_byte_len);
|
||||
#endif
|
||||
|
||||
size_t gc_pool_block_len = MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB;
|
||||
MP_STATE_MEM(gc_pool_start) = (byte *)end - gc_pool_block_len * BYTES_PER_BLOCK;
|
||||
MP_STATE_MEM(gc_pool_end) = end;
|
||||
|
||||
#if MICROPY_ENABLE_FINALISER
|
||||
assert(MP_STATE_MEM(gc_pool_start) >= MP_STATE_MEM(gc_finaliser_table_start) + gc_finaliser_table_byte_len);
|
||||
#endif
|
||||
|
||||
// clear ATBs
|
||||
memset(MP_STATE_MEM(gc_alloc_table_start), 0, MP_STATE_MEM(gc_alloc_table_byte_len));
|
||||
|
||||
#if MICROPY_ENABLE_FINALISER
|
||||
// clear FTBs
|
||||
memset(MP_STATE_MEM(gc_finaliser_table_start), 0, gc_finaliser_table_byte_len);
|
||||
#endif
|
||||
|
||||
// set last free ATB index to start of heap
|
||||
MP_STATE_MEM(gc_last_free_atb_index) = 0;
|
||||
|
||||
// unlock the GC
|
||||
MP_STATE_MEM(gc_lock_depth) = 0;
|
||||
|
||||
// allow auto collection
|
||||
MP_STATE_MEM(gc_auto_collect_enabled) = 1;
|
||||
|
||||
#if MICROPY_GC_ALLOC_THRESHOLD
|
||||
// by default, maxuint for gc threshold, effectively turning gc-by-threshold off
|
||||
MP_STATE_MEM(gc_alloc_threshold) = (size_t)-1;
|
||||
MP_STATE_MEM(gc_alloc_amount) = 0;
|
||||
#endif
|
||||
|
||||
#if MICROPY_PY_THREAD && !MICROPY_PY_THREAD_GIL
|
||||
mp_thread_mutex_init(&MP_STATE_MEM(gc_mutex));
|
||||
#endif
|
||||
|
||||
DEBUG_printf("GC layout:\n");
|
||||
DEBUG_printf(" alloc table at %p, length " UINT_FMT " bytes, " UINT_FMT " blocks\n", MP_STATE_MEM(gc_alloc_table_start), MP_STATE_MEM(gc_alloc_table_byte_len), MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB);
|
||||
#if MICROPY_ENABLE_FINALISER
|
||||
DEBUG_printf(" finaliser table at %p, length " UINT_FMT " bytes, " UINT_FMT " blocks\n", MP_STATE_MEM(gc_finaliser_table_start), gc_finaliser_table_byte_len, gc_finaliser_table_byte_len * BLOCKS_PER_FTB);
|
||||
#endif
|
||||
DEBUG_printf(" pool at %p, length " UINT_FMT " bytes, " UINT_FMT " blocks\n", MP_STATE_MEM(gc_pool_start), gc_pool_block_len * BYTES_PER_BLOCK, gc_pool_block_len);
|
||||
}
|
||||
|
||||
void gc_lock(void) {
|
||||
GC_ENTER();
|
||||
MP_STATE_MEM(gc_lock_depth)++;
|
||||
GC_EXIT();
|
||||
}
|
||||
|
||||
void gc_unlock(void) {
|
||||
GC_ENTER();
|
||||
MP_STATE_MEM(gc_lock_depth)--;
|
||||
GC_EXIT();
|
||||
}
|
||||
|
||||
bool gc_is_locked(void) {
|
||||
return MP_STATE_MEM(gc_lock_depth) != 0;
|
||||
}
|
||||
|
||||
// ptr should be of type void*
|
||||
#define VERIFY_PTR(ptr) ( \
|
||||
((uintptr_t)(ptr) & (BYTES_PER_BLOCK - 1)) == 0 /* must be aligned on a block */ \
|
||||
&& ptr >= (void *)MP_STATE_MEM(gc_pool_start) /* must be above start of pool */ \
|
||||
&& ptr < (void *)MP_STATE_MEM(gc_pool_end) /* must be below end of pool */ \
|
||||
)
|
||||
|
||||
#ifndef TRACE_MARK
|
||||
#if DEBUG_PRINT
|
||||
#define TRACE_MARK(block, ptr) DEBUG_printf("gc_mark(%p)\n", ptr)
|
||||
#else
|
||||
#define TRACE_MARK(block, ptr)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Take the given block as the topmost block on the stack. Check all it's
|
||||
// children: mark the unmarked child blocks and put those newly marked
|
||||
// blocks on the stack. When all children have been checked, pop off the
|
||||
// topmost block on the stack and repeat with that one.
|
||||
STATIC void gc_mark_subtree(size_t block) {
|
||||
// Start with the block passed in the argument.
|
||||
size_t sp = 0;
|
||||
for (;;) {
|
||||
// work out number of consecutive blocks in the chain starting with this one
|
||||
size_t n_blocks = 0;
|
||||
do {
|
||||
n_blocks += 1;
|
||||
} while (ATB_GET_KIND(block + n_blocks) == AT_TAIL);
|
||||
|
||||
// check this block's children
|
||||
void **ptrs = (void **)PTR_FROM_BLOCK(block);
|
||||
for (size_t i = n_blocks * BYTES_PER_BLOCK / sizeof(void *); i > 0; i--, ptrs++) {
|
||||
void *ptr = *ptrs;
|
||||
if (VERIFY_PTR(ptr)) {
|
||||
// Mark and push this pointer
|
||||
size_t childblock = BLOCK_FROM_PTR(ptr);
|
||||
if (ATB_GET_KIND(childblock) == AT_HEAD) {
|
||||
// an unmarked head, mark it, and push it on gc stack
|
||||
TRACE_MARK(childblock, ptr);
|
||||
ATB_HEAD_TO_MARK(childblock);
|
||||
if (sp < MICROPY_ALLOC_GC_STACK_SIZE) {
|
||||
MP_STATE_MEM(gc_stack)[sp++] = childblock;
|
||||
} else {
|
||||
MP_STATE_MEM(gc_stack_overflow) = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Are there any blocks on the stack?
|
||||
if (sp == 0) {
|
||||
break; // No, stack is empty, we're done.
|
||||
}
|
||||
|
||||
// pop the next block off the stack
|
||||
block = MP_STATE_MEM(gc_stack)[--sp];
|
||||
}
|
||||
}
|
||||
|
||||
STATIC void gc_deal_with_stack_overflow(void) {
|
||||
while (MP_STATE_MEM(gc_stack_overflow)) {
|
||||
MP_STATE_MEM(gc_stack_overflow) = 0;
|
||||
|
||||
// scan entire memory looking for blocks which have been marked but not their children
|
||||
for (size_t block = 0; block < MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB; block++) {
|
||||
// trace (again) if mark bit set
|
||||
if (ATB_GET_KIND(block) == AT_MARK) {
|
||||
gc_mark_subtree(block);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
STATIC void gc_sweep(void) {
|
||||
#if MICROPY_PY_GC_COLLECT_RETVAL
|
||||
MP_STATE_MEM(gc_collected) = 0;
|
||||
#endif
|
||||
// free unmarked heads and their tails
|
||||
int free_tail = 0;
|
||||
for (size_t block = 0; block < MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB; block++) {
|
||||
switch (ATB_GET_KIND(block)) {
|
||||
case AT_HEAD:
|
||||
#if MICROPY_ENABLE_FINALISER
|
||||
if (FTB_GET(block)) {
|
||||
mp_obj_base_t *obj = (mp_obj_base_t *)PTR_FROM_BLOCK(block);
|
||||
if (obj->type != NULL) {
|
||||
// if the object has a type then see if it has a __del__ method
|
||||
mp_obj_t dest[2];
|
||||
mp_load_method_maybe(MP_OBJ_FROM_PTR(obj), MP_QSTR___del__, dest);
|
||||
if (dest[0] != MP_OBJ_NULL) {
|
||||
// load_method returned a method, execute it in a protected environment
|
||||
#if MICROPY_ENABLE_SCHEDULER
|
||||
mp_sched_lock();
|
||||
#endif
|
||||
mp_call_function_1_protected(dest[0], dest[1]);
|
||||
#if MICROPY_ENABLE_SCHEDULER
|
||||
mp_sched_unlock();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
// clear finaliser flag
|
||||
FTB_CLEAR(block);
|
||||
}
|
||||
#endif
|
||||
free_tail = 1;
|
||||
DEBUG_printf("gc_sweep(%p)\n", PTR_FROM_BLOCK(block));
|
||||
#if MICROPY_PY_GC_COLLECT_RETVAL
|
||||
MP_STATE_MEM(gc_collected)++;
|
||||
#endif
|
||||
// fall through to free the head
|
||||
MP_FALLTHROUGH
|
||||
|
||||
case AT_TAIL:
|
||||
if (free_tail) {
|
||||
ATB_ANY_TO_FREE(block);
|
||||
#if CLEAR_ON_SWEEP
|
||||
memset((void *)PTR_FROM_BLOCK(block), 0, BYTES_PER_BLOCK);
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
|
||||
case AT_MARK:
|
||||
ATB_MARK_TO_HEAD(block);
|
||||
free_tail = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gc_collect_start(void) {
|
||||
GC_ENTER();
|
||||
MP_STATE_MEM(gc_lock_depth)++;
|
||||
#if MICROPY_GC_ALLOC_THRESHOLD
|
||||
MP_STATE_MEM(gc_alloc_amount) = 0;
|
||||
#endif
|
||||
MP_STATE_MEM(gc_stack_overflow) = 0;
|
||||
|
||||
// Trace root pointers. This relies on the root pointers being organised
|
||||
// correctly in the mp_state_ctx structure. We scan nlr_top, dict_locals,
|
||||
// dict_globals, then the root pointer section of mp_state_vm.
|
||||
void **ptrs = (void **)(void *)&mp_state_ctx;
|
||||
size_t root_start = offsetof(mp_state_ctx_t, thread.dict_locals);
|
||||
size_t root_end = offsetof(mp_state_ctx_t, vm.qstr_last_chunk);
|
||||
gc_collect_root(ptrs + root_start / sizeof(void *), (root_end - root_start) / sizeof(void *));
|
||||
|
||||
#if MICROPY_ENABLE_PYSTACK
|
||||
// Trace root pointers from the Python stack.
|
||||
ptrs = (void **)(void *)MP_STATE_THREAD(pystack_start);
|
||||
gc_collect_root(ptrs, (MP_STATE_THREAD(pystack_cur) - MP_STATE_THREAD(pystack_start)) / sizeof(void *));
|
||||
#endif
|
||||
}
|
||||
|
||||
void gc_collect_root(void **ptrs, size_t len) {
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
void *ptr = ptrs[i];
|
||||
if (VERIFY_PTR(ptr)) {
|
||||
size_t block = BLOCK_FROM_PTR(ptr);
|
||||
if (ATB_GET_KIND(block) == AT_HEAD) {
|
||||
// An unmarked head: mark it, and mark all its children
|
||||
TRACE_MARK(block, ptr);
|
||||
ATB_HEAD_TO_MARK(block);
|
||||
gc_mark_subtree(block);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gc_collect_end(void) {
|
||||
gc_deal_with_stack_overflow();
|
||||
gc_sweep();
|
||||
MP_STATE_MEM(gc_last_free_atb_index) = 0;
|
||||
MP_STATE_MEM(gc_lock_depth)--;
|
||||
GC_EXIT();
|
||||
}
|
||||
|
||||
void gc_sweep_all(void) {
|
||||
GC_ENTER();
|
||||
MP_STATE_MEM(gc_lock_depth)++;
|
||||
MP_STATE_MEM(gc_stack_overflow) = 0;
|
||||
gc_collect_end();
|
||||
}
|
||||
|
||||
void gc_info(gc_info_t *info) {
|
||||
GC_ENTER();
|
||||
info->total = MP_STATE_MEM(gc_pool_end) - MP_STATE_MEM(gc_pool_start);
|
||||
info->used = 0;
|
||||
info->free = 0;
|
||||
info->max_free = 0;
|
||||
info->num_1block = 0;
|
||||
info->num_2block = 0;
|
||||
info->max_block = 0;
|
||||
bool finish = false;
|
||||
for (size_t block = 0, len = 0, len_free = 0; !finish;) {
|
||||
size_t kind = ATB_GET_KIND(block);
|
||||
switch (kind) {
|
||||
case AT_FREE:
|
||||
info->free += 1;
|
||||
len_free += 1;
|
||||
len = 0;
|
||||
break;
|
||||
|
||||
case AT_HEAD:
|
||||
info->used += 1;
|
||||
len = 1;
|
||||
break;
|
||||
|
||||
case AT_TAIL:
|
||||
info->used += 1;
|
||||
len += 1;
|
||||
break;
|
||||
|
||||
case AT_MARK:
|
||||
// shouldn't happen
|
||||
break;
|
||||
}
|
||||
|
||||
block++;
|
||||
finish = (block == MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB);
|
||||
// Get next block type if possible
|
||||
if (!finish) {
|
||||
kind = ATB_GET_KIND(block);
|
||||
}
|
||||
|
||||
if (finish || kind == AT_FREE || kind == AT_HEAD) {
|
||||
if (len == 1) {
|
||||
info->num_1block += 1;
|
||||
} else if (len == 2) {
|
||||
info->num_2block += 1;
|
||||
}
|
||||
if (len > info->max_block) {
|
||||
info->max_block = len;
|
||||
}
|
||||
if (finish || kind == AT_HEAD) {
|
||||
if (len_free > info->max_free) {
|
||||
info->max_free = len_free;
|
||||
}
|
||||
len_free = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
info->used *= BYTES_PER_BLOCK;
|
||||
info->free *= BYTES_PER_BLOCK;
|
||||
GC_EXIT();
|
||||
}
|
||||
|
||||
void *gc_alloc(size_t n_bytes, unsigned int alloc_flags) {
|
||||
bool has_finaliser = alloc_flags & GC_ALLOC_FLAG_HAS_FINALISER;
|
||||
size_t n_blocks = ((n_bytes + BYTES_PER_BLOCK - 1) & (~(BYTES_PER_BLOCK - 1))) / BYTES_PER_BLOCK;
|
||||
DEBUG_printf("gc_alloc(" UINT_FMT " bytes -> " UINT_FMT " blocks)\n", n_bytes, n_blocks);
|
||||
|
||||
// check for 0 allocation
|
||||
if (n_blocks == 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
GC_ENTER();
|
||||
|
||||
// check if GC is locked
|
||||
if (MP_STATE_MEM(gc_lock_depth) > 0) {
|
||||
GC_EXIT();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
size_t i;
|
||||
size_t end_block;
|
||||
size_t start_block;
|
||||
size_t n_free;
|
||||
int collected = !MP_STATE_MEM(gc_auto_collect_enabled);
|
||||
|
||||
#if MICROPY_GC_ALLOC_THRESHOLD
|
||||
if (!collected && MP_STATE_MEM(gc_alloc_amount) >= MP_STATE_MEM(gc_alloc_threshold)) {
|
||||
GC_EXIT();
|
||||
gc_collect();
|
||||
collected = 1;
|
||||
GC_ENTER();
|
||||
}
|
||||
#endif
|
||||
|
||||
for (;;) {
|
||||
|
||||
// look for a run of n_blocks available blocks
|
||||
n_free = 0;
|
||||
for (i = MP_STATE_MEM(gc_last_free_atb_index); i < MP_STATE_MEM(gc_alloc_table_byte_len); i++) {
|
||||
byte a = MP_STATE_MEM(gc_alloc_table_start)[i];
|
||||
// *FORMAT-OFF*
|
||||
if (ATB_0_IS_FREE(a)) { if (++n_free >= n_blocks) { i = i * BLOCKS_PER_ATB + 0; goto found; } } else { n_free = 0; }
|
||||
if (ATB_1_IS_FREE(a)) { if (++n_free >= n_blocks) { i = i * BLOCKS_PER_ATB + 1; goto found; } } else { n_free = 0; }
|
||||
if (ATB_2_IS_FREE(a)) { if (++n_free >= n_blocks) { i = i * BLOCKS_PER_ATB + 2; goto found; } } else { n_free = 0; }
|
||||
if (ATB_3_IS_FREE(a)) { if (++n_free >= n_blocks) { i = i * BLOCKS_PER_ATB + 3; goto found; } } else { n_free = 0; }
|
||||
// *FORMAT-ON*
|
||||
}
|
||||
|
||||
GC_EXIT();
|
||||
// nothing found!
|
||||
if (collected) {
|
||||
return NULL;
|
||||
}
|
||||
DEBUG_printf("gc_alloc(" UINT_FMT "): no free mem, triggering GC\n", n_bytes);
|
||||
gc_collect();
|
||||
collected = 1;
|
||||
GC_ENTER();
|
||||
}
|
||||
|
||||
// found, ending at block i inclusive
|
||||
found:
|
||||
// get starting and end blocks, both inclusive
|
||||
end_block = i;
|
||||
start_block = i - n_free + 1;
|
||||
|
||||
// Set last free ATB index to block after last block we found, for start of
|
||||
// next scan. To reduce fragmentation, we only do this if we were looking
|
||||
// for a single free block, which guarantees that there are no free blocks
|
||||
// before this one. Also, whenever we free or shink a block we must check
|
||||
// if this index needs adjusting (see gc_realloc and gc_free).
|
||||
if (n_free == 1) {
|
||||
MP_STATE_MEM(gc_last_free_atb_index) = (i + 1) / BLOCKS_PER_ATB;
|
||||
}
|
||||
|
||||
// mark first block as used head
|
||||
ATB_FREE_TO_HEAD(start_block);
|
||||
|
||||
// mark rest of blocks as used tail
|
||||
// TODO for a run of many blocks can make this more efficient
|
||||
for (size_t bl = start_block + 1; bl <= end_block; bl++) {
|
||||
ATB_FREE_TO_TAIL(bl);
|
||||
}
|
||||
|
||||
// get pointer to first block
|
||||
// we must create this pointer before unlocking the GC so a collection can find it
|
||||
void *ret_ptr = (void *)(MP_STATE_MEM(gc_pool_start) + start_block * BYTES_PER_BLOCK);
|
||||
DEBUG_printf("gc_alloc(%p)\n", ret_ptr);
|
||||
|
||||
#if MICROPY_GC_ALLOC_THRESHOLD
|
||||
MP_STATE_MEM(gc_alloc_amount) += n_blocks;
|
||||
#endif
|
||||
|
||||
GC_EXIT();
|
||||
|
||||
#if MICROPY_GC_CONSERVATIVE_CLEAR
|
||||
// be conservative and zero out all the newly allocated blocks
|
||||
memset((byte *)ret_ptr, 0, (end_block - start_block + 1) * BYTES_PER_BLOCK);
|
||||
#else
|
||||
// zero out the additional bytes of the newly allocated blocks
|
||||
// This is needed because the blocks may have previously held pointers
|
||||
// to the heap and will not be set to something else if the caller
|
||||
// doesn't actually use the entire block. As such they will continue
|
||||
// to point to the heap and may prevent other blocks from being reclaimed.
|
||||
memset((byte *)ret_ptr + n_bytes, 0, (end_block - start_block + 1) * BYTES_PER_BLOCK - n_bytes);
|
||||
#endif
|
||||
|
||||
#if MICROPY_ENABLE_FINALISER
|
||||
if (has_finaliser) {
|
||||
// clear type pointer in case it is never set
|
||||
((mp_obj_base_t *)ret_ptr)->type = NULL;
|
||||
// set mp_obj flag only if it has a finaliser
|
||||
GC_ENTER();
|
||||
FTB_SET(start_block);
|
||||
GC_EXIT();
|
||||
}
|
||||
#else
|
||||
(void)has_finaliser;
|
||||
#endif
|
||||
|
||||
#if EXTENSIVE_HEAP_PROFILING
|
||||
gc_dump_alloc_table();
|
||||
#endif
|
||||
|
||||
return ret_ptr;
|
||||
}
|
||||
|
||||
/*
|
||||
void *gc_alloc(mp_uint_t n_bytes) {
|
||||
return _gc_alloc(n_bytes, false);
|
||||
}
|
||||
|
||||
void *gc_alloc_with_finaliser(mp_uint_t n_bytes) {
|
||||
return _gc_alloc(n_bytes, true);
|
||||
}
|
||||
*/
|
||||
|
||||
// force the freeing of a piece of memory
|
||||
// TODO: freeing here does not call finaliser
|
||||
void gc_free(void *ptr) {
|
||||
GC_ENTER();
|
||||
if (MP_STATE_MEM(gc_lock_depth) > 0) {
|
||||
// TODO how to deal with this error?
|
||||
GC_EXIT();
|
||||
return;
|
||||
}
|
||||
|
||||
DEBUG_printf("gc_free(%p)\n", ptr);
|
||||
|
||||
if (ptr == NULL) {
|
||||
GC_EXIT();
|
||||
} else {
|
||||
// get the GC block number corresponding to this pointer
|
||||
assert(VERIFY_PTR(ptr));
|
||||
size_t block = BLOCK_FROM_PTR(ptr);
|
||||
assert(ATB_GET_KIND(block) == AT_HEAD);
|
||||
|
||||
#if MICROPY_ENABLE_FINALISER
|
||||
FTB_CLEAR(block);
|
||||
#endif
|
||||
|
||||
// set the last_free pointer to this block if it's earlier in the heap
|
||||
if (block / BLOCKS_PER_ATB < MP_STATE_MEM(gc_last_free_atb_index)) {
|
||||
MP_STATE_MEM(gc_last_free_atb_index) = block / BLOCKS_PER_ATB;
|
||||
}
|
||||
|
||||
// free head and all of its tail blocks
|
||||
do {
|
||||
ATB_ANY_TO_FREE(block);
|
||||
block += 1;
|
||||
} while (ATB_GET_KIND(block) == AT_TAIL);
|
||||
|
||||
GC_EXIT();
|
||||
|
||||
#if EXTENSIVE_HEAP_PROFILING
|
||||
gc_dump_alloc_table();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
size_t gc_nbytes(const void *ptr) {
|
||||
GC_ENTER();
|
||||
if (VERIFY_PTR(ptr)) {
|
||||
size_t block = BLOCK_FROM_PTR(ptr);
|
||||
if (ATB_GET_KIND(block) == AT_HEAD) {
|
||||
// work out number of consecutive blocks in the chain starting with this on
|
||||
size_t n_blocks = 0;
|
||||
do {
|
||||
n_blocks += 1;
|
||||
} while (ATB_GET_KIND(block + n_blocks) == AT_TAIL);
|
||||
GC_EXIT();
|
||||
return n_blocks * BYTES_PER_BLOCK;
|
||||
}
|
||||
}
|
||||
|
||||
// invalid pointer
|
||||
GC_EXIT();
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if 0
|
||||
// old, simple realloc that didn't expand memory in place
|
||||
void *gc_realloc(void *ptr, mp_uint_t n_bytes) {
|
||||
mp_uint_t n_existing = gc_nbytes(ptr);
|
||||
if (n_bytes <= n_existing) {
|
||||
return ptr;
|
||||
} else {
|
||||
bool has_finaliser;
|
||||
if (ptr == NULL) {
|
||||
has_finaliser = false;
|
||||
} else {
|
||||
#if MICROPY_ENABLE_FINALISER
|
||||
has_finaliser = FTB_GET(BLOCK_FROM_PTR((mp_uint_t)ptr));
|
||||
#else
|
||||
has_finaliser = false;
|
||||
#endif
|
||||
}
|
||||
void *ptr2 = gc_alloc(n_bytes, has_finaliser);
|
||||
if (ptr2 == NULL) {
|
||||
return ptr2;
|
||||
}
|
||||
memcpy(ptr2, ptr, n_existing);
|
||||
gc_free(ptr);
|
||||
return ptr2;
|
||||
}
|
||||
}
|
||||
|
||||
#else // Alternative gc_realloc impl
|
||||
|
||||
void *gc_realloc(void *ptr_in, size_t n_bytes, bool allow_move) {
|
||||
// check for pure allocation
|
||||
if (ptr_in == NULL) {
|
||||
return gc_alloc(n_bytes, false);
|
||||
}
|
||||
|
||||
// check for pure free
|
||||
if (n_bytes == 0) {
|
||||
gc_free(ptr_in);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *ptr = ptr_in;
|
||||
|
||||
GC_ENTER();
|
||||
|
||||
if (MP_STATE_MEM(gc_lock_depth) > 0) {
|
||||
GC_EXIT();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// get the GC block number corresponding to this pointer
|
||||
assert(VERIFY_PTR(ptr));
|
||||
size_t block = BLOCK_FROM_PTR(ptr);
|
||||
assert(ATB_GET_KIND(block) == AT_HEAD);
|
||||
|
||||
// compute number of new blocks that are requested
|
||||
size_t new_blocks = (n_bytes + BYTES_PER_BLOCK - 1) / BYTES_PER_BLOCK;
|
||||
|
||||
// Get the total number of consecutive blocks that are already allocated to
|
||||
// this chunk of memory, and then count the number of free blocks following
|
||||
// it. Stop if we reach the end of the heap, or if we find enough extra
|
||||
// free blocks to satisfy the realloc. Note that we need to compute the
|
||||
// total size of the existing memory chunk so we can correctly and
|
||||
// efficiently shrink it (see below for shrinking code).
|
||||
size_t n_free = 0;
|
||||
size_t n_blocks = 1; // counting HEAD block
|
||||
size_t max_block = MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB;
|
||||
for (size_t bl = block + n_blocks; bl < max_block; bl++) {
|
||||
byte block_type = ATB_GET_KIND(bl);
|
||||
if (block_type == AT_TAIL) {
|
||||
n_blocks++;
|
||||
continue;
|
||||
}
|
||||
if (block_type == AT_FREE) {
|
||||
n_free++;
|
||||
if (n_blocks + n_free >= new_blocks) {
|
||||
// stop as soon as we find enough blocks for n_bytes
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// return original ptr if it already has the requested number of blocks
|
||||
if (new_blocks == n_blocks) {
|
||||
GC_EXIT();
|
||||
return ptr_in;
|
||||
}
|
||||
|
||||
// check if we can shrink the allocated area
|
||||
if (new_blocks < n_blocks) {
|
||||
// free unneeded tail blocks
|
||||
for (size_t bl = block + new_blocks, count = n_blocks - new_blocks; count > 0; bl++, count--) {
|
||||
ATB_ANY_TO_FREE(bl);
|
||||
}
|
||||
|
||||
// set the last_free pointer to end of this block if it's earlier in the heap
|
||||
if ((block + new_blocks) / BLOCKS_PER_ATB < MP_STATE_MEM(gc_last_free_atb_index)) {
|
||||
MP_STATE_MEM(gc_last_free_atb_index) = (block + new_blocks) / BLOCKS_PER_ATB;
|
||||
}
|
||||
|
||||
GC_EXIT();
|
||||
|
||||
#if EXTENSIVE_HEAP_PROFILING
|
||||
gc_dump_alloc_table();
|
||||
#endif
|
||||
|
||||
return ptr_in;
|
||||
}
|
||||
|
||||
// check if we can expand in place
|
||||
if (new_blocks <= n_blocks + n_free) {
|
||||
// mark few more blocks as used tail
|
||||
for (size_t bl = block + n_blocks; bl < block + new_blocks; bl++) {
|
||||
assert(ATB_GET_KIND(bl) == AT_FREE);
|
||||
ATB_FREE_TO_TAIL(bl);
|
||||
}
|
||||
|
||||
GC_EXIT();
|
||||
|
||||
#if MICROPY_GC_CONSERVATIVE_CLEAR
|
||||
// be conservative and zero out all the newly allocated blocks
|
||||
memset((byte *)ptr_in + n_blocks * BYTES_PER_BLOCK, 0, (new_blocks - n_blocks) * BYTES_PER_BLOCK);
|
||||
#else
|
||||
// zero out the additional bytes of the newly allocated blocks (see comment above in gc_alloc)
|
||||
memset((byte *)ptr_in + n_bytes, 0, new_blocks * BYTES_PER_BLOCK - n_bytes);
|
||||
#endif
|
||||
|
||||
#if EXTENSIVE_HEAP_PROFILING
|
||||
gc_dump_alloc_table();
|
||||
#endif
|
||||
|
||||
return ptr_in;
|
||||
}
|
||||
|
||||
#if MICROPY_ENABLE_FINALISER
|
||||
bool ftb_state = FTB_GET(block);
|
||||
#else
|
||||
bool ftb_state = false;
|
||||
#endif
|
||||
|
||||
GC_EXIT();
|
||||
|
||||
if (!allow_move) {
|
||||
// not allowed to move memory block so return failure
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// can't resize inplace; try to find a new contiguous chain
|
||||
void *ptr_out = gc_alloc(n_bytes, ftb_state);
|
||||
|
||||
// check that the alloc succeeded
|
||||
if (ptr_out == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
DEBUG_printf("gc_realloc(%p -> %p)\n", ptr_in, ptr_out);
|
||||
memcpy(ptr_out, ptr_in, n_blocks * BYTES_PER_BLOCK);
|
||||
gc_free(ptr_in);
|
||||
return ptr_out;
|
||||
}
|
||||
#endif // Alternative gc_realloc impl
|
||||
|
||||
void gc_dump_info(void) {
|
||||
gc_info_t info;
|
||||
gc_info(&info);
|
||||
mp_printf(&mp_plat_print, "GC: total: %u, used: %u, free: %u\n",
|
||||
(uint)info.total, (uint)info.used, (uint)info.free);
|
||||
mp_printf(&mp_plat_print, " No. of 1-blocks: %u, 2-blocks: %u, max blk sz: %u, max free sz: %u\n",
|
||||
(uint)info.num_1block, (uint)info.num_2block, (uint)info.max_block, (uint)info.max_free);
|
||||
}
|
||||
|
||||
void gc_dump_alloc_table(void) {
|
||||
GC_ENTER();
|
||||
static const size_t DUMP_BYTES_PER_LINE = 64;
|
||||
#if !EXTENSIVE_HEAP_PROFILING
|
||||
// When comparing heap output we don't want to print the starting
|
||||
// pointer of the heap because it changes from run to run.
|
||||
mp_printf(&mp_plat_print, "GC memory layout; from %p:", MP_STATE_MEM(gc_pool_start));
|
||||
#endif
|
||||
for (size_t bl = 0; bl < MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB; bl++) {
|
||||
if (bl % DUMP_BYTES_PER_LINE == 0) {
|
||||
// a new line of blocks
|
||||
{
|
||||
// check if this line contains only free blocks
|
||||
size_t bl2 = bl;
|
||||
while (bl2 < MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB && ATB_GET_KIND(bl2) == AT_FREE) {
|
||||
bl2++;
|
||||
}
|
||||
if (bl2 - bl >= 2 * DUMP_BYTES_PER_LINE) {
|
||||
// there are at least 2 lines containing only free blocks, so abbreviate their printing
|
||||
mp_printf(&mp_plat_print, "\n (%u lines all free)", (uint)(bl2 - bl) / DUMP_BYTES_PER_LINE);
|
||||
bl = bl2 & (~(DUMP_BYTES_PER_LINE - 1));
|
||||
if (bl >= MP_STATE_MEM(gc_alloc_table_byte_len) * BLOCKS_PER_ATB) {
|
||||
// got to end of heap
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// print header for new line of blocks
|
||||
// (the cast to uint32_t is for 16-bit ports)
|
||||
// mp_printf(&mp_plat_print, "\n%05x: ", (uint)(PTR_FROM_BLOCK(bl) & (uint32_t)0xfffff));
|
||||
mp_printf(&mp_plat_print, "\n%05x: ", (uint)((bl * BYTES_PER_BLOCK) & (uint32_t)0xfffff));
|
||||
}
|
||||
int c = ' ';
|
||||
switch (ATB_GET_KIND(bl)) {
|
||||
case AT_FREE:
|
||||
c = '.';
|
||||
break;
|
||||
/* this prints out if the object is reachable from BSS or STACK (for unix only)
|
||||
case AT_HEAD: {
|
||||
c = 'h';
|
||||
void **ptrs = (void**)(void*)&mp_state_ctx;
|
||||
mp_uint_t len = offsetof(mp_state_ctx_t, vm.stack_top) / sizeof(mp_uint_t);
|
||||
for (mp_uint_t i = 0; i < len; i++) {
|
||||
mp_uint_t ptr = (mp_uint_t)ptrs[i];
|
||||
if (VERIFY_PTR(ptr) && BLOCK_FROM_PTR(ptr) == bl) {
|
||||
c = 'B';
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (c == 'h') {
|
||||
ptrs = (void**)&c;
|
||||
len = ((mp_uint_t)MP_STATE_THREAD(stack_top) - (mp_uint_t)&c) / sizeof(mp_uint_t);
|
||||
for (mp_uint_t i = 0; i < len; i++) {
|
||||
mp_uint_t ptr = (mp_uint_t)ptrs[i];
|
||||
if (VERIFY_PTR(ptr) && BLOCK_FROM_PTR(ptr) == bl) {
|
||||
c = 'S';
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
*/
|
||||
/* this prints the uPy object type of the head block */
|
||||
case AT_HEAD: {
|
||||
void **ptr = (void **)(MP_STATE_MEM(gc_pool_start) + bl * BYTES_PER_BLOCK);
|
||||
if (*ptr == &mp_type_tuple) {
|
||||
c = 'T';
|
||||
} else if (*ptr == &mp_type_list) {
|
||||
c = 'L';
|
||||
} else if (*ptr == &mp_type_dict) {
|
||||
c = 'D';
|
||||
} else if (*ptr == &mp_type_str || *ptr == &mp_type_bytes) {
|
||||
c = 'S';
|
||||
}
|
||||
#if MICROPY_PY_BUILTINS_BYTEARRAY
|
||||
else if (*ptr == &mp_type_bytearray) {
|
||||
c = 'A';
|
||||
}
|
||||
#endif
|
||||
#if MICROPY_PY_ARRAY
|
||||
else if (*ptr == &mp_type_array) {
|
||||
c = 'A';
|
||||
}
|
||||
#endif
|
||||
#if MICROPY_PY_BUILTINS_FLOAT
|
||||
else if (*ptr == &mp_type_float) {
|
||||
c = 'F';
|
||||
}
|
||||
#endif
|
||||
else if (*ptr == &mp_type_fun_bc) {
|
||||
c = 'B';
|
||||
} else if (*ptr == &mp_type_module) {
|
||||
c = 'M';
|
||||
} else {
|
||||
c = 'h';
|
||||
#if 0
|
||||
// This code prints "Q" for qstr-pool data, and "q" for qstr-str
|
||||
// data. It can be useful to see how qstrs are being allocated,
|
||||
// but is disabled by default because it is very slow.
|
||||
for (qstr_pool_t *pool = MP_STATE_VM(last_pool); c == 'h' && pool != NULL; pool = pool->prev) {
|
||||
if ((qstr_pool_t *)ptr == pool) {
|
||||
c = 'Q';
|
||||
break;
|
||||
}
|
||||
for (const byte **q = pool->qstrs, **q_top = pool->qstrs + pool->len; q < q_top; q++) {
|
||||
if ((const byte *)ptr == *q) {
|
||||
c = 'q';
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AT_TAIL:
|
||||
c = '=';
|
||||
break;
|
||||
case AT_MARK:
|
||||
c = 'm';
|
||||
break;
|
||||
}
|
||||
mp_printf(&mp_plat_print, "%c", c);
|
||||
}
|
||||
mp_print_str(&mp_plat_print, "\n");
|
||||
GC_EXIT();
|
||||
}
|
||||
|
||||
#if 0
|
||||
// For testing the GC functions
|
||||
void gc_test(void) {
|
||||
mp_uint_t len = 500;
|
||||
mp_uint_t *heap = malloc(len);
|
||||
gc_init(heap, heap + len / sizeof(mp_uint_t));
|
||||
void *ptrs[100];
|
||||
{
|
||||
mp_uint_t **p = gc_alloc(16, false);
|
||||
p[0] = gc_alloc(64, false);
|
||||
p[1] = gc_alloc(1, false);
|
||||
p[2] = gc_alloc(1, false);
|
||||
p[3] = gc_alloc(1, false);
|
||||
mp_uint_t ***p2 = gc_alloc(16, false);
|
||||
p2[0] = p;
|
||||
p2[1] = p;
|
||||
ptrs[0] = p2;
|
||||
}
|
||||
for (int i = 0; i < 25; i += 2) {
|
||||
mp_uint_t *p = gc_alloc(i, false);
|
||||
printf("p=%p\n", p);
|
||||
if (i & 3) {
|
||||
// ptrs[i] = p;
|
||||
}
|
||||
}
|
||||
|
||||
printf("Before GC:\n");
|
||||
gc_dump_alloc_table();
|
||||
printf("Starting GC...\n");
|
||||
gc_collect_start();
|
||||
gc_collect_root(ptrs, sizeof(ptrs) / sizeof(void *));
|
||||
gc_collect_end();
|
||||
printf("After GC:\n");
|
||||
gc_dump_alloc_table();
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // MICROPY_ENABLE_GC
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013, 2014 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef MICROPY_INCLUDED_PY_GC_H
|
||||
#define MICROPY_INCLUDED_PY_GC_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "py/mpconfig.h"
|
||||
#include "py/misc.h"
|
||||
|
||||
void gc_init(void *start, void *end);
|
||||
|
||||
// These lock/unlock functions can be nested.
|
||||
// They can be used to prevent the GC from allocating/freeing.
|
||||
void gc_lock(void);
|
||||
void gc_unlock(void);
|
||||
bool gc_is_locked(void);
|
||||
|
||||
// A given port must implement gc_collect by using the other collect functions.
|
||||
void gc_collect(void);
|
||||
void gc_collect_start(void);
|
||||
void gc_collect_root(void **ptrs, size_t len);
|
||||
void gc_collect_end(void);
|
||||
|
||||
// Use this function to sweep the whole heap and run all finalisers
|
||||
void gc_sweep_all(void);
|
||||
|
||||
enum {
|
||||
GC_ALLOC_FLAG_HAS_FINALISER = 1,
|
||||
};
|
||||
|
||||
void *gc_alloc(size_t n_bytes, unsigned int alloc_flags);
|
||||
void gc_free(void *ptr); // does not call finaliser
|
||||
size_t gc_nbytes(const void *ptr);
|
||||
void *gc_realloc(void *ptr, size_t n_bytes, bool allow_move);
|
||||
|
||||
typedef struct _gc_info_t {
|
||||
size_t total;
|
||||
size_t used;
|
||||
size_t free;
|
||||
size_t max_free;
|
||||
size_t num_1block;
|
||||
size_t num_2block;
|
||||
size_t max_block;
|
||||
} gc_info_t;
|
||||
|
||||
void gc_info(gc_info_t *info);
|
||||
void gc_dump_info(void);
|
||||
void gc_dump_alloc_table(void);
|
||||
|
||||
#endif // MICROPY_INCLUDED_PY_GC_H
|
||||
@@ -0,0 +1,372 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013-2020 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
// *FORMAT-OFF*
|
||||
|
||||
// rules for writing rules:
|
||||
// - zero_or_more is implemented using opt_rule around a one_or_more rule
|
||||
// - don't put opt_rule in arguments of or rule; instead, wrap the call to this or rule in opt_rule
|
||||
|
||||
// Generic sub-rules used by multiple rules below.
|
||||
|
||||
DEF_RULE_NC(generic_colon_test, and_ident(2), tok(DEL_COLON), rule(test))
|
||||
DEF_RULE_NC(generic_equal_test, and_ident(2), tok(DEL_EQUAL), rule(test))
|
||||
|
||||
// # Start symbols for the grammar:
|
||||
// # single_input is a single interactive statement;
|
||||
// # file_input is a module or sequence of commands read from an input file;
|
||||
// # eval_input is the input for the eval() functions.
|
||||
// # NB: compound_stmt in single_input is followed by extra NEWLINE! --> not in MicroPython
|
||||
// single_input: NEWLINE | simple_stmt | compound_stmt
|
||||
// file_input: (NEWLINE | stmt)* ENDMARKER
|
||||
// eval_input: testlist NEWLINE* ENDMARKER
|
||||
|
||||
DEF_RULE_NC(single_input, or(3), tok(NEWLINE), rule(simple_stmt), rule(compound_stmt))
|
||||
DEF_RULE(file_input, c(generic_all_nodes), and_ident(1), opt_rule(file_input_2))
|
||||
DEF_RULE(file_input_2, c(generic_all_nodes), one_or_more, rule(file_input_3))
|
||||
DEF_RULE_NC(file_input_3, or(2), tok(NEWLINE), rule(stmt))
|
||||
DEF_RULE_NC(eval_input, and_ident(2), rule(testlist), opt_rule(eval_input_2))
|
||||
DEF_RULE_NC(eval_input_2, and(1), tok(NEWLINE))
|
||||
|
||||
// decorator: '@' dotted_name [ '(' [arglist] ')' ] NEWLINE
|
||||
// decorators: decorator+
|
||||
// decorated: decorators (classdef | funcdef | async_funcdef)
|
||||
// funcdef: 'def' NAME parameters ['->' test] ':' suite
|
||||
// async_funcdef: 'async' funcdef
|
||||
// parameters: '(' [typedargslist] ')'
|
||||
// typedargslist: tfpdef ['=' test] (',' tfpdef ['=' test])* [',' ['*' [tfpdef] (',' tfpdef ['=' test])* [',' '**' tfpdef] | '**' tfpdef]] | '*' [tfpdef] (',' tfpdef ['=' test])* [',' '**' tfpdef] | '**' tfpdef
|
||||
// tfpdef: NAME [':' test]
|
||||
// varargslist: vfpdef ['=' test] (',' vfpdef ['=' test])* [',' ['*' [vfpdef] (',' vfpdef ['=' test])* [',' '**' vfpdef] | '**' vfpdef]] | '*' [vfpdef] (',' vfpdef ['=' test])* [',' '**' vfpdef] | '**' vfpdef
|
||||
// vfpdef: NAME
|
||||
|
||||
DEF_RULE_NC(decorator, and(4), tok(OP_AT), rule(dotted_name), opt_rule(trailer_paren), tok(NEWLINE))
|
||||
DEF_RULE_NC(decorators, one_or_more, rule(decorator))
|
||||
DEF_RULE(decorated, c(decorated), and_ident(2), rule(decorators), rule(decorated_body))
|
||||
#if MICROPY_PY_ASYNC_AWAIT
|
||||
DEF_RULE_NC(decorated_body, or(3), rule(classdef), rule(funcdef), rule(async_funcdef))
|
||||
DEF_RULE_NC(async_funcdef, and(2), tok(KW_ASYNC), rule(funcdef))
|
||||
#else
|
||||
DEF_RULE_NC(decorated_body, or(2), rule(classdef), rule(funcdef))
|
||||
#endif
|
||||
DEF_RULE(funcdef, c(funcdef), and_blank(8), tok(KW_DEF), tok(NAME), tok(DEL_PAREN_OPEN), opt_rule(typedargslist), tok(DEL_PAREN_CLOSE), opt_rule(funcdefrettype), tok(DEL_COLON), rule(suite))
|
||||
DEF_RULE_NC(funcdefrettype, and_ident(2), tok(DEL_MINUS_MORE), rule(test))
|
||||
// note: typedargslist lets through more than is allowed, compiler does further checks
|
||||
DEF_RULE_NC(typedargslist, list_with_end, rule(typedargslist_item), tok(DEL_COMMA))
|
||||
DEF_RULE_NC(typedargslist_item, or(3), rule(typedargslist_name), rule(typedargslist_star), rule(typedargslist_dbl_star))
|
||||
DEF_RULE_NC(typedargslist_name, and_ident(3), tok(NAME), opt_rule(generic_colon_test), opt_rule(generic_equal_test))
|
||||
DEF_RULE_NC(typedargslist_star, and(2), tok(OP_STAR), opt_rule(tfpdef))
|
||||
DEF_RULE_NC(typedargslist_dbl_star, and(3), tok(OP_DBL_STAR), tok(NAME), opt_rule(generic_colon_test))
|
||||
DEF_RULE_NC(tfpdef, and(2), tok(NAME), opt_rule(generic_colon_test))
|
||||
// note: varargslist lets through more than is allowed, compiler does further checks
|
||||
DEF_RULE_NC(varargslist, list_with_end, rule(varargslist_item), tok(DEL_COMMA))
|
||||
DEF_RULE_NC(varargslist_item, or(3), rule(varargslist_name), rule(varargslist_star), rule(varargslist_dbl_star))
|
||||
DEF_RULE_NC(varargslist_name, and_ident(2), tok(NAME), opt_rule(generic_equal_test))
|
||||
DEF_RULE_NC(varargslist_star, and(2), tok(OP_STAR), opt_rule(vfpdef))
|
||||
DEF_RULE_NC(varargslist_dbl_star, and(2), tok(OP_DBL_STAR), tok(NAME))
|
||||
DEF_RULE_NC(vfpdef, and_ident(1), tok(NAME))
|
||||
|
||||
// stmt: compound_stmt | simple_stmt
|
||||
|
||||
DEF_RULE_NC(stmt, or(2), rule(compound_stmt), rule(simple_stmt))
|
||||
|
||||
// simple_stmt: small_stmt (';' small_stmt)* [';'] NEWLINE
|
||||
|
||||
DEF_RULE_NC(simple_stmt, and_ident(2), rule(simple_stmt_2), tok(NEWLINE))
|
||||
DEF_RULE(simple_stmt_2, c(generic_all_nodes), list_with_end, rule(small_stmt), tok(DEL_SEMICOLON))
|
||||
|
||||
// small_stmt: expr_stmt | del_stmt | pass_stmt | flow_stmt | import_stmt | global_stmt | nonlocal_stmt | assert_stmt
|
||||
// expr_stmt: testlist_star_expr (annassign | augassign (yield_expr|testlist) | ('=' (yield_expr|testlist_star_expr))*)
|
||||
// testlist_star_expr: (test|star_expr) (',' (test|star_expr))* [',']
|
||||
// annassign: ':' test ['=' (yield_expr|testlist_star_expr)]
|
||||
// augassign: '+=' | '-=' | '*=' | '@=' | '/=' | '%=' | '&=' | '|=' | '^=' | '<<=' | '>>=' | '**=' | '//='
|
||||
// # For normal and annotated assignments, additional restrictions enforced by the interpreter
|
||||
|
||||
DEF_RULE_NC(small_stmt, or(8), rule(del_stmt), rule(pass_stmt), rule(flow_stmt), rule(import_stmt), rule(global_stmt), rule(nonlocal_stmt), rule(assert_stmt), rule(expr_stmt))
|
||||
DEF_RULE(expr_stmt, c(expr_stmt), and(2), rule(testlist_star_expr), opt_rule(expr_stmt_2))
|
||||
DEF_RULE_NC(expr_stmt_2, or(3), rule(annassign), rule(expr_stmt_augassign), rule(expr_stmt_assign_list))
|
||||
DEF_RULE_NC(expr_stmt_augassign, and_ident(2), rule(augassign), rule(expr_stmt_6))
|
||||
DEF_RULE_NC(expr_stmt_assign_list, one_or_more, rule(expr_stmt_assign))
|
||||
DEF_RULE_NC(expr_stmt_assign, and_ident(2), tok(DEL_EQUAL), rule(expr_stmt_6))
|
||||
DEF_RULE_NC(expr_stmt_6, or(2), rule(yield_expr), rule(testlist_star_expr))
|
||||
DEF_RULE(testlist_star_expr, c(generic_tuple), list_with_end, rule(testlist_star_expr_2), tok(DEL_COMMA))
|
||||
DEF_RULE_NC(testlist_star_expr_2, or(2), rule(star_expr), rule(test))
|
||||
DEF_RULE_NC(annassign, and(3), tok(DEL_COLON), rule(test), opt_rule(expr_stmt_assign))
|
||||
DEF_RULE_NC(augassign, or(13), tok(DEL_PLUS_EQUAL), tok(DEL_MINUS_EQUAL), tok(DEL_STAR_EQUAL), tok(DEL_AT_EQUAL), tok(DEL_SLASH_EQUAL), tok(DEL_PERCENT_EQUAL), tok(DEL_AMPERSAND_EQUAL), tok(DEL_PIPE_EQUAL), tok(DEL_CARET_EQUAL), tok(DEL_DBL_LESS_EQUAL), tok(DEL_DBL_MORE_EQUAL), tok(DEL_DBL_STAR_EQUAL), tok(DEL_DBL_SLASH_EQUAL))
|
||||
|
||||
// del_stmt: 'del' exprlist
|
||||
// pass_stmt: 'pass'
|
||||
// flow_stmt: break_stmt | continue_stmt | return_stmt | raise_stmt | yield_stmt
|
||||
// break_stmt: 'break'
|
||||
// continue_stmt: 'continue'
|
||||
// return_stmt: 'return' [testlist]
|
||||
// yield_stmt: yield_expr
|
||||
// raise_stmt: 'raise' [test ['from' test]]
|
||||
|
||||
DEF_RULE(del_stmt, c(del_stmt), and(2), tok(KW_DEL), rule(exprlist))
|
||||
DEF_RULE(pass_stmt, c(generic_all_nodes), and(1), tok(KW_PASS))
|
||||
DEF_RULE_NC(flow_stmt, or(5), rule(break_stmt), rule(continue_stmt), rule(return_stmt), rule(raise_stmt), rule(yield_stmt))
|
||||
DEF_RULE(break_stmt, c(break_cont_stmt), and(1), tok(KW_BREAK))
|
||||
DEF_RULE(continue_stmt, c(break_cont_stmt), and(1), tok(KW_CONTINUE))
|
||||
DEF_RULE(return_stmt, c(return_stmt), and(2), tok(KW_RETURN), opt_rule(testlist))
|
||||
DEF_RULE(yield_stmt, c(yield_stmt), and(1), rule(yield_expr))
|
||||
DEF_RULE(raise_stmt, c(raise_stmt), and(2), tok(KW_RAISE), opt_rule(raise_stmt_arg))
|
||||
DEF_RULE_NC(raise_stmt_arg, and_ident(2), rule(test), opt_rule(raise_stmt_from))
|
||||
DEF_RULE_NC(raise_stmt_from, and_ident(2), tok(KW_FROM), rule(test))
|
||||
|
||||
// import_stmt: import_name | import_from
|
||||
// import_name: 'import' dotted_as_names
|
||||
// import_from: 'from' (('.' | '...')* dotted_name | ('.' | '...')+) 'import' ('*' | '(' import_as_names ')' | import_as_names)
|
||||
// import_as_name: NAME ['as' NAME]
|
||||
// dotted_as_name: dotted_name ['as' NAME]
|
||||
// import_as_names: import_as_name (',' import_as_name)* [',']
|
||||
// dotted_as_names: dotted_as_name (',' dotted_as_name)*
|
||||
// dotted_name: NAME ('.' NAME)*
|
||||
// global_stmt: 'global' NAME (',' NAME)*
|
||||
// nonlocal_stmt: 'nonlocal' NAME (',' NAME)*
|
||||
// assert_stmt: 'assert' test [',' test]
|
||||
|
||||
DEF_RULE_NC(import_stmt, or(2), rule(import_name), rule(import_from))
|
||||
DEF_RULE(import_name, c(import_name), and(2), tok(KW_IMPORT), rule(dotted_as_names))
|
||||
DEF_RULE(import_from, c(import_from), and(4), tok(KW_FROM), rule(import_from_2), tok(KW_IMPORT), rule(import_from_3))
|
||||
DEF_RULE_NC(import_from_2, or(2), rule(dotted_name), rule(import_from_2b))
|
||||
DEF_RULE_NC(import_from_2b, and_ident(2), rule(one_or_more_period_or_ellipsis), opt_rule(dotted_name))
|
||||
DEF_RULE_NC(import_from_3, or(3), tok(OP_STAR), rule(import_as_names_paren), rule(import_as_names))
|
||||
DEF_RULE_NC(import_as_names_paren, and_ident(3), tok(DEL_PAREN_OPEN), rule(import_as_names), tok(DEL_PAREN_CLOSE))
|
||||
DEF_RULE_NC(one_or_more_period_or_ellipsis, one_or_more, rule(period_or_ellipsis))
|
||||
DEF_RULE_NC(period_or_ellipsis, or(2), tok(DEL_PERIOD), tok(ELLIPSIS))
|
||||
DEF_RULE_NC(import_as_name, and(2), tok(NAME), opt_rule(as_name))
|
||||
DEF_RULE_NC(dotted_as_name, and_ident(2), rule(dotted_name), opt_rule(as_name))
|
||||
DEF_RULE_NC(as_name, and_ident(2), tok(KW_AS), tok(NAME))
|
||||
DEF_RULE_NC(import_as_names, list_with_end, rule(import_as_name), tok(DEL_COMMA))
|
||||
DEF_RULE_NC(dotted_as_names, list, rule(dotted_as_name), tok(DEL_COMMA))
|
||||
DEF_RULE_NC(dotted_name, list, tok(NAME), tok(DEL_PERIOD))
|
||||
DEF_RULE(global_stmt, c(global_nonlocal_stmt), and(2), tok(KW_GLOBAL), rule(name_list))
|
||||
DEF_RULE(nonlocal_stmt, c(global_nonlocal_stmt), and(2), tok(KW_NONLOCAL), rule(name_list))
|
||||
DEF_RULE_NC(name_list, list, tok(NAME), tok(DEL_COMMA))
|
||||
DEF_RULE(assert_stmt, c(assert_stmt), and(3), tok(KW_ASSERT), rule(test), opt_rule(assert_stmt_extra))
|
||||
DEF_RULE_NC(assert_stmt_extra, and_ident(2), tok(DEL_COMMA), rule(test))
|
||||
|
||||
// compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef | decorated | async_stmt
|
||||
// if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ['else' ':' suite]
|
||||
// while_stmt: 'while' test ':' suite ['else' ':' suite]
|
||||
// for_stmt: 'for' exprlist 'in' testlist ':' suite ['else' ':' suite]
|
||||
// try_stmt: 'try' ':' suite ((except_clause ':' suite)+ ['else' ':' suite] ['finally' ':' suite] | 'finally' ':' suite)
|
||||
// # NB compile.c makes sure that the default except clause is last
|
||||
// except_clause: 'except' [test ['as' NAME]]
|
||||
// with_stmt: 'with' with_item (',' with_item)* ':' suite
|
||||
// with_item: test ['as' expr]
|
||||
// suite: simple_stmt | NEWLINE INDENT stmt+ DEDENT
|
||||
// async_stmt: 'async' (funcdef | with_stmt | for_stmt)
|
||||
|
||||
#if MICROPY_PY_ASYNC_AWAIT
|
||||
DEF_RULE_NC(compound_stmt, or(9), rule(if_stmt), rule(while_stmt), rule(for_stmt), rule(try_stmt), rule(with_stmt), rule(funcdef), rule(classdef), rule(decorated), rule(async_stmt))
|
||||
DEF_RULE(async_stmt, c(async_stmt), and(2), tok(KW_ASYNC), rule(async_stmt_2))
|
||||
DEF_RULE_NC(async_stmt_2, or(3), rule(funcdef), rule(with_stmt), rule(for_stmt))
|
||||
#else
|
||||
DEF_RULE_NC(compound_stmt, or(8), rule(if_stmt), rule(while_stmt), rule(for_stmt), rule(try_stmt), rule(with_stmt), rule(funcdef), rule(classdef), rule(decorated))
|
||||
#endif
|
||||
DEF_RULE(if_stmt, c(if_stmt), and(6), tok(KW_IF), rule(namedexpr_test), tok(DEL_COLON), rule(suite), opt_rule(if_stmt_elif_list), opt_rule(else_stmt))
|
||||
DEF_RULE_NC(if_stmt_elif_list, one_or_more, rule(if_stmt_elif))
|
||||
DEF_RULE_NC(if_stmt_elif, and(4), tok(KW_ELIF), rule(namedexpr_test), tok(DEL_COLON), rule(suite))
|
||||
DEF_RULE(while_stmt, c(while_stmt), and(5), tok(KW_WHILE), rule(namedexpr_test), tok(DEL_COLON), rule(suite), opt_rule(else_stmt))
|
||||
DEF_RULE(for_stmt, c(for_stmt), and(7), tok(KW_FOR), rule(exprlist), tok(KW_IN), rule(testlist), tok(DEL_COLON), rule(suite), opt_rule(else_stmt))
|
||||
DEF_RULE(try_stmt, c(try_stmt), and(4), tok(KW_TRY), tok(DEL_COLON), rule(suite), rule(try_stmt_2))
|
||||
DEF_RULE_NC(try_stmt_2, or(2), rule(try_stmt_except_and_more), rule(try_stmt_finally))
|
||||
DEF_RULE_NC(try_stmt_except_and_more, and_ident(3), rule(try_stmt_except_list), opt_rule(else_stmt), opt_rule(try_stmt_finally))
|
||||
DEF_RULE_NC(try_stmt_except, and(4), tok(KW_EXCEPT), opt_rule(try_stmt_as_name), tok(DEL_COLON), rule(suite))
|
||||
DEF_RULE_NC(try_stmt_as_name, and_ident(2), rule(test), opt_rule(as_name))
|
||||
DEF_RULE_NC(try_stmt_except_list, one_or_more, rule(try_stmt_except))
|
||||
DEF_RULE_NC(try_stmt_finally, and(3), tok(KW_FINALLY), tok(DEL_COLON), rule(suite))
|
||||
DEF_RULE_NC(else_stmt, and_ident(3), tok(KW_ELSE), tok(DEL_COLON), rule(suite))
|
||||
DEF_RULE(with_stmt, c(with_stmt), and(4), tok(KW_WITH), rule(with_stmt_list), tok(DEL_COLON), rule(suite))
|
||||
DEF_RULE_NC(with_stmt_list, list, rule(with_item), tok(DEL_COMMA))
|
||||
DEF_RULE_NC(with_item, and_ident(2), rule(test), opt_rule(with_item_as))
|
||||
DEF_RULE_NC(with_item_as, and_ident(2), tok(KW_AS), rule(expr))
|
||||
DEF_RULE_NC(suite, or(2), rule(suite_block), rule(simple_stmt))
|
||||
DEF_RULE_NC(suite_block, and_ident(4), tok(NEWLINE), tok(INDENT), rule(suite_block_stmts), tok(DEDENT))
|
||||
DEF_RULE(suite_block_stmts, c(generic_all_nodes), one_or_more, rule(stmt))
|
||||
|
||||
// test: or_test ['if' or_test 'else' test] | lambdef
|
||||
// test_nocond: or_test | lambdef_nocond
|
||||
// lambdef: 'lambda' [varargslist] ':' test
|
||||
// lambdef_nocond: 'lambda' [varargslist] ':' test_nocond
|
||||
|
||||
#if MICROPY_PY_ASSIGN_EXPR
|
||||
DEF_RULE(namedexpr_test, c(namedexpr), and_ident(2), rule(test), opt_rule(namedexpr_test_2))
|
||||
DEF_RULE_NC(namedexpr_test_2, and_ident(2), tok(OP_ASSIGN), rule(test))
|
||||
#else
|
||||
DEF_RULE_NC(namedexpr_test, or(1), rule(test))
|
||||
#endif
|
||||
DEF_RULE_NC(test, or(2), rule(lambdef), rule(test_if_expr))
|
||||
DEF_RULE(test_if_expr, c(test_if_expr), and_ident(2), rule(or_test), opt_rule(test_if_else))
|
||||
DEF_RULE_NC(test_if_else, and(4), tok(KW_IF), rule(or_test), tok(KW_ELSE), rule(test))
|
||||
DEF_RULE_NC(test_nocond, or(2), rule(lambdef_nocond), rule(or_test))
|
||||
DEF_RULE(lambdef, c(lambdef), and_blank(4), tok(KW_LAMBDA), opt_rule(varargslist), tok(DEL_COLON), rule(test))
|
||||
DEF_RULE(lambdef_nocond, c(lambdef), and_blank(4), tok(KW_LAMBDA), opt_rule(varargslist), tok(DEL_COLON), rule(test_nocond))
|
||||
|
||||
// or_test: and_test ('or' and_test)*
|
||||
// and_test: not_test ('and' not_test)*
|
||||
// not_test: 'not' not_test | comparison
|
||||
// comparison: expr (comp_op expr)*
|
||||
// comp_op: '<'|'>'|'=='|'>='|'<='|'!='|'in'|'not' 'in'|'is'|'is' 'not'
|
||||
// star_expr: '*' expr
|
||||
// expr: xor_expr ('|' xor_expr)*
|
||||
// xor_expr: and_expr ('^' and_expr)*
|
||||
// and_expr: shift_expr ('&' shift_expr)*
|
||||
// shift_expr: arith_expr (('<<'|'>>') arith_expr)*
|
||||
// arith_expr: term (('+'|'-') term)*
|
||||
// term: factor (('*'|'@'|'/'|'%'|'//') factor)*
|
||||
// factor: ('+'|'-'|'~') factor | power
|
||||
// power: atom_expr ['**' factor]
|
||||
// atom_expr: 'await' atom trailer* | atom trailer*
|
||||
|
||||
DEF_RULE(or_test, c(or_and_test), list, rule(and_test), tok(KW_OR))
|
||||
DEF_RULE(and_test, c(or_and_test), list, rule(not_test), tok(KW_AND))
|
||||
DEF_RULE_NC(not_test, or(2), rule(not_test_2), rule(comparison))
|
||||
DEF_RULE(not_test_2, c(not_test_2), and(2), tok(KW_NOT), rule(not_test))
|
||||
DEF_RULE(comparison, c(comparison), list, rule(expr), rule(comp_op))
|
||||
DEF_RULE_NC(comp_op, or(9), tok(OP_LESS), tok(OP_MORE), tok(OP_DBL_EQUAL), tok(OP_LESS_EQUAL), tok(OP_MORE_EQUAL), tok(OP_NOT_EQUAL), tok(KW_IN), rule(comp_op_not_in), rule(comp_op_is))
|
||||
DEF_RULE_NC(comp_op_not_in, and(2), tok(KW_NOT), tok(KW_IN))
|
||||
DEF_RULE_NC(comp_op_is, and(2), tok(KW_IS), opt_rule(comp_op_is_not))
|
||||
DEF_RULE_NC(comp_op_is_not, and(1), tok(KW_NOT))
|
||||
DEF_RULE(star_expr, c(star_expr), and(2), tok(OP_STAR), rule(expr))
|
||||
DEF_RULE(expr, c(binary_op), list, rule(xor_expr), tok(OP_PIPE))
|
||||
DEF_RULE(xor_expr, c(binary_op), list, rule(and_expr), tok(OP_CARET))
|
||||
DEF_RULE(and_expr, c(binary_op), list, rule(shift_expr), tok(OP_AMPERSAND))
|
||||
DEF_RULE(shift_expr, c(term), list, rule(arith_expr), rule(shift_op))
|
||||
DEF_RULE_NC(shift_op, or(2), tok(OP_DBL_LESS), tok(OP_DBL_MORE))
|
||||
DEF_RULE(arith_expr, c(term), list, rule(term), rule(arith_op))
|
||||
DEF_RULE_NC(arith_op, or(2), tok(OP_PLUS), tok(OP_MINUS))
|
||||
DEF_RULE(term, c(term), list, rule(factor), rule(term_op))
|
||||
DEF_RULE_NC(term_op, or(5), tok(OP_STAR), tok(OP_AT), tok(OP_SLASH), tok(OP_PERCENT), tok(OP_DBL_SLASH))
|
||||
DEF_RULE_NC(factor, or(2), rule(factor_2), rule(power))
|
||||
DEF_RULE(factor_2, c(factor_2), and_ident(2), rule(factor_op), rule(factor))
|
||||
DEF_RULE_NC(factor_op, or(3), tok(OP_PLUS), tok(OP_MINUS), tok(OP_TILDE))
|
||||
DEF_RULE(power, c(power), and_ident(2), rule(atom_expr), opt_rule(power_dbl_star))
|
||||
#if MICROPY_PY_ASYNC_AWAIT
|
||||
DEF_RULE_NC(atom_expr, or(2), rule(atom_expr_await), rule(atom_expr_normal))
|
||||
DEF_RULE(atom_expr_await, c(atom_expr_await), and(3), tok(KW_AWAIT), rule(atom), opt_rule(atom_expr_trailers))
|
||||
#else
|
||||
DEF_RULE_NC(atom_expr, or(1), rule(atom_expr_normal))
|
||||
#endif
|
||||
DEF_RULE(atom_expr_normal, c(atom_expr_normal), and_ident(2), rule(atom), opt_rule(atom_expr_trailers))
|
||||
DEF_RULE_NC(atom_expr_trailers, one_or_more, rule(trailer))
|
||||
DEF_RULE_NC(power_dbl_star, and_ident(2), tok(OP_DBL_STAR), rule(factor))
|
||||
|
||||
// atom: '(' [yield_expr|testlist_comp] ')' | '[' [testlist_comp] ']' | '{' [dictorsetmaker] '}' | NAME | NUMBER | STRING+ | '...' | 'None' | 'True' | 'False'
|
||||
// testlist_comp: (test|star_expr) ( comp_for | (',' (test|star_expr))* [','] )
|
||||
// trailer: '(' [arglist] ')' | '[' subscriptlist ']' | '.' NAME
|
||||
|
||||
DEF_RULE_NC(atom, or(12), tok(NAME), tok(INTEGER), tok(FLOAT_OR_IMAG), tok(STRING), tok(BYTES), tok(ELLIPSIS), tok(KW_NONE), tok(KW_TRUE), tok(KW_FALSE), rule(atom_paren), rule(atom_bracket), rule(atom_brace))
|
||||
DEF_RULE(atom_paren, c(atom_paren), and(3), tok(DEL_PAREN_OPEN), opt_rule(atom_2b), tok(DEL_PAREN_CLOSE))
|
||||
DEF_RULE_NC(atom_2b, or(2), rule(yield_expr), rule(testlist_comp))
|
||||
DEF_RULE(atom_bracket, c(atom_bracket), and(3), tok(DEL_BRACKET_OPEN), opt_rule(testlist_comp), tok(DEL_BRACKET_CLOSE))
|
||||
DEF_RULE(atom_brace, c(atom_brace), and(3), tok(DEL_BRACE_OPEN), opt_rule(dictorsetmaker), tok(DEL_BRACE_CLOSE))
|
||||
DEF_RULE_NC(testlist_comp, and_ident(2), rule(testlist_comp_2), opt_rule(testlist_comp_3))
|
||||
DEF_RULE_NC(testlist_comp_2, or(2), rule(star_expr), rule(namedexpr_test))
|
||||
DEF_RULE_NC(testlist_comp_3, or(2), rule(comp_for), rule(testlist_comp_3b))
|
||||
DEF_RULE_NC(testlist_comp_3b, and_ident(2), tok(DEL_COMMA), opt_rule(testlist_comp_3c))
|
||||
DEF_RULE_NC(testlist_comp_3c, list_with_end, rule(testlist_comp_2), tok(DEL_COMMA))
|
||||
DEF_RULE_NC(trailer, or(3), rule(trailer_paren), rule(trailer_bracket), rule(trailer_period))
|
||||
DEF_RULE(trailer_paren, c(trailer_paren), and(3), tok(DEL_PAREN_OPEN), opt_rule(arglist), tok(DEL_PAREN_CLOSE))
|
||||
DEF_RULE(trailer_bracket, c(trailer_bracket), and(3), tok(DEL_BRACKET_OPEN), rule(subscriptlist), tok(DEL_BRACKET_CLOSE))
|
||||
DEF_RULE(trailer_period, c(trailer_period), and(2), tok(DEL_PERIOD), tok(NAME))
|
||||
|
||||
// subscriptlist: subscript (',' subscript)* [',']
|
||||
// subscript: test | [test] ':' [test] [sliceop]
|
||||
// sliceop: ':' [test]
|
||||
|
||||
#if MICROPY_PY_BUILTINS_SLICE
|
||||
DEF_RULE(subscriptlist, c(generic_tuple), list_with_end, rule(subscript), tok(DEL_COMMA))
|
||||
DEF_RULE_NC(subscript, or(2), rule(subscript_3), rule(subscript_2))
|
||||
DEF_RULE(subscript_2, c(subscript), and_ident(2), rule(test), opt_rule(subscript_3))
|
||||
DEF_RULE(subscript_3, c(subscript), and(2), tok(DEL_COLON), opt_rule(subscript_3b))
|
||||
DEF_RULE_NC(subscript_3b, or(2), rule(subscript_3c), rule(subscript_3d))
|
||||
DEF_RULE_NC(subscript_3c, and(2), tok(DEL_COLON), opt_rule(test))
|
||||
DEF_RULE_NC(subscript_3d, and_ident(2), rule(test), opt_rule(sliceop))
|
||||
DEF_RULE_NC(sliceop, and(2), tok(DEL_COLON), opt_rule(test))
|
||||
#else
|
||||
DEF_RULE(subscriptlist, c(generic_tuple), list_with_end, rule(test), tok(DEL_COMMA))
|
||||
#endif
|
||||
|
||||
// exprlist: (expr|star_expr) (',' (expr|star_expr))* [',']
|
||||
// testlist: test (',' test)* [',']
|
||||
// dictorsetmaker: (test ':' test (comp_for | (',' test ':' test)* [','])) | (test (comp_for | (',' test)* [',']))
|
||||
|
||||
DEF_RULE_NC(exprlist, list_with_end, rule(exprlist_2), tok(DEL_COMMA))
|
||||
DEF_RULE_NC(exprlist_2, or(2), rule(star_expr), rule(expr))
|
||||
DEF_RULE(testlist, c(generic_tuple), list_with_end, rule(test), tok(DEL_COMMA))
|
||||
// TODO dictorsetmaker lets through more than is allowed
|
||||
DEF_RULE_NC(dictorsetmaker, and_ident(2), rule(dictorsetmaker_item), opt_rule(dictorsetmaker_tail))
|
||||
#if MICROPY_PY_BUILTINS_SET
|
||||
DEF_RULE(dictorsetmaker_item, c(dictorsetmaker_item), and_ident(2), rule(test), opt_rule(generic_colon_test))
|
||||
#else
|
||||
DEF_RULE(dictorsetmaker_item, c(dictorsetmaker_item), and(3), rule(test), tok(DEL_COLON), rule(test))
|
||||
#endif
|
||||
DEF_RULE_NC(dictorsetmaker_tail, or(2), rule(comp_for), rule(dictorsetmaker_list))
|
||||
DEF_RULE_NC(dictorsetmaker_list, and(2), tok(DEL_COMMA), opt_rule(dictorsetmaker_list2))
|
||||
DEF_RULE_NC(dictorsetmaker_list2, list_with_end, rule(dictorsetmaker_item), tok(DEL_COMMA))
|
||||
|
||||
// classdef: 'class' NAME ['(' [arglist] ')'] ':' suite
|
||||
|
||||
DEF_RULE(classdef, c(classdef), and_blank(5), tok(KW_CLASS), tok(NAME), opt_rule(classdef_2), tok(DEL_COLON), rule(suite))
|
||||
DEF_RULE_NC(classdef_2, and_ident(3), tok(DEL_PAREN_OPEN), opt_rule(arglist), tok(DEL_PAREN_CLOSE))
|
||||
|
||||
// arglist: (argument ',')* (argument [','] | '*' test (',' argument)* [',' '**' test] | '**' test)
|
||||
|
||||
// TODO arglist lets through more than is allowed, compiler needs to do further verification
|
||||
DEF_RULE_NC(arglist, list_with_end, rule(arglist_2), tok(DEL_COMMA))
|
||||
DEF_RULE_NC(arglist_2, or(3), rule(arglist_star), rule(arglist_dbl_star), rule(argument))
|
||||
DEF_RULE_NC(arglist_star, and(2), tok(OP_STAR), rule(test))
|
||||
DEF_RULE_NC(arglist_dbl_star, and(2), tok(OP_DBL_STAR), rule(test))
|
||||
|
||||
// # The reason that keywords are test nodes instead of NAME is that using NAME
|
||||
// # results in an ambiguity. ast.c makes sure it's a NAME.
|
||||
// argument: test [comp_for] | test '=' test # Really [keyword '='] test
|
||||
// comp_iter: comp_for | comp_if
|
||||
// comp_for: 'for' exprlist 'in' or_test [comp_iter]
|
||||
// comp_if: 'if' test_nocond [comp_iter]
|
||||
|
||||
DEF_RULE_NC(argument, and_ident(2), rule(test), opt_rule(argument_2))
|
||||
#if MICROPY_PY_ASSIGN_EXPR
|
||||
DEF_RULE_NC(argument_2, or(3), rule(comp_for), rule(generic_equal_test), rule(argument_3))
|
||||
DEF_RULE_NC(argument_3, and(2), tok(OP_ASSIGN), rule(test))
|
||||
#else
|
||||
DEF_RULE_NC(argument_2, or(2), rule(comp_for), rule(generic_equal_test))
|
||||
#endif
|
||||
DEF_RULE_NC(comp_iter, or(2), rule(comp_for), rule(comp_if))
|
||||
DEF_RULE_NC(comp_for, and_blank(5), tok(KW_FOR), rule(exprlist), tok(KW_IN), rule(or_test), opt_rule(comp_iter))
|
||||
DEF_RULE_NC(comp_if, and(3), tok(KW_IF), rule(test_nocond), opt_rule(comp_iter))
|
||||
|
||||
// # not used in grammar, but may appear in "node" passed from Parser to Compiler
|
||||
// encoding_decl: NAME
|
||||
|
||||
// yield_expr: 'yield' [yield_arg]
|
||||
// yield_arg: 'from' test | testlist
|
||||
|
||||
DEF_RULE(yield_expr, c(yield_expr), and(2), tok(KW_YIELD), opt_rule(yield_arg))
|
||||
DEF_RULE_NC(yield_arg, or(2), rule(yield_arg_from), rule(testlist))
|
||||
DEF_RULE_NC(yield_arg_from, and(2), tok(KW_FROM), rule(test))
|
||||
@@ -0,0 +1,787 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013, 2014 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "py/reader.h"
|
||||
#include "py/lexer.h"
|
||||
#include "py/runtime.h"
|
||||
|
||||
#if MICROPY_ENABLE_COMPILER
|
||||
|
||||
#define TAB_SIZE (8)
|
||||
|
||||
// TODO seems that CPython allows NULL byte in the input stream
|
||||
// don't know if that's intentional or not, but we don't allow it
|
||||
|
||||
#define MP_LEXER_EOF ((unichar)MP_READER_EOF)
|
||||
#define CUR_CHAR(lex) ((lex)->chr0)
|
||||
|
||||
STATIC bool is_end(mp_lexer_t *lex) {
|
||||
return lex->chr0 == MP_LEXER_EOF;
|
||||
}
|
||||
|
||||
STATIC bool is_physical_newline(mp_lexer_t *lex) {
|
||||
return lex->chr0 == '\n';
|
||||
}
|
||||
|
||||
STATIC bool is_char(mp_lexer_t *lex, byte c) {
|
||||
return lex->chr0 == c;
|
||||
}
|
||||
|
||||
STATIC bool is_char_or(mp_lexer_t *lex, byte c1, byte c2) {
|
||||
return lex->chr0 == c1 || lex->chr0 == c2;
|
||||
}
|
||||
|
||||
STATIC bool is_char_or3(mp_lexer_t *lex, byte c1, byte c2, byte c3) {
|
||||
return lex->chr0 == c1 || lex->chr0 == c2 || lex->chr0 == c3;
|
||||
}
|
||||
|
||||
STATIC bool is_char_following(mp_lexer_t *lex, byte c) {
|
||||
return lex->chr1 == c;
|
||||
}
|
||||
|
||||
STATIC bool is_char_following_or(mp_lexer_t *lex, byte c1, byte c2) {
|
||||
return lex->chr1 == c1 || lex->chr1 == c2;
|
||||
}
|
||||
|
||||
STATIC bool is_char_following_following_or(mp_lexer_t *lex, byte c1, byte c2) {
|
||||
return lex->chr2 == c1 || lex->chr2 == c2;
|
||||
}
|
||||
|
||||
STATIC bool is_char_and(mp_lexer_t *lex, byte c1, byte c2) {
|
||||
return lex->chr0 == c1 && lex->chr1 == c2;
|
||||
}
|
||||
|
||||
STATIC bool is_whitespace(mp_lexer_t *lex) {
|
||||
return unichar_isspace(lex->chr0);
|
||||
}
|
||||
|
||||
STATIC bool is_letter(mp_lexer_t *lex) {
|
||||
return unichar_isalpha(lex->chr0);
|
||||
}
|
||||
|
||||
STATIC bool is_digit(mp_lexer_t *lex) {
|
||||
return unichar_isdigit(lex->chr0);
|
||||
}
|
||||
|
||||
STATIC bool is_following_digit(mp_lexer_t *lex) {
|
||||
return unichar_isdigit(lex->chr1);
|
||||
}
|
||||
|
||||
STATIC bool is_following_base_char(mp_lexer_t *lex) {
|
||||
const unichar chr1 = lex->chr1 | 0x20;
|
||||
return chr1 == 'b' || chr1 == 'o' || chr1 == 'x';
|
||||
}
|
||||
|
||||
STATIC bool is_following_odigit(mp_lexer_t *lex) {
|
||||
return lex->chr1 >= '0' && lex->chr1 <= '7';
|
||||
}
|
||||
|
||||
STATIC bool is_string_or_bytes(mp_lexer_t *lex) {
|
||||
return is_char_or(lex, '\'', '\"')
|
||||
|| (is_char_or3(lex, 'r', 'u', 'b') && is_char_following_or(lex, '\'', '\"'))
|
||||
|| ((is_char_and(lex, 'r', 'b') || is_char_and(lex, 'b', 'r'))
|
||||
&& is_char_following_following_or(lex, '\'', '\"'));
|
||||
}
|
||||
|
||||
// to easily parse utf-8 identifiers we allow any raw byte with high bit set
|
||||
STATIC bool is_head_of_identifier(mp_lexer_t *lex) {
|
||||
return is_letter(lex) || lex->chr0 == '_' || lex->chr0 >= 0x80;
|
||||
}
|
||||
|
||||
STATIC bool is_tail_of_identifier(mp_lexer_t *lex) {
|
||||
return is_head_of_identifier(lex) || is_digit(lex);
|
||||
}
|
||||
|
||||
STATIC void next_char(mp_lexer_t *lex) {
|
||||
if (lex->chr0 == '\n') {
|
||||
// a new line
|
||||
++lex->line;
|
||||
lex->column = 1;
|
||||
} else if (lex->chr0 == '\t') {
|
||||
// a tab
|
||||
lex->column = (((lex->column - 1 + TAB_SIZE) / TAB_SIZE) * TAB_SIZE) + 1;
|
||||
} else {
|
||||
// a character worth one column
|
||||
++lex->column;
|
||||
}
|
||||
|
||||
lex->chr0 = lex->chr1;
|
||||
lex->chr1 = lex->chr2;
|
||||
lex->chr2 = lex->reader.readbyte(lex->reader.data);
|
||||
|
||||
if (lex->chr1 == '\r') {
|
||||
// CR is a new line, converted to LF
|
||||
lex->chr1 = '\n';
|
||||
if (lex->chr2 == '\n') {
|
||||
// CR LF is a single new line, throw out the extra LF
|
||||
lex->chr2 = lex->reader.readbyte(lex->reader.data);
|
||||
}
|
||||
}
|
||||
|
||||
// check if we need to insert a newline at end of file
|
||||
if (lex->chr2 == MP_LEXER_EOF && lex->chr1 != MP_LEXER_EOF && lex->chr1 != '\n') {
|
||||
lex->chr2 = '\n';
|
||||
}
|
||||
}
|
||||
|
||||
STATIC void indent_push(mp_lexer_t *lex, size_t indent) {
|
||||
if (lex->num_indent_level >= lex->alloc_indent_level) {
|
||||
lex->indent_level = m_renew(uint16_t, lex->indent_level, lex->alloc_indent_level, lex->alloc_indent_level + MICROPY_ALLOC_LEXEL_INDENT_INC);
|
||||
lex->alloc_indent_level += MICROPY_ALLOC_LEXEL_INDENT_INC;
|
||||
}
|
||||
lex->indent_level[lex->num_indent_level++] = indent;
|
||||
}
|
||||
|
||||
STATIC size_t indent_top(mp_lexer_t *lex) {
|
||||
return lex->indent_level[lex->num_indent_level - 1];
|
||||
}
|
||||
|
||||
STATIC void indent_pop(mp_lexer_t *lex) {
|
||||
lex->num_indent_level -= 1;
|
||||
}
|
||||
|
||||
// some tricky operator encoding:
|
||||
// <op> = begin with <op>, if this opchar matches then begin here
|
||||
// e<op> = end with <op>, if this opchar matches then end
|
||||
// c<op> = continue with <op>, if this opchar matches then continue matching
|
||||
// this means if the start of two ops are the same then they are equal til the last char
|
||||
|
||||
STATIC const char *const tok_enc =
|
||||
"()[]{},;~" // singles
|
||||
":e=" // : :=
|
||||
"<e=c<e=" // < <= << <<=
|
||||
">e=c>e=" // > >= >> >>=
|
||||
"*e=c*e=" // * *= ** **=
|
||||
"+e=" // + +=
|
||||
"-e=e>" // - -= ->
|
||||
"&e=" // & &=
|
||||
"|e=" // | |=
|
||||
"/e=c/e=" // / /= // //=
|
||||
"%e=" // % %=
|
||||
"^e=" // ^ ^=
|
||||
"@e=" // @ @=
|
||||
"=e=" // = ==
|
||||
"!."; // start of special cases: != . ...
|
||||
|
||||
// TODO static assert that number of tokens is less than 256 so we can safely make this table with byte sized entries
|
||||
STATIC const uint8_t tok_enc_kind[] = {
|
||||
MP_TOKEN_DEL_PAREN_OPEN, MP_TOKEN_DEL_PAREN_CLOSE,
|
||||
MP_TOKEN_DEL_BRACKET_OPEN, MP_TOKEN_DEL_BRACKET_CLOSE,
|
||||
MP_TOKEN_DEL_BRACE_OPEN, MP_TOKEN_DEL_BRACE_CLOSE,
|
||||
MP_TOKEN_DEL_COMMA, MP_TOKEN_DEL_SEMICOLON, MP_TOKEN_OP_TILDE,
|
||||
|
||||
MP_TOKEN_DEL_COLON, MP_TOKEN_OP_ASSIGN,
|
||||
MP_TOKEN_OP_LESS, MP_TOKEN_OP_LESS_EQUAL, MP_TOKEN_OP_DBL_LESS, MP_TOKEN_DEL_DBL_LESS_EQUAL,
|
||||
MP_TOKEN_OP_MORE, MP_TOKEN_OP_MORE_EQUAL, MP_TOKEN_OP_DBL_MORE, MP_TOKEN_DEL_DBL_MORE_EQUAL,
|
||||
MP_TOKEN_OP_STAR, MP_TOKEN_DEL_STAR_EQUAL, MP_TOKEN_OP_DBL_STAR, MP_TOKEN_DEL_DBL_STAR_EQUAL,
|
||||
MP_TOKEN_OP_PLUS, MP_TOKEN_DEL_PLUS_EQUAL,
|
||||
MP_TOKEN_OP_MINUS, MP_TOKEN_DEL_MINUS_EQUAL, MP_TOKEN_DEL_MINUS_MORE,
|
||||
MP_TOKEN_OP_AMPERSAND, MP_TOKEN_DEL_AMPERSAND_EQUAL,
|
||||
MP_TOKEN_OP_PIPE, MP_TOKEN_DEL_PIPE_EQUAL,
|
||||
MP_TOKEN_OP_SLASH, MP_TOKEN_DEL_SLASH_EQUAL, MP_TOKEN_OP_DBL_SLASH, MP_TOKEN_DEL_DBL_SLASH_EQUAL,
|
||||
MP_TOKEN_OP_PERCENT, MP_TOKEN_DEL_PERCENT_EQUAL,
|
||||
MP_TOKEN_OP_CARET, MP_TOKEN_DEL_CARET_EQUAL,
|
||||
MP_TOKEN_OP_AT, MP_TOKEN_DEL_AT_EQUAL,
|
||||
MP_TOKEN_DEL_EQUAL, MP_TOKEN_OP_DBL_EQUAL,
|
||||
};
|
||||
|
||||
// must have the same order as enum in lexer.h
|
||||
// must be sorted according to strcmp
|
||||
STATIC const char *const tok_kw[] = {
|
||||
"False",
|
||||
"None",
|
||||
"True",
|
||||
"__debug__",
|
||||
"and",
|
||||
"as",
|
||||
"assert",
|
||||
#if MICROPY_PY_ASYNC_AWAIT
|
||||
"async",
|
||||
"await",
|
||||
#endif
|
||||
"break",
|
||||
"class",
|
||||
"continue",
|
||||
"def",
|
||||
"del",
|
||||
"elif",
|
||||
"else",
|
||||
"except",
|
||||
"finally",
|
||||
"for",
|
||||
"from",
|
||||
"global",
|
||||
"if",
|
||||
"import",
|
||||
"in",
|
||||
"is",
|
||||
"lambda",
|
||||
"nonlocal",
|
||||
"not",
|
||||
"or",
|
||||
"pass",
|
||||
"raise",
|
||||
"return",
|
||||
"try",
|
||||
"while",
|
||||
"with",
|
||||
"yield",
|
||||
};
|
||||
|
||||
// This is called with CUR_CHAR() before first hex digit, and should return with
|
||||
// it pointing to last hex digit
|
||||
// num_digits must be greater than zero
|
||||
STATIC bool get_hex(mp_lexer_t *lex, size_t num_digits, mp_uint_t *result) {
|
||||
mp_uint_t num = 0;
|
||||
while (num_digits-- != 0) {
|
||||
next_char(lex);
|
||||
unichar c = CUR_CHAR(lex);
|
||||
if (!unichar_isxdigit(c)) {
|
||||
return false;
|
||||
}
|
||||
num = (num << 4) + unichar_xdigit_value(c);
|
||||
}
|
||||
*result = num;
|
||||
return true;
|
||||
}
|
||||
|
||||
STATIC void parse_string_literal(mp_lexer_t *lex, bool is_raw) {
|
||||
// get first quoting character
|
||||
char quote_char = '\'';
|
||||
if (is_char(lex, '\"')) {
|
||||
quote_char = '\"';
|
||||
}
|
||||
next_char(lex);
|
||||
|
||||
// work out if it's a single or triple quoted literal
|
||||
size_t num_quotes;
|
||||
if (is_char_and(lex, quote_char, quote_char)) {
|
||||
// triple quotes
|
||||
next_char(lex);
|
||||
next_char(lex);
|
||||
num_quotes = 3;
|
||||
} else {
|
||||
// single quotes
|
||||
num_quotes = 1;
|
||||
}
|
||||
|
||||
size_t n_closing = 0;
|
||||
while (!is_end(lex) && (num_quotes > 1 || !is_char(lex, '\n')) && n_closing < num_quotes) {
|
||||
if (is_char(lex, quote_char)) {
|
||||
n_closing += 1;
|
||||
vstr_add_char(&lex->vstr, CUR_CHAR(lex));
|
||||
} else {
|
||||
n_closing = 0;
|
||||
if (is_char(lex, '\\')) {
|
||||
next_char(lex);
|
||||
unichar c = CUR_CHAR(lex);
|
||||
if (is_raw) {
|
||||
// raw strings allow escaping of quotes, but the backslash is also emitted
|
||||
vstr_add_char(&lex->vstr, '\\');
|
||||
} else {
|
||||
switch (c) {
|
||||
// note: "c" can never be MP_LEXER_EOF because next_char
|
||||
// always inserts a newline at the end of the input stream
|
||||
case '\n':
|
||||
c = MP_LEXER_EOF;
|
||||
break; // backslash escape the newline, just ignore it
|
||||
case '\\':
|
||||
break;
|
||||
case '\'':
|
||||
break;
|
||||
case '"':
|
||||
break;
|
||||
case 'a':
|
||||
c = 0x07;
|
||||
break;
|
||||
case 'b':
|
||||
c = 0x08;
|
||||
break;
|
||||
case 't':
|
||||
c = 0x09;
|
||||
break;
|
||||
case 'n':
|
||||
c = 0x0a;
|
||||
break;
|
||||
case 'v':
|
||||
c = 0x0b;
|
||||
break;
|
||||
case 'f':
|
||||
c = 0x0c;
|
||||
break;
|
||||
case 'r':
|
||||
c = 0x0d;
|
||||
break;
|
||||
case 'u':
|
||||
case 'U':
|
||||
if (lex->tok_kind == MP_TOKEN_BYTES) {
|
||||
// b'\u1234' == b'\\u1234'
|
||||
vstr_add_char(&lex->vstr, '\\');
|
||||
break;
|
||||
}
|
||||
// Otherwise fall through.
|
||||
MP_FALLTHROUGH
|
||||
case 'x': {
|
||||
mp_uint_t num = 0;
|
||||
if (!get_hex(lex, (c == 'x' ? 2 : c == 'u' ? 4 : 8), &num)) {
|
||||
// not enough hex chars for escape sequence
|
||||
lex->tok_kind = MP_TOKEN_INVALID;
|
||||
}
|
||||
c = num;
|
||||
break;
|
||||
}
|
||||
case 'N':
|
||||
// Supporting '\N{LATIN SMALL LETTER A}' == 'a' would require keeping the
|
||||
// entire Unicode name table in the core. As of Unicode 6.3.0, that's nearly
|
||||
// 3MB of text; even gzip-compressed and with minimal structure, it'll take
|
||||
// roughly half a meg of storage. This form of Unicode escape may be added
|
||||
// later on, but it's definitely not a priority right now. -- CJA 20140607
|
||||
mp_raise_NotImplementedError(MP_ERROR_TEXT("unicode name escapes"));
|
||||
break;
|
||||
default:
|
||||
if (c >= '0' && c <= '7') {
|
||||
// Octal sequence, 1-3 chars
|
||||
size_t digits = 3;
|
||||
mp_uint_t num = c - '0';
|
||||
while (is_following_odigit(lex) && --digits != 0) {
|
||||
next_char(lex);
|
||||
num = num * 8 + (CUR_CHAR(lex) - '0');
|
||||
}
|
||||
c = num;
|
||||
} else {
|
||||
// unrecognised escape character; CPython lets this through verbatim as '\' and then the character
|
||||
vstr_add_char(&lex->vstr, '\\');
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (c != MP_LEXER_EOF) {
|
||||
if (MICROPY_PY_BUILTINS_STR_UNICODE_DYNAMIC) {
|
||||
if (c < 0x110000 && lex->tok_kind == MP_TOKEN_STRING) {
|
||||
vstr_add_char(&lex->vstr, c);
|
||||
} else if (c < 0x100 && lex->tok_kind == MP_TOKEN_BYTES) {
|
||||
vstr_add_byte(&lex->vstr, c);
|
||||
} else {
|
||||
// unicode character out of range
|
||||
// this raises a generic SyntaxError; could provide more info
|
||||
lex->tok_kind = MP_TOKEN_INVALID;
|
||||
}
|
||||
} else {
|
||||
// without unicode everything is just added as an 8-bit byte
|
||||
if (c < 0x100) {
|
||||
vstr_add_byte(&lex->vstr, c);
|
||||
} else {
|
||||
// 8-bit character out of range
|
||||
// this raises a generic SyntaxError; could provide more info
|
||||
lex->tok_kind = MP_TOKEN_INVALID;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Add the "character" as a byte so that we remain 8-bit clean.
|
||||
// This way, strings are parsed correctly whether or not they contain utf-8 chars.
|
||||
vstr_add_byte(&lex->vstr, CUR_CHAR(lex));
|
||||
}
|
||||
}
|
||||
next_char(lex);
|
||||
}
|
||||
|
||||
// check we got the required end quotes
|
||||
if (n_closing < num_quotes) {
|
||||
lex->tok_kind = MP_TOKEN_LONELY_STRING_OPEN;
|
||||
}
|
||||
|
||||
// cut off the end quotes from the token text
|
||||
vstr_cut_tail_bytes(&lex->vstr, n_closing);
|
||||
}
|
||||
|
||||
STATIC bool skip_whitespace(mp_lexer_t *lex, bool stop_at_newline) {
|
||||
bool had_physical_newline = false;
|
||||
while (!is_end(lex)) {
|
||||
if (is_physical_newline(lex)) {
|
||||
if (stop_at_newline && lex->nested_bracket_level == 0) {
|
||||
break;
|
||||
}
|
||||
had_physical_newline = true;
|
||||
next_char(lex);
|
||||
} else if (is_whitespace(lex)) {
|
||||
next_char(lex);
|
||||
} else if (is_char(lex, '#')) {
|
||||
next_char(lex);
|
||||
while (!is_end(lex) && !is_physical_newline(lex)) {
|
||||
next_char(lex);
|
||||
}
|
||||
// had_physical_newline will be set on next loop
|
||||
} else if (is_char_and(lex, '\\', '\n')) {
|
||||
// line-continuation, so don't set had_physical_newline
|
||||
next_char(lex);
|
||||
next_char(lex);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return had_physical_newline;
|
||||
}
|
||||
|
||||
void mp_lexer_to_next(mp_lexer_t *lex) {
|
||||
// start new token text
|
||||
vstr_reset(&lex->vstr);
|
||||
|
||||
// skip white space and comments
|
||||
bool had_physical_newline = skip_whitespace(lex, false);
|
||||
|
||||
// set token source information
|
||||
lex->tok_line = lex->line;
|
||||
lex->tok_column = lex->column;
|
||||
|
||||
if (lex->emit_dent < 0) {
|
||||
lex->tok_kind = MP_TOKEN_DEDENT;
|
||||
lex->emit_dent += 1;
|
||||
|
||||
} else if (lex->emit_dent > 0) {
|
||||
lex->tok_kind = MP_TOKEN_INDENT;
|
||||
lex->emit_dent -= 1;
|
||||
|
||||
} else if (had_physical_newline && lex->nested_bracket_level == 0) {
|
||||
lex->tok_kind = MP_TOKEN_NEWLINE;
|
||||
|
||||
size_t num_spaces = lex->column - 1;
|
||||
if (num_spaces == indent_top(lex)) {
|
||||
} else if (num_spaces > indent_top(lex)) {
|
||||
indent_push(lex, num_spaces);
|
||||
lex->emit_dent += 1;
|
||||
} else {
|
||||
while (num_spaces < indent_top(lex)) {
|
||||
indent_pop(lex);
|
||||
lex->emit_dent -= 1;
|
||||
}
|
||||
if (num_spaces != indent_top(lex)) {
|
||||
lex->tok_kind = MP_TOKEN_DEDENT_MISMATCH;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (is_end(lex)) {
|
||||
lex->tok_kind = MP_TOKEN_END;
|
||||
|
||||
} else if (is_string_or_bytes(lex)) {
|
||||
// a string or bytes literal
|
||||
|
||||
// Python requires adjacent string/bytes literals to be automatically
|
||||
// concatenated. We do it here in the tokeniser to make efficient use of RAM,
|
||||
// because then the lexer's vstr can be used to accumulate the string literal,
|
||||
// in contrast to creating a parse tree of strings and then joining them later
|
||||
// in the compiler. It's also more compact in code size to do it here.
|
||||
|
||||
// MP_TOKEN_END is used to indicate that this is the first string token
|
||||
lex->tok_kind = MP_TOKEN_END;
|
||||
|
||||
// Loop to accumulate string/bytes literals
|
||||
do {
|
||||
// parse type codes
|
||||
bool is_raw = false;
|
||||
mp_token_kind_t kind = MP_TOKEN_STRING;
|
||||
int n_char = 0;
|
||||
if (is_char(lex, 'u')) {
|
||||
n_char = 1;
|
||||
} else if (is_char(lex, 'b')) {
|
||||
kind = MP_TOKEN_BYTES;
|
||||
n_char = 1;
|
||||
if (is_char_following(lex, 'r')) {
|
||||
is_raw = true;
|
||||
n_char = 2;
|
||||
}
|
||||
} else if (is_char(lex, 'r')) {
|
||||
is_raw = true;
|
||||
n_char = 1;
|
||||
if (is_char_following(lex, 'b')) {
|
||||
kind = MP_TOKEN_BYTES;
|
||||
n_char = 2;
|
||||
}
|
||||
}
|
||||
|
||||
// Set or check token kind
|
||||
if (lex->tok_kind == MP_TOKEN_END) {
|
||||
lex->tok_kind = kind;
|
||||
} else if (lex->tok_kind != kind) {
|
||||
// Can't concatenate string with bytes
|
||||
break;
|
||||
}
|
||||
|
||||
// Skip any type code characters
|
||||
if (n_char != 0) {
|
||||
next_char(lex);
|
||||
if (n_char == 2) {
|
||||
next_char(lex);
|
||||
}
|
||||
}
|
||||
|
||||
// Parse the literal
|
||||
parse_string_literal(lex, is_raw);
|
||||
|
||||
// Skip whitespace so we can check if there's another string following
|
||||
skip_whitespace(lex, true);
|
||||
|
||||
} while (is_string_or_bytes(lex));
|
||||
|
||||
} else if (is_head_of_identifier(lex)) {
|
||||
lex->tok_kind = MP_TOKEN_NAME;
|
||||
|
||||
// get first char (add as byte to remain 8-bit clean and support utf-8)
|
||||
vstr_add_byte(&lex->vstr, CUR_CHAR(lex));
|
||||
next_char(lex);
|
||||
|
||||
// get tail chars
|
||||
while (!is_end(lex) && is_tail_of_identifier(lex)) {
|
||||
vstr_add_byte(&lex->vstr, CUR_CHAR(lex));
|
||||
next_char(lex);
|
||||
}
|
||||
|
||||
// Check if the name is a keyword.
|
||||
// We also check for __debug__ here and convert it to its value. This is
|
||||
// so the parser gives a syntax error on, eg, x.__debug__. Otherwise, we
|
||||
// need to check for this special token in many places in the compiler.
|
||||
const char *s = vstr_null_terminated_str(&lex->vstr);
|
||||
for (size_t i = 0; i < MP_ARRAY_SIZE(tok_kw); i++) {
|
||||
int cmp = strcmp(s, tok_kw[i]);
|
||||
if (cmp == 0) {
|
||||
lex->tok_kind = MP_TOKEN_KW_FALSE + i;
|
||||
if (lex->tok_kind == MP_TOKEN_KW___DEBUG__) {
|
||||
lex->tok_kind = (MP_STATE_VM(mp_optimise_value) == 0 ? MP_TOKEN_KW_TRUE : MP_TOKEN_KW_FALSE);
|
||||
}
|
||||
break;
|
||||
} else if (cmp < 0) {
|
||||
// Table is sorted and comparison was less-than, so stop searching
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (is_digit(lex) || (is_char(lex, '.') && is_following_digit(lex))) {
|
||||
bool forced_integer = false;
|
||||
if (is_char(lex, '.')) {
|
||||
lex->tok_kind = MP_TOKEN_FLOAT_OR_IMAG;
|
||||
} else {
|
||||
lex->tok_kind = MP_TOKEN_INTEGER;
|
||||
if (is_char(lex, '0') && is_following_base_char(lex)) {
|
||||
forced_integer = true;
|
||||
}
|
||||
}
|
||||
|
||||
// get first char
|
||||
vstr_add_char(&lex->vstr, CUR_CHAR(lex));
|
||||
next_char(lex);
|
||||
|
||||
// get tail chars
|
||||
while (!is_end(lex)) {
|
||||
if (!forced_integer && is_char_or(lex, 'e', 'E')) {
|
||||
lex->tok_kind = MP_TOKEN_FLOAT_OR_IMAG;
|
||||
vstr_add_char(&lex->vstr, 'e');
|
||||
next_char(lex);
|
||||
if (is_char(lex, '+') || is_char(lex, '-')) {
|
||||
vstr_add_char(&lex->vstr, CUR_CHAR(lex));
|
||||
next_char(lex);
|
||||
}
|
||||
} else if (is_letter(lex) || is_digit(lex) || is_char(lex, '.')) {
|
||||
if (is_char_or3(lex, '.', 'j', 'J')) {
|
||||
lex->tok_kind = MP_TOKEN_FLOAT_OR_IMAG;
|
||||
}
|
||||
vstr_add_char(&lex->vstr, CUR_CHAR(lex));
|
||||
next_char(lex);
|
||||
} else if (is_char(lex, '_')) {
|
||||
next_char(lex);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// search for encoded delimiter or operator
|
||||
|
||||
const char *t = tok_enc;
|
||||
size_t tok_enc_index = 0;
|
||||
for (; *t != 0 && !is_char(lex, *t); t += 1) {
|
||||
if (*t == 'e' || *t == 'c') {
|
||||
t += 1;
|
||||
}
|
||||
tok_enc_index += 1;
|
||||
}
|
||||
|
||||
next_char(lex);
|
||||
|
||||
if (*t == 0) {
|
||||
// didn't match any delimiter or operator characters
|
||||
lex->tok_kind = MP_TOKEN_INVALID;
|
||||
|
||||
} else if (*t == '!') {
|
||||
// "!=" is a special case because "!" is not a valid operator
|
||||
if (is_char(lex, '=')) {
|
||||
next_char(lex);
|
||||
lex->tok_kind = MP_TOKEN_OP_NOT_EQUAL;
|
||||
} else {
|
||||
lex->tok_kind = MP_TOKEN_INVALID;
|
||||
}
|
||||
|
||||
} else if (*t == '.') {
|
||||
// "." and "..." are special cases because ".." is not a valid operator
|
||||
if (is_char_and(lex, '.', '.')) {
|
||||
next_char(lex);
|
||||
next_char(lex);
|
||||
lex->tok_kind = MP_TOKEN_ELLIPSIS;
|
||||
} else {
|
||||
lex->tok_kind = MP_TOKEN_DEL_PERIOD;
|
||||
}
|
||||
|
||||
} else {
|
||||
// matched a delimiter or operator character
|
||||
|
||||
// get the maximum characters for a valid token
|
||||
t += 1;
|
||||
size_t t_index = tok_enc_index;
|
||||
while (*t == 'c' || *t == 'e') {
|
||||
t_index += 1;
|
||||
if (is_char(lex, t[1])) {
|
||||
next_char(lex);
|
||||
tok_enc_index = t_index;
|
||||
if (*t == 'e') {
|
||||
break;
|
||||
}
|
||||
} else if (*t == 'c') {
|
||||
break;
|
||||
}
|
||||
t += 2;
|
||||
}
|
||||
|
||||
// set token kind
|
||||
lex->tok_kind = tok_enc_kind[tok_enc_index];
|
||||
|
||||
// compute bracket level for implicit line joining
|
||||
if (lex->tok_kind == MP_TOKEN_DEL_PAREN_OPEN || lex->tok_kind == MP_TOKEN_DEL_BRACKET_OPEN || lex->tok_kind == MP_TOKEN_DEL_BRACE_OPEN) {
|
||||
lex->nested_bracket_level += 1;
|
||||
} else if (lex->tok_kind == MP_TOKEN_DEL_PAREN_CLOSE || lex->tok_kind == MP_TOKEN_DEL_BRACKET_CLOSE || lex->tok_kind == MP_TOKEN_DEL_BRACE_CLOSE) {
|
||||
lex->nested_bracket_level -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mp_lexer_t *mp_lexer_new(qstr src_name, mp_reader_t reader) {
|
||||
mp_lexer_t *lex = m_new_obj(mp_lexer_t);
|
||||
|
||||
lex->source_name = src_name;
|
||||
lex->reader = reader;
|
||||
lex->line = 1;
|
||||
lex->column = (size_t)-2; // account for 3 dummy bytes
|
||||
lex->emit_dent = 0;
|
||||
lex->nested_bracket_level = 0;
|
||||
lex->alloc_indent_level = MICROPY_ALLOC_LEXER_INDENT_INIT;
|
||||
lex->num_indent_level = 1;
|
||||
lex->indent_level = m_new(uint16_t, lex->alloc_indent_level);
|
||||
vstr_init(&lex->vstr, 32);
|
||||
|
||||
// store sentinel for first indentation level
|
||||
lex->indent_level[0] = 0;
|
||||
|
||||
// load lexer with start of file, advancing lex->column to 1
|
||||
// start with dummy bytes and use next_char() for proper EOL/EOF handling
|
||||
lex->chr0 = lex->chr1 = lex->chr2 = 0;
|
||||
next_char(lex);
|
||||
next_char(lex);
|
||||
next_char(lex);
|
||||
|
||||
// preload first token
|
||||
mp_lexer_to_next(lex);
|
||||
|
||||
// Check that the first token is in the first column. If it's not then we
|
||||
// convert the token kind to INDENT so that the parser gives a syntax error.
|
||||
if (lex->tok_column != 1) {
|
||||
lex->tok_kind = MP_TOKEN_INDENT;
|
||||
}
|
||||
|
||||
return lex;
|
||||
}
|
||||
|
||||
mp_lexer_t *mp_lexer_new_from_str_len(qstr src_name, const char *str, size_t len, size_t free_len) {
|
||||
mp_reader_t reader;
|
||||
mp_reader_new_mem(&reader, (const byte *)str, len, free_len);
|
||||
return mp_lexer_new(src_name, reader);
|
||||
}
|
||||
|
||||
#if MICROPY_READER_POSIX || MICROPY_READER_VFS
|
||||
|
||||
mp_lexer_t *mp_lexer_new_from_file(const char *filename) {
|
||||
mp_reader_t reader;
|
||||
mp_reader_new_file(&reader, filename);
|
||||
return mp_lexer_new(qstr_from_str(filename), reader);
|
||||
}
|
||||
|
||||
#if MICROPY_HELPER_LEXER_UNIX
|
||||
|
||||
mp_lexer_t *mp_lexer_new_from_fd(qstr filename, int fd, bool close_fd) {
|
||||
mp_reader_t reader;
|
||||
mp_reader_new_file_from_fd(&reader, fd, close_fd);
|
||||
return mp_lexer_new(filename, reader);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
void mp_lexer_free(mp_lexer_t *lex) {
|
||||
if (lex) {
|
||||
lex->reader.close(lex->reader.data);
|
||||
vstr_clear(&lex->vstr);
|
||||
m_del(uint16_t, lex->indent_level, lex->alloc_indent_level);
|
||||
m_del_obj(mp_lexer_t, lex);
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
// This function is used to print the current token and should only be
|
||||
// needed to debug the lexer, so it's not available via a config option.
|
||||
void mp_lexer_show_token(const mp_lexer_t *lex) {
|
||||
printf("(" UINT_FMT ":" UINT_FMT ") kind:%u str:%p len:%zu", lex->tok_line, lex->tok_column, lex->tok_kind, lex->vstr.buf, lex->vstr.len);
|
||||
if (lex->vstr.len > 0) {
|
||||
const byte *i = (const byte *)lex->vstr.buf;
|
||||
const byte *j = (const byte *)i + lex->vstr.len;
|
||||
printf(" ");
|
||||
while (i < j) {
|
||||
unichar c = utf8_get_char(i);
|
||||
i = utf8_next_char(i);
|
||||
if (unichar_isprint(c)) {
|
||||
printf("%c", (int)c);
|
||||
} else {
|
||||
printf("?");
|
||||
}
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // MICROPY_ENABLE_COMPILER
|
||||
@@ -0,0 +1,201 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013, 2014 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef MICROPY_INCLUDED_PY_LEXER_H
|
||||
#define MICROPY_INCLUDED_PY_LEXER_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "py/mpconfig.h"
|
||||
#include "py/qstr.h"
|
||||
#include "py/reader.h"
|
||||
|
||||
/* lexer.h -- simple tokeniser for MicroPython
|
||||
*
|
||||
* Uses (byte) length instead of null termination.
|
||||
* Tokens are the same - UTF-8 with (byte) length.
|
||||
*/
|
||||
|
||||
typedef enum _mp_token_kind_t {
|
||||
MP_TOKEN_END,
|
||||
|
||||
MP_TOKEN_INVALID,
|
||||
MP_TOKEN_DEDENT_MISMATCH,
|
||||
MP_TOKEN_LONELY_STRING_OPEN,
|
||||
|
||||
MP_TOKEN_NEWLINE,
|
||||
MP_TOKEN_INDENT,
|
||||
MP_TOKEN_DEDENT,
|
||||
|
||||
MP_TOKEN_NAME,
|
||||
MP_TOKEN_INTEGER,
|
||||
MP_TOKEN_FLOAT_OR_IMAG,
|
||||
MP_TOKEN_STRING,
|
||||
MP_TOKEN_BYTES,
|
||||
|
||||
MP_TOKEN_ELLIPSIS,
|
||||
|
||||
MP_TOKEN_KW_FALSE,
|
||||
MP_TOKEN_KW_NONE,
|
||||
MP_TOKEN_KW_TRUE,
|
||||
MP_TOKEN_KW___DEBUG__,
|
||||
MP_TOKEN_KW_AND,
|
||||
MP_TOKEN_KW_AS,
|
||||
MP_TOKEN_KW_ASSERT,
|
||||
#if MICROPY_PY_ASYNC_AWAIT
|
||||
MP_TOKEN_KW_ASYNC,
|
||||
MP_TOKEN_KW_AWAIT,
|
||||
#endif
|
||||
MP_TOKEN_KW_BREAK,
|
||||
MP_TOKEN_KW_CLASS,
|
||||
MP_TOKEN_KW_CONTINUE,
|
||||
MP_TOKEN_KW_DEF,
|
||||
MP_TOKEN_KW_DEL,
|
||||
MP_TOKEN_KW_ELIF,
|
||||
MP_TOKEN_KW_ELSE,
|
||||
MP_TOKEN_KW_EXCEPT,
|
||||
MP_TOKEN_KW_FINALLY,
|
||||
MP_TOKEN_KW_FOR,
|
||||
MP_TOKEN_KW_FROM,
|
||||
MP_TOKEN_KW_GLOBAL,
|
||||
MP_TOKEN_KW_IF,
|
||||
MP_TOKEN_KW_IMPORT,
|
||||
MP_TOKEN_KW_IN,
|
||||
MP_TOKEN_KW_IS,
|
||||
MP_TOKEN_KW_LAMBDA,
|
||||
MP_TOKEN_KW_NONLOCAL,
|
||||
MP_TOKEN_KW_NOT,
|
||||
MP_TOKEN_KW_OR,
|
||||
MP_TOKEN_KW_PASS,
|
||||
MP_TOKEN_KW_RAISE,
|
||||
MP_TOKEN_KW_RETURN,
|
||||
MP_TOKEN_KW_TRY,
|
||||
MP_TOKEN_KW_WHILE,
|
||||
MP_TOKEN_KW_WITH,
|
||||
MP_TOKEN_KW_YIELD,
|
||||
|
||||
MP_TOKEN_OP_ASSIGN,
|
||||
MP_TOKEN_OP_TILDE,
|
||||
|
||||
// Order of these 6 matches corresponding mp_binary_op_t operator
|
||||
MP_TOKEN_OP_LESS,
|
||||
MP_TOKEN_OP_MORE,
|
||||
MP_TOKEN_OP_DBL_EQUAL,
|
||||
MP_TOKEN_OP_LESS_EQUAL,
|
||||
MP_TOKEN_OP_MORE_EQUAL,
|
||||
MP_TOKEN_OP_NOT_EQUAL,
|
||||
|
||||
// Order of these 13 matches corresponding mp_binary_op_t operator
|
||||
MP_TOKEN_OP_PIPE,
|
||||
MP_TOKEN_OP_CARET,
|
||||
MP_TOKEN_OP_AMPERSAND,
|
||||
MP_TOKEN_OP_DBL_LESS,
|
||||
MP_TOKEN_OP_DBL_MORE,
|
||||
MP_TOKEN_OP_PLUS,
|
||||
MP_TOKEN_OP_MINUS,
|
||||
MP_TOKEN_OP_STAR,
|
||||
MP_TOKEN_OP_AT,
|
||||
MP_TOKEN_OP_DBL_SLASH,
|
||||
MP_TOKEN_OP_SLASH,
|
||||
MP_TOKEN_OP_PERCENT,
|
||||
MP_TOKEN_OP_DBL_STAR,
|
||||
|
||||
// Order of these 13 matches corresponding mp_binary_op_t operator
|
||||
MP_TOKEN_DEL_PIPE_EQUAL,
|
||||
MP_TOKEN_DEL_CARET_EQUAL,
|
||||
MP_TOKEN_DEL_AMPERSAND_EQUAL,
|
||||
MP_TOKEN_DEL_DBL_LESS_EQUAL,
|
||||
MP_TOKEN_DEL_DBL_MORE_EQUAL,
|
||||
MP_TOKEN_DEL_PLUS_EQUAL,
|
||||
MP_TOKEN_DEL_MINUS_EQUAL,
|
||||
MP_TOKEN_DEL_STAR_EQUAL,
|
||||
MP_TOKEN_DEL_AT_EQUAL,
|
||||
MP_TOKEN_DEL_DBL_SLASH_EQUAL,
|
||||
MP_TOKEN_DEL_SLASH_EQUAL,
|
||||
MP_TOKEN_DEL_PERCENT_EQUAL,
|
||||
MP_TOKEN_DEL_DBL_STAR_EQUAL,
|
||||
|
||||
MP_TOKEN_DEL_PAREN_OPEN,
|
||||
MP_TOKEN_DEL_PAREN_CLOSE,
|
||||
MP_TOKEN_DEL_BRACKET_OPEN,
|
||||
MP_TOKEN_DEL_BRACKET_CLOSE,
|
||||
MP_TOKEN_DEL_BRACE_OPEN,
|
||||
MP_TOKEN_DEL_BRACE_CLOSE,
|
||||
MP_TOKEN_DEL_COMMA,
|
||||
MP_TOKEN_DEL_COLON,
|
||||
MP_TOKEN_DEL_PERIOD,
|
||||
MP_TOKEN_DEL_SEMICOLON,
|
||||
MP_TOKEN_DEL_EQUAL,
|
||||
MP_TOKEN_DEL_MINUS_MORE,
|
||||
} mp_token_kind_t;
|
||||
|
||||
// this data structure is exposed for efficiency
|
||||
// public members are: source_name, tok_line, tok_column, tok_kind, vstr
|
||||
typedef struct _mp_lexer_t {
|
||||
qstr source_name; // name of source
|
||||
mp_reader_t reader; // stream source
|
||||
|
||||
unichar chr0, chr1, chr2; // current cached characters from source
|
||||
|
||||
size_t line; // current source line
|
||||
size_t column; // current source column
|
||||
|
||||
mp_int_t emit_dent; // non-zero when there are INDENT/DEDENT tokens to emit
|
||||
mp_int_t nested_bracket_level; // >0 when there are nested brackets over multiple lines
|
||||
|
||||
size_t alloc_indent_level;
|
||||
size_t num_indent_level;
|
||||
uint16_t *indent_level;
|
||||
|
||||
size_t tok_line; // token source line
|
||||
size_t tok_column; // token source column
|
||||
mp_token_kind_t tok_kind; // token kind
|
||||
vstr_t vstr; // token data
|
||||
} mp_lexer_t;
|
||||
|
||||
mp_lexer_t *mp_lexer_new(qstr src_name, mp_reader_t reader);
|
||||
mp_lexer_t *mp_lexer_new_from_str_len(qstr src_name, const char *str, size_t len, size_t free_len);
|
||||
|
||||
void mp_lexer_free(mp_lexer_t *lex);
|
||||
void mp_lexer_to_next(mp_lexer_t *lex);
|
||||
|
||||
/******************************************************************/
|
||||
// platform specific import function; must be implemented for a specific port
|
||||
// TODO tidy up, rename, or put elsewhere
|
||||
|
||||
typedef enum {
|
||||
MP_IMPORT_STAT_NO_EXIST,
|
||||
MP_IMPORT_STAT_DIR,
|
||||
MP_IMPORT_STAT_FILE,
|
||||
} mp_import_stat_t;
|
||||
|
||||
mp_import_stat_t mp_import_stat(const char *path);
|
||||
mp_lexer_t *mp_lexer_new_from_file(const char *filename);
|
||||
|
||||
#if MICROPY_HELPER_LEXER_UNIX
|
||||
mp_lexer_t *mp_lexer_new_from_fd(qstr filename, int fd, bool close_fd);
|
||||
#endif
|
||||
|
||||
#endif // MICROPY_INCLUDED_PY_LEXER_H
|
||||
@@ -0,0 +1,205 @@
|
||||
from __future__ import print_function
|
||||
|
||||
import collections
|
||||
import re
|
||||
import sys
|
||||
|
||||
import gzip
|
||||
import zlib
|
||||
|
||||
|
||||
_COMPRESSED_MARKER = 0xFF
|
||||
|
||||
|
||||
def check_non_ascii(msg):
|
||||
for c in msg:
|
||||
if ord(c) >= 0x80:
|
||||
print(
|
||||
'Unable to generate compressed data: message "{}" contains a non-ascii character "{}".'.format(
|
||||
msg, c
|
||||
),
|
||||
file=sys.stderr,
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
# Replace <char><space> with <char | 0x80>.
|
||||
# Trival scheme to demo/test.
|
||||
def space_compression(error_strings):
|
||||
for line in error_strings:
|
||||
check_non_ascii(line)
|
||||
result = ""
|
||||
for i in range(len(line)):
|
||||
if i > 0 and line[i] == " ":
|
||||
result = result[:-1]
|
||||
result += "\\{:03o}".format(ord(line[i - 1]))
|
||||
else:
|
||||
result += line[i]
|
||||
error_strings[line] = result
|
||||
return None
|
||||
|
||||
|
||||
# Replace common words with <0x80 | index>.
|
||||
# Index is into a table of words stored as aaaaa<0x80|a>bbb<0x80|b>...
|
||||
# Replaced words are assumed to have spaces either side to avoid having to store the spaces in the compressed strings.
|
||||
def word_compression(error_strings):
|
||||
topn = collections.Counter()
|
||||
|
||||
for line in error_strings.keys():
|
||||
check_non_ascii(line)
|
||||
for word in line.split(" "):
|
||||
topn[word] += 1
|
||||
|
||||
# Order not just by frequency, but by expected saving. i.e. prefer a longer string that is used less frequently.
|
||||
# Use the word itself for ties so that compression is deterministic.
|
||||
def bytes_saved(item):
|
||||
w, n = item
|
||||
return -((len(w) + 1) * (n - 1)), w
|
||||
|
||||
top128 = sorted(topn.items(), key=bytes_saved)[:128]
|
||||
|
||||
index = [w for w, _ in top128]
|
||||
index_lookup = {w: i for i, w in enumerate(index)}
|
||||
|
||||
for line in error_strings.keys():
|
||||
result = ""
|
||||
need_space = False
|
||||
for word in line.split(" "):
|
||||
if word in index_lookup:
|
||||
result += "\\{:03o}".format(0b10000000 | index_lookup[word])
|
||||
need_space = False
|
||||
else:
|
||||
if need_space:
|
||||
result += " "
|
||||
need_space = True
|
||||
result += word
|
||||
error_strings[line] = result.strip()
|
||||
|
||||
return "".join(w[:-1] + "\\{:03o}".format(0b10000000 | ord(w[-1])) for w in index)
|
||||
|
||||
|
||||
# Replace chars in text with variable length bit sequence.
|
||||
# For comparison only (the table is not emitted).
|
||||
def huffman_compression(error_strings):
|
||||
# https://github.com/tannewt/huffman
|
||||
import huffman
|
||||
|
||||
all_strings = "".join(error_strings)
|
||||
cb = huffman.codebook(collections.Counter(all_strings).items())
|
||||
|
||||
for line in error_strings:
|
||||
b = "1"
|
||||
for c in line:
|
||||
b += cb[c]
|
||||
n = len(b)
|
||||
if n % 8 != 0:
|
||||
n += 8 - (n % 8)
|
||||
result = ""
|
||||
for i in range(0, n, 8):
|
||||
result += "\\{:03o}".format(int(b[i : i + 8], 2))
|
||||
if len(result) > len(line) * 4:
|
||||
result = line
|
||||
error_strings[line] = result
|
||||
|
||||
# TODO: This would be the prefix lengths and the table ordering.
|
||||
return "_" * (10 + len(cb))
|
||||
|
||||
|
||||
# Replace common N-letter sequences with <0x80 | index>, where
|
||||
# the common sequences are stored in a separate table.
|
||||
# This isn't very useful, need a smarter way to find top-ngrams.
|
||||
def ngram_compression(error_strings):
|
||||
topn = collections.Counter()
|
||||
N = 2
|
||||
|
||||
for line in error_strings.keys():
|
||||
check_non_ascii(line)
|
||||
if len(line) < N:
|
||||
continue
|
||||
for i in range(0, len(line) - N, N):
|
||||
topn[line[i : i + N]] += 1
|
||||
|
||||
def bytes_saved(item):
|
||||
w, n = item
|
||||
return -(len(w) * (n - 1))
|
||||
|
||||
top128 = sorted(topn.items(), key=bytes_saved)[:128]
|
||||
|
||||
index = [w for w, _ in top128]
|
||||
index_lookup = {w: i for i, w in enumerate(index)}
|
||||
|
||||
for line in error_strings.keys():
|
||||
result = ""
|
||||
for i in range(0, len(line) - N + 1, N):
|
||||
word = line[i : i + N]
|
||||
if word in index_lookup:
|
||||
result += "\\{:03o}".format(0b10000000 | index_lookup[word])
|
||||
else:
|
||||
result += word
|
||||
if len(line) % N != 0:
|
||||
result += line[len(line) - len(line) % N :]
|
||||
error_strings[line] = result.strip()
|
||||
|
||||
return "".join(index)
|
||||
|
||||
|
||||
def main(collected_path, fn):
|
||||
error_strings = collections.OrderedDict()
|
||||
max_uncompressed_len = 0
|
||||
num_uses = 0
|
||||
|
||||
# Read in all MP_ERROR_TEXT strings.
|
||||
with open(collected_path, "r") as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
num_uses += 1
|
||||
error_strings[line] = None
|
||||
max_uncompressed_len = max(max_uncompressed_len, len(line))
|
||||
|
||||
# So that objexcept.c can figure out how big the buffer needs to be.
|
||||
print("#define MP_MAX_UNCOMPRESSED_TEXT_LEN ({})".format(max_uncompressed_len))
|
||||
|
||||
# Run the compression.
|
||||
compressed_data = fn(error_strings)
|
||||
|
||||
# Print the data table.
|
||||
print('MP_COMPRESSED_DATA("{}")'.format(compressed_data))
|
||||
|
||||
# Print the replacements.
|
||||
for uncomp, comp in error_strings.items():
|
||||
if uncomp == comp:
|
||||
prefix = ""
|
||||
else:
|
||||
prefix = "\\{:03o}".format(_COMPRESSED_MARKER)
|
||||
print('MP_MATCH_COMPRESSED("{}", "{}{}")'.format(uncomp, prefix, comp))
|
||||
|
||||
# Used to calculate the "true" length of the (escaped) compressed strings.
|
||||
def unescape(s):
|
||||
return re.sub(r"\\\d\d\d", "!", s)
|
||||
|
||||
# Stats. Note this doesn't include the cost of the decompressor code.
|
||||
uncomp_len = sum(len(s) + 1 for s in error_strings.keys())
|
||||
comp_len = sum(1 + len(unescape(s)) + 1 for s in error_strings.values())
|
||||
data_len = len(compressed_data) + 1 if compressed_data else 0
|
||||
print("// Total input length: {}".format(uncomp_len))
|
||||
print("// Total compressed length: {}".format(comp_len))
|
||||
print("// Total data length: {}".format(data_len))
|
||||
print("// Predicted saving: {}".format(uncomp_len - comp_len - data_len))
|
||||
|
||||
# Somewhat meaningless comparison to zlib/gzip.
|
||||
all_input_bytes = "\\0".join(error_strings.keys()).encode()
|
||||
print()
|
||||
if hasattr(gzip, "compress"):
|
||||
gzip_len = len(gzip.compress(all_input_bytes)) + num_uses * 4
|
||||
print("// gzip length: {}".format(gzip_len))
|
||||
print("// Percentage of gzip: {:.1f}%".format(100 * (comp_len + data_len) / gzip_len))
|
||||
if hasattr(zlib, "compress"):
|
||||
zlib_len = len(zlib.compress(all_input_bytes)) + num_uses * 4
|
||||
print("// zlib length: {}".format(zlib_len))
|
||||
print("// Percentage of zlib: {:.1f}%".format(100 * (comp_len + data_len) / zlib_len))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(sys.argv[1], word_compression)
|
||||
@@ -0,0 +1,110 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
# This pre-processor parses provided objects' c files for
|
||||
# MP_REGISTER_MODULE(module_name, obj_module, enabled_define)
|
||||
# These are used to generate a header with the required entries for
|
||||
# "mp_rom_map_elem_t mp_builtin_module_table[]" in py/objmodule.c
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import re
|
||||
import io
|
||||
import os
|
||||
import argparse
|
||||
|
||||
|
||||
pattern = re.compile(r"[\n;]\s*MP_REGISTER_MODULE\((.*?),\s*(.*?),\s*(.*?)\);", flags=re.DOTALL)
|
||||
|
||||
|
||||
def find_c_file(obj_file, vpath):
|
||||
"""Search vpaths for the c file that matches the provided object_file.
|
||||
|
||||
:param str obj_file: object file to find the matching c file for
|
||||
:param List[str] vpath: List of base paths, similar to gcc vpath
|
||||
:return: str path to c file or None
|
||||
"""
|
||||
c_file = None
|
||||
relative_c_file = os.path.splitext(obj_file)[0] + ".c"
|
||||
relative_c_file = relative_c_file.lstrip("/\\")
|
||||
for p in vpath:
|
||||
possible_c_file = os.path.join(p, relative_c_file)
|
||||
if os.path.exists(possible_c_file):
|
||||
c_file = possible_c_file
|
||||
break
|
||||
|
||||
return c_file
|
||||
|
||||
|
||||
def find_module_registrations(c_file):
|
||||
"""Find any MP_REGISTER_MODULE definitions in the provided c file.
|
||||
|
||||
:param str c_file: path to c file to check
|
||||
:return: List[(module_name, obj_module, enabled_define)]
|
||||
"""
|
||||
global pattern
|
||||
|
||||
if c_file is None:
|
||||
# No c file to match the object file, skip
|
||||
return set()
|
||||
|
||||
with io.open(c_file, encoding="utf-8") as c_file_obj:
|
||||
return set(re.findall(pattern, c_file_obj.read()))
|
||||
|
||||
|
||||
def generate_module_table_header(modules):
|
||||
"""Generate header with module table entries for builtin modules.
|
||||
|
||||
:param List[(module_name, obj_module, enabled_define)] modules: module defs
|
||||
:return: None
|
||||
"""
|
||||
|
||||
# Print header file for all external modules.
|
||||
mod_defs = []
|
||||
print("// Automatically generated by makemoduledefs.py.\n")
|
||||
for module_name, obj_module, enabled_define in modules:
|
||||
mod_def = "MODULE_DEF_{}".format(module_name.upper())
|
||||
mod_defs.append(mod_def)
|
||||
print(
|
||||
(
|
||||
"#if ({enabled_define})\n"
|
||||
" extern const struct _mp_obj_module_t {obj_module};\n"
|
||||
" #define {mod_def} {{ MP_ROM_QSTR({module_name}), MP_ROM_PTR(&{obj_module}) }},\n"
|
||||
"#else\n"
|
||||
" #define {mod_def}\n"
|
||||
"#endif\n"
|
||||
).format(
|
||||
module_name=module_name,
|
||||
obj_module=obj_module,
|
||||
enabled_define=enabled_define,
|
||||
mod_def=mod_def,
|
||||
)
|
||||
)
|
||||
|
||||
print("\n#define MICROPY_REGISTERED_MODULES \\")
|
||||
|
||||
for mod_def in mod_defs:
|
||||
print(" {mod_def} \\".format(mod_def=mod_def))
|
||||
|
||||
print("// MICROPY_REGISTERED_MODULES")
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument(
|
||||
"--vpath", default=".", help="comma separated list of folders to search for c files in"
|
||||
)
|
||||
parser.add_argument("files", nargs="*", help="list of c files to search")
|
||||
args = parser.parse_args()
|
||||
|
||||
vpath = [p.strip() for p in args.vpath.split(",")]
|
||||
|
||||
modules = set()
|
||||
for obj_file in args.files:
|
||||
c_file = find_c_file(obj_file, vpath)
|
||||
modules |= find_module_registrations(c_file)
|
||||
|
||||
generate_module_table_header(sorted(modules))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,369 @@
|
||||
"""
|
||||
Process raw qstr file and output qstr data with length, hash and data bytes.
|
||||
|
||||
This script works with Python 2.6, 2.7, 3.3 and 3.4.
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import re
|
||||
import sys
|
||||
|
||||
# Python 2/3 compatibility:
|
||||
# - iterating through bytes is different
|
||||
# - codepoint2name lives in a different module
|
||||
import platform
|
||||
|
||||
if platform.python_version_tuple()[0] == "2":
|
||||
bytes_cons = lambda val, enc=None: bytearray(val)
|
||||
from htmlentitydefs import codepoint2name
|
||||
elif platform.python_version_tuple()[0] == "3":
|
||||
bytes_cons = bytes
|
||||
from html.entities import codepoint2name
|
||||
# end compatibility code
|
||||
|
||||
codepoint2name[ord("-")] = "hyphen"
|
||||
|
||||
# add some custom names to map characters that aren't in HTML
|
||||
codepoint2name[ord(" ")] = "space"
|
||||
codepoint2name[ord("'")] = "squot"
|
||||
codepoint2name[ord(",")] = "comma"
|
||||
codepoint2name[ord(".")] = "dot"
|
||||
codepoint2name[ord(":")] = "colon"
|
||||
codepoint2name[ord(";")] = "semicolon"
|
||||
codepoint2name[ord("/")] = "slash"
|
||||
codepoint2name[ord("%")] = "percent"
|
||||
codepoint2name[ord("#")] = "hash"
|
||||
codepoint2name[ord("(")] = "paren_open"
|
||||
codepoint2name[ord(")")] = "paren_close"
|
||||
codepoint2name[ord("[")] = "bracket_open"
|
||||
codepoint2name[ord("]")] = "bracket_close"
|
||||
codepoint2name[ord("{")] = "brace_open"
|
||||
codepoint2name[ord("}")] = "brace_close"
|
||||
codepoint2name[ord("*")] = "star"
|
||||
codepoint2name[ord("!")] = "bang"
|
||||
codepoint2name[ord("\\")] = "backslash"
|
||||
codepoint2name[ord("+")] = "plus"
|
||||
codepoint2name[ord("$")] = "dollar"
|
||||
codepoint2name[ord("=")] = "equals"
|
||||
codepoint2name[ord("?")] = "question"
|
||||
codepoint2name[ord("@")] = "at_sign"
|
||||
codepoint2name[ord("^")] = "caret"
|
||||
codepoint2name[ord("|")] = "pipe"
|
||||
codepoint2name[ord("~")] = "tilde"
|
||||
|
||||
# static qstrs, should be sorted
|
||||
|
||||
static_qstr_list = [
|
||||
"",
|
||||
"__dir__", # Put __dir__ after empty qstr for builtin dir() to work
|
||||
"\n",
|
||||
" ",
|
||||
"*",
|
||||
"/",
|
||||
"<module>",
|
||||
"_",
|
||||
"__call__",
|
||||
"__class__",
|
||||
"__delitem__",
|
||||
"__enter__",
|
||||
"__exit__",
|
||||
"__getattr__",
|
||||
"__getitem__",
|
||||
"__hash__",
|
||||
"__init__",
|
||||
"__int__",
|
||||
"__iter__",
|
||||
"__len__",
|
||||
"__main__",
|
||||
"__module__",
|
||||
"__name__",
|
||||
"__new__",
|
||||
"__next__",
|
||||
"__qualname__",
|
||||
"__repr__",
|
||||
"__setitem__",
|
||||
"__str__",
|
||||
"ArithmeticError",
|
||||
"AssertionError",
|
||||
"AttributeError",
|
||||
"BaseException",
|
||||
"EOFError",
|
||||
"Ellipsis",
|
||||
"Exception",
|
||||
"GeneratorExit",
|
||||
"ImportError",
|
||||
"IndentationError",
|
||||
"IndexError",
|
||||
"KeyError",
|
||||
"KeyboardInterrupt",
|
||||
"LookupError",
|
||||
"MemoryError",
|
||||
"NameError",
|
||||
"NoneType",
|
||||
"NotImplementedError",
|
||||
"OSError",
|
||||
"OverflowError",
|
||||
"RuntimeError",
|
||||
"StopIteration",
|
||||
"SyntaxError",
|
||||
"SystemExit",
|
||||
"TypeError",
|
||||
"ValueError",
|
||||
"ZeroDivisionError",
|
||||
"abs",
|
||||
"all",
|
||||
"any",
|
||||
"append",
|
||||
"args",
|
||||
"bool",
|
||||
"builtins",
|
||||
"bytearray",
|
||||
"bytecode",
|
||||
"bytes",
|
||||
"callable",
|
||||
"chr",
|
||||
"classmethod",
|
||||
"clear",
|
||||
"close",
|
||||
"const",
|
||||
"copy",
|
||||
"count",
|
||||
"dict",
|
||||
"dir",
|
||||
"divmod",
|
||||
"end",
|
||||
"endswith",
|
||||
"eval",
|
||||
"exec",
|
||||
"extend",
|
||||
"find",
|
||||
"format",
|
||||
"from_bytes",
|
||||
"get",
|
||||
"getattr",
|
||||
"globals",
|
||||
"hasattr",
|
||||
"hash",
|
||||
"id",
|
||||
"index",
|
||||
"insert",
|
||||
"int",
|
||||
"isalpha",
|
||||
"isdigit",
|
||||
"isinstance",
|
||||
"islower",
|
||||
"isspace",
|
||||
"issubclass",
|
||||
"isupper",
|
||||
"items",
|
||||
"iter",
|
||||
"join",
|
||||
"key",
|
||||
"keys",
|
||||
"len",
|
||||
"list",
|
||||
"little",
|
||||
"locals",
|
||||
"lower",
|
||||
"lstrip",
|
||||
"main",
|
||||
"map",
|
||||
"micropython",
|
||||
"next",
|
||||
"object",
|
||||
"open",
|
||||
"ord",
|
||||
"pop",
|
||||
"popitem",
|
||||
"pow",
|
||||
"print",
|
||||
"range",
|
||||
"read",
|
||||
"readinto",
|
||||
"readline",
|
||||
"remove",
|
||||
"replace",
|
||||
"repr",
|
||||
"reverse",
|
||||
"rfind",
|
||||
"rindex",
|
||||
"round",
|
||||
"rsplit",
|
||||
"rstrip",
|
||||
"self",
|
||||
"send",
|
||||
"sep",
|
||||
"set",
|
||||
"setattr",
|
||||
"setdefault",
|
||||
"sort",
|
||||
"sorted",
|
||||
"split",
|
||||
"start",
|
||||
"startswith",
|
||||
"staticmethod",
|
||||
"step",
|
||||
"stop",
|
||||
"str",
|
||||
"strip",
|
||||
"sum",
|
||||
"super",
|
||||
"throw",
|
||||
"to_bytes",
|
||||
"tuple",
|
||||
"type",
|
||||
"update",
|
||||
"upper",
|
||||
"utf-8",
|
||||
"value",
|
||||
"values",
|
||||
"write",
|
||||
"zip",
|
||||
]
|
||||
|
||||
# this must match the equivalent function in qstr.c
|
||||
def compute_hash(qstr, bytes_hash):
|
||||
hash = 5381
|
||||
for b in qstr:
|
||||
hash = (hash * 33) ^ b
|
||||
# Make sure that valid hash is never zero, zero means "hash not computed"
|
||||
return (hash & ((1 << (8 * bytes_hash)) - 1)) or 1
|
||||
|
||||
|
||||
def qstr_escape(qst):
|
||||
def esc_char(m):
|
||||
c = ord(m.group(0))
|
||||
try:
|
||||
name = codepoint2name[c]
|
||||
except KeyError:
|
||||
name = "0x%02x" % c
|
||||
return "_" + name + "_"
|
||||
|
||||
return re.sub(r"[^A-Za-z0-9_]", esc_char, qst)
|
||||
|
||||
|
||||
def parse_input_headers(infiles):
|
||||
qcfgs = {}
|
||||
qstrs = {}
|
||||
|
||||
# add static qstrs
|
||||
for qstr in static_qstr_list:
|
||||
# work out the corresponding qstr name
|
||||
ident = qstr_escape(qstr)
|
||||
|
||||
# don't add duplicates
|
||||
assert ident not in qstrs
|
||||
|
||||
# add the qstr to the list, with order number to retain original order in file
|
||||
order = len(qstrs) - 300000
|
||||
qstrs[ident] = (order, ident, qstr)
|
||||
|
||||
# read the qstrs in from the input files
|
||||
for infile in infiles:
|
||||
with open(infile, "rt") as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
|
||||
# is this a config line?
|
||||
match = re.match(r"^QCFG\((.+), (.+)\)", line)
|
||||
if match:
|
||||
value = match.group(2)
|
||||
if value[0] == "(" and value[-1] == ")":
|
||||
# strip parenthesis from config value
|
||||
value = value[1:-1]
|
||||
qcfgs[match.group(1)] = value
|
||||
continue
|
||||
|
||||
# is this a QSTR line?
|
||||
match = re.match(r"^Q\((.*)\)$", line)
|
||||
if not match:
|
||||
continue
|
||||
|
||||
# get the qstr value
|
||||
qstr = match.group(1)
|
||||
|
||||
# special cases to specify control characters
|
||||
if qstr == "\\n":
|
||||
qstr = "\n"
|
||||
elif qstr == "\\r\\n":
|
||||
qstr = "\r\n"
|
||||
|
||||
# work out the corresponding qstr name
|
||||
ident = qstr_escape(qstr)
|
||||
|
||||
# don't add duplicates
|
||||
if ident in qstrs:
|
||||
continue
|
||||
|
||||
# add the qstr to the list, with order number to retain original order in file
|
||||
order = len(qstrs)
|
||||
# but put special method names like __add__ at the top of list, so
|
||||
# that their id's fit into a byte
|
||||
if ident == "":
|
||||
# Sort empty qstr above all still
|
||||
order = -200000
|
||||
elif ident == "__dir__":
|
||||
# Put __dir__ after empty qstr for builtin dir() to work
|
||||
order = -190000
|
||||
elif ident.startswith("__"):
|
||||
order -= 100000
|
||||
qstrs[ident] = (order, ident, qstr)
|
||||
|
||||
if not qcfgs:
|
||||
sys.stderr.write("ERROR: Empty preprocessor output - check for errors above\n")
|
||||
sys.exit(1)
|
||||
|
||||
return qcfgs, qstrs
|
||||
|
||||
|
||||
def make_bytes(cfg_bytes_len, cfg_bytes_hash, qstr):
|
||||
qbytes = bytes_cons(qstr, "utf8")
|
||||
qlen = len(qbytes)
|
||||
qhash = compute_hash(qbytes, cfg_bytes_hash)
|
||||
if all(32 <= ord(c) <= 126 and c != "\\" and c != '"' for c in qstr):
|
||||
# qstr is all printable ASCII so render it as-is (for easier debugging)
|
||||
qdata = qstr
|
||||
else:
|
||||
# qstr contains non-printable codes so render entire thing as hex pairs
|
||||
qdata = "".join(("\\x%02x" % b) for b in qbytes)
|
||||
if qlen >= (1 << (8 * cfg_bytes_len)):
|
||||
print("qstr is too long:", qstr)
|
||||
assert False
|
||||
qlen_str = ("\\x%02x" * cfg_bytes_len) % tuple(
|
||||
((qlen >> (8 * i)) & 0xFF) for i in range(cfg_bytes_len)
|
||||
)
|
||||
qhash_str = ("\\x%02x" * cfg_bytes_hash) % tuple(
|
||||
((qhash >> (8 * i)) & 0xFF) for i in range(cfg_bytes_hash)
|
||||
)
|
||||
return '(const byte*)"%s%s" "%s"' % (qhash_str, qlen_str, qdata)
|
||||
|
||||
|
||||
def print_qstr_data(qcfgs, qstrs):
|
||||
# get config variables
|
||||
cfg_bytes_len = int(qcfgs["BYTES_IN_LEN"])
|
||||
cfg_bytes_hash = int(qcfgs["BYTES_IN_HASH"])
|
||||
|
||||
# print out the starter of the generated C header file
|
||||
print("// This file was automatically generated by makeqstrdata.py")
|
||||
print("")
|
||||
|
||||
# add NULL qstr with no hash or data
|
||||
print(
|
||||
'QDEF(MP_QSTRnull, (const byte*)"%s%s" "")'
|
||||
% ("\\x00" * cfg_bytes_hash, "\\x00" * cfg_bytes_len)
|
||||
)
|
||||
|
||||
# go through each qstr and print it out
|
||||
for order, ident, qstr in sorted(qstrs.values(), key=lambda x: x[0]):
|
||||
qbytes = make_bytes(cfg_bytes_len, cfg_bytes_hash, qstr)
|
||||
print("QDEF(MP_QSTR_%s, %s)" % (ident, qbytes))
|
||||
|
||||
|
||||
def do_work(infiles):
|
||||
qcfgs, qstrs = parse_input_headers(infiles)
|
||||
print_qstr_data(qcfgs, qstrs)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
do_work(sys.argv[1:])
|
||||
@@ -0,0 +1,193 @@
|
||||
"""
|
||||
This script processes the output from the C preprocessor and extracts all
|
||||
qstr. Each qstr is transformed into a qstr definition of the form 'Q(...)'.
|
||||
|
||||
This script works with Python 2.6, 2.7, 3.3 and 3.4.
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import io
|
||||
import os
|
||||
|
||||
|
||||
# Extract MP_QSTR_FOO macros.
|
||||
_MODE_QSTR = "qstr"
|
||||
|
||||
# Extract MP_COMPRESSED_ROM_TEXT("") macros. (Which come from MP_ERROR_TEXT)
|
||||
_MODE_COMPRESS = "compress"
|
||||
|
||||
|
||||
def preprocess():
|
||||
if any(src in args.dependencies for src in args.changed_sources):
|
||||
sources = args.sources
|
||||
elif any(args.changed_sources):
|
||||
sources = args.changed_sources
|
||||
else:
|
||||
sources = args.sources
|
||||
csources = []
|
||||
cxxsources = []
|
||||
for source in sources:
|
||||
if source.endswith(".cpp"):
|
||||
cxxsources.append(source)
|
||||
else:
|
||||
csources.append(source)
|
||||
try:
|
||||
os.makedirs(os.path.dirname(args.output[0]))
|
||||
except OSError:
|
||||
pass
|
||||
with open(args.output[0], "w") as out_file:
|
||||
if csources:
|
||||
subprocess.check_call(args.pp + args.cflags + csources, stdout=out_file)
|
||||
if cxxsources:
|
||||
subprocess.check_call(args.pp + args.cxxflags + cxxsources, stdout=out_file)
|
||||
|
||||
|
||||
def write_out(fname, output):
|
||||
if output:
|
||||
for m, r in [("/", "__"), ("\\", "__"), (":", "@"), ("..", "@@")]:
|
||||
fname = fname.replace(m, r)
|
||||
with open(args.output_dir + "/" + fname + "." + args.mode, "w") as f:
|
||||
f.write("\n".join(output) + "\n")
|
||||
|
||||
|
||||
def process_file(f):
|
||||
re_line = re.compile(r"#[line]*\s\d+\s\"([^\"]+)\"")
|
||||
if args.mode == _MODE_QSTR:
|
||||
re_match = re.compile(r"MP_QSTR_[_a-zA-Z0-9]+")
|
||||
elif args.mode == _MODE_COMPRESS:
|
||||
re_match = re.compile(r'MP_COMPRESSED_ROM_TEXT\("([^"]*)"\)')
|
||||
output = []
|
||||
last_fname = None
|
||||
for line in f:
|
||||
if line.isspace():
|
||||
continue
|
||||
# match gcc-like output (# n "file") and msvc-like output (#line n "file")
|
||||
if line.startswith(("# ", "#line")):
|
||||
m = re_line.match(line)
|
||||
assert m is not None
|
||||
fname = m.group(1)
|
||||
if os.path.splitext(fname)[1] not in [".c", ".cpp"]:
|
||||
continue
|
||||
if fname != last_fname:
|
||||
write_out(last_fname, output)
|
||||
output = []
|
||||
last_fname = fname
|
||||
continue
|
||||
for match in re_match.findall(line):
|
||||
if args.mode == _MODE_QSTR:
|
||||
name = match.replace("MP_QSTR_", "")
|
||||
output.append("Q(" + name + ")")
|
||||
elif args.mode == _MODE_COMPRESS:
|
||||
output.append(match)
|
||||
|
||||
if last_fname:
|
||||
write_out(last_fname, output)
|
||||
return ""
|
||||
|
||||
|
||||
def cat_together():
|
||||
import glob
|
||||
import hashlib
|
||||
|
||||
hasher = hashlib.md5()
|
||||
all_lines = []
|
||||
outf = open(args.output_dir + "/out", "wb")
|
||||
for fname in glob.glob(args.output_dir + "/*." + args.mode):
|
||||
with open(fname, "rb") as f:
|
||||
lines = f.readlines()
|
||||
all_lines += lines
|
||||
all_lines.sort()
|
||||
all_lines = b"\n".join(all_lines)
|
||||
outf.write(all_lines)
|
||||
outf.close()
|
||||
hasher.update(all_lines)
|
||||
new_hash = hasher.hexdigest()
|
||||
# print(new_hash)
|
||||
old_hash = None
|
||||
try:
|
||||
with open(args.output_file + ".hash") as f:
|
||||
old_hash = f.read()
|
||||
except IOError:
|
||||
pass
|
||||
mode_full = "QSTR"
|
||||
if args.mode == _MODE_COMPRESS:
|
||||
mode_full = "Compressed data"
|
||||
if old_hash != new_hash:
|
||||
print(mode_full, "updated")
|
||||
try:
|
||||
# rename below might fail if file exists
|
||||
os.remove(args.output_file)
|
||||
except:
|
||||
pass
|
||||
os.rename(args.output_dir + "/out", args.output_file)
|
||||
with open(args.output_file + ".hash", "w") as f:
|
||||
f.write(new_hash)
|
||||
else:
|
||||
print(mode_full, "not updated")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) < 6:
|
||||
print("usage: %s command mode input_filename output_dir output_file" % sys.argv[0])
|
||||
sys.exit(2)
|
||||
|
||||
class Args:
|
||||
pass
|
||||
|
||||
args = Args()
|
||||
args.command = sys.argv[1]
|
||||
|
||||
if args.command == "pp":
|
||||
named_args = {
|
||||
s: []
|
||||
for s in [
|
||||
"pp",
|
||||
"output",
|
||||
"cflags",
|
||||
"cxxflags",
|
||||
"sources",
|
||||
"changed_sources",
|
||||
"dependencies",
|
||||
]
|
||||
}
|
||||
|
||||
for arg in sys.argv[1:]:
|
||||
if arg in named_args:
|
||||
current_tok = arg
|
||||
else:
|
||||
named_args[current_tok].append(arg)
|
||||
|
||||
if not named_args["pp"] or len(named_args["output"]) != 1:
|
||||
print("usage: %s %s ..." % (sys.argv[0], " ... ".join(named_args)))
|
||||
sys.exit(2)
|
||||
|
||||
for k, v in named_args.items():
|
||||
setattr(args, k, v)
|
||||
|
||||
preprocess()
|
||||
sys.exit(0)
|
||||
|
||||
args.mode = sys.argv[2]
|
||||
args.input_filename = sys.argv[3] # Unused for command=cat
|
||||
args.output_dir = sys.argv[4]
|
||||
args.output_file = None if len(sys.argv) == 5 else sys.argv[5] # Unused for command=split
|
||||
|
||||
if args.mode not in (_MODE_QSTR, _MODE_COMPRESS):
|
||||
print("error: mode %s unrecognised" % sys.argv[2])
|
||||
sys.exit(2)
|
||||
|
||||
try:
|
||||
os.makedirs(args.output_dir)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
if args.command == "split":
|
||||
with io.open(args.input_filename, encoding="utf-8") as infile:
|
||||
process_file(infile)
|
||||
|
||||
if args.command == "cat":
|
||||
cat_together()
|
||||
@@ -0,0 +1,111 @@
|
||||
"""
|
||||
Generate header file with macros defining MicroPython version info.
|
||||
|
||||
This script works with Python 2.6, 2.7, 3.3 and 3.4.
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import sys
|
||||
import os
|
||||
import datetime
|
||||
import subprocess
|
||||
|
||||
|
||||
def get_version_info_from_git():
|
||||
# Python 2.6 doesn't have check_output, so check for that
|
||||
try:
|
||||
subprocess.check_output
|
||||
subprocess.check_call
|
||||
except AttributeError:
|
||||
return None
|
||||
|
||||
# Note: git describe doesn't work if no tag is available
|
||||
try:
|
||||
git_tag = subprocess.check_output(
|
||||
["git", "describe", "--dirty", "--always", "--match", "v[1-9].*"],
|
||||
stderr=subprocess.STDOUT,
|
||||
universal_newlines=True,
|
||||
).strip()
|
||||
except subprocess.CalledProcessError as er:
|
||||
if er.returncode == 128:
|
||||
# git exit code of 128 means no repository found
|
||||
return None
|
||||
git_tag = ""
|
||||
except OSError:
|
||||
return None
|
||||
try:
|
||||
git_hash = subprocess.check_output(
|
||||
["git", "rev-parse", "--short", "HEAD"],
|
||||
stderr=subprocess.STDOUT,
|
||||
universal_newlines=True,
|
||||
).strip()
|
||||
except subprocess.CalledProcessError:
|
||||
git_hash = "unknown"
|
||||
except OSError:
|
||||
return None
|
||||
|
||||
try:
|
||||
# Check if there are any modified files.
|
||||
subprocess.check_call(
|
||||
["git", "diff", "--no-ext-diff", "--quiet", "--exit-code"], stderr=subprocess.STDOUT
|
||||
)
|
||||
# Check if there are any staged files.
|
||||
subprocess.check_call(
|
||||
["git", "diff-index", "--cached", "--quiet", "HEAD", "--"], stderr=subprocess.STDOUT
|
||||
)
|
||||
except subprocess.CalledProcessError:
|
||||
git_hash += "-dirty"
|
||||
except OSError:
|
||||
return None
|
||||
|
||||
return git_tag, git_hash
|
||||
|
||||
|
||||
def get_version_info_from_docs_conf():
|
||||
with open(os.path.join(os.path.dirname(sys.argv[0]), "..", "docs", "conf.py")) as f:
|
||||
for line in f:
|
||||
if line.startswith("version = release = '"):
|
||||
ver = line.strip().split(" = ")[2].strip("'")
|
||||
git_tag = "v" + ver
|
||||
return git_tag, "<no hash>"
|
||||
return None
|
||||
|
||||
|
||||
def make_version_header(filename):
|
||||
# Get version info using git, with fallback to docs/conf.py
|
||||
info = get_version_info_from_git()
|
||||
if info is None:
|
||||
info = get_version_info_from_docs_conf()
|
||||
|
||||
git_tag, git_hash = info
|
||||
|
||||
# Generate the file with the git and version info
|
||||
file_data = """\
|
||||
// This file was generated by py/makeversionhdr.py
|
||||
#define MICROPY_GIT_TAG "%s"
|
||||
#define MICROPY_GIT_HASH "%s"
|
||||
#define MICROPY_BUILD_DATE "%s"
|
||||
""" % (
|
||||
git_tag,
|
||||
git_hash,
|
||||
datetime.date.today().strftime("%Y-%m-%d"),
|
||||
)
|
||||
|
||||
# Check if the file contents changed from last time
|
||||
write_file = True
|
||||
if os.path.isfile(filename):
|
||||
with open(filename, "r") as f:
|
||||
existing_data = f.read()
|
||||
if existing_data == file_data:
|
||||
write_file = False
|
||||
|
||||
# Only write the file if we need to
|
||||
if write_file:
|
||||
print("GEN %s" % filename)
|
||||
with open(filename, "w") as f:
|
||||
f.write(file_data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
make_version_header(sys.argv[1])
|
||||
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013, 2014 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "py/mpconfig.h"
|
||||
#include "py/misc.h"
|
||||
#include "py/mpstate.h"
|
||||
|
||||
#if MICROPY_DEBUG_VERBOSE // print debugging info
|
||||
#define DEBUG_printf DEBUG_printf
|
||||
#else // don't print debugging info
|
||||
#define DEBUG_printf(...) (void)0
|
||||
#endif
|
||||
|
||||
#if MICROPY_MEM_STATS
|
||||
#if !MICROPY_MALLOC_USES_ALLOCATED_SIZE
|
||||
#error MICROPY_MEM_STATS requires MICROPY_MALLOC_USES_ALLOCATED_SIZE
|
||||
#endif
|
||||
#define UPDATE_PEAK() { if (MP_STATE_MEM(current_bytes_allocated) > MP_STATE_MEM(peak_bytes_allocated)) MP_STATE_MEM(peak_bytes_allocated) = MP_STATE_MEM(current_bytes_allocated); }
|
||||
#endif
|
||||
|
||||
#if MICROPY_ENABLE_GC
|
||||
#include "py/gc.h"
|
||||
|
||||
// We redirect standard alloc functions to GC heap - just for the rest of
|
||||
// this module. In the rest of MicroPython source, system malloc can be
|
||||
// freely accessed - for interfacing with system and 3rd-party libs for
|
||||
// example. On the other hand, some (e.g. bare-metal) ports may use GC
|
||||
// heap as system heap, so, to avoid warnings, we do undef's first.
|
||||
#undef malloc
|
||||
#undef free
|
||||
#undef realloc
|
||||
#define malloc(b) gc_alloc((b), false)
|
||||
#define malloc_with_finaliser(b) gc_alloc((b), true)
|
||||
#define free gc_free
|
||||
#define realloc(ptr, n) gc_realloc(ptr, n, true)
|
||||
#define realloc_ext(ptr, n, mv) gc_realloc(ptr, n, mv)
|
||||
#else
|
||||
|
||||
// GC is disabled. Use system malloc/realloc/free.
|
||||
|
||||
#if MICROPY_ENABLE_FINALISER
|
||||
#error MICROPY_ENABLE_FINALISER requires MICROPY_ENABLE_GC
|
||||
#endif
|
||||
|
||||
STATIC void *realloc_ext(void *ptr, size_t n_bytes, bool allow_move) {
|
||||
if (allow_move) {
|
||||
return realloc(ptr, n_bytes);
|
||||
} else {
|
||||
// We are asked to resize, but without moving the memory region pointed to
|
||||
// by ptr. Unless the underlying memory manager has special provision for
|
||||
// this behaviour there is nothing we can do except fail to resize.
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // MICROPY_ENABLE_GC
|
||||
|
||||
void *m_malloc(size_t num_bytes) {
|
||||
void *ptr = malloc(num_bytes);
|
||||
if (ptr == NULL && num_bytes != 0) {
|
||||
m_malloc_fail(num_bytes);
|
||||
}
|
||||
#if MICROPY_MEM_STATS
|
||||
MP_STATE_MEM(total_bytes_allocated) += num_bytes;
|
||||
MP_STATE_MEM(current_bytes_allocated) += num_bytes;
|
||||
UPDATE_PEAK();
|
||||
#endif
|
||||
DEBUG_printf("malloc %d : %p\n", num_bytes, ptr);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void *m_malloc_maybe(size_t num_bytes) {
|
||||
void *ptr = malloc(num_bytes);
|
||||
#if MICROPY_MEM_STATS
|
||||
MP_STATE_MEM(total_bytes_allocated) += num_bytes;
|
||||
MP_STATE_MEM(current_bytes_allocated) += num_bytes;
|
||||
UPDATE_PEAK();
|
||||
#endif
|
||||
DEBUG_printf("malloc %d : %p\n", num_bytes, ptr);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
#if MICROPY_ENABLE_FINALISER
|
||||
void *m_malloc_with_finaliser(size_t num_bytes) {
|
||||
void *ptr = malloc_with_finaliser(num_bytes);
|
||||
if (ptr == NULL && num_bytes != 0) {
|
||||
m_malloc_fail(num_bytes);
|
||||
}
|
||||
#if MICROPY_MEM_STATS
|
||||
MP_STATE_MEM(total_bytes_allocated) += num_bytes;
|
||||
MP_STATE_MEM(current_bytes_allocated) += num_bytes;
|
||||
UPDATE_PEAK();
|
||||
#endif
|
||||
DEBUG_printf("malloc %d : %p\n", num_bytes, ptr);
|
||||
return ptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
void *m_malloc0(size_t num_bytes) {
|
||||
void *ptr = m_malloc(num_bytes);
|
||||
// If this config is set then the GC clears all memory, so we don't need to.
|
||||
#if !MICROPY_GC_CONSERVATIVE_CLEAR
|
||||
memset(ptr, 0, num_bytes);
|
||||
#endif
|
||||
return ptr;
|
||||
}
|
||||
|
||||
#if MICROPY_MALLOC_USES_ALLOCATED_SIZE
|
||||
void *m_realloc(void *ptr, size_t old_num_bytes, size_t new_num_bytes)
|
||||
#else
|
||||
void *m_realloc(void *ptr, size_t new_num_bytes)
|
||||
#endif
|
||||
{
|
||||
void *new_ptr = realloc(ptr, new_num_bytes);
|
||||
if (new_ptr == NULL && new_num_bytes != 0) {
|
||||
m_malloc_fail(new_num_bytes);
|
||||
}
|
||||
#if MICROPY_MEM_STATS
|
||||
// At first thought, "Total bytes allocated" should only grow,
|
||||
// after all, it's *total*. But consider for example 2K block
|
||||
// shrunk to 1K and then grown to 2K again. It's still 2K
|
||||
// allocated total. If we process only positive increments,
|
||||
// we'll count 3K.
|
||||
size_t diff = new_num_bytes - old_num_bytes;
|
||||
MP_STATE_MEM(total_bytes_allocated) += diff;
|
||||
MP_STATE_MEM(current_bytes_allocated) += diff;
|
||||
UPDATE_PEAK();
|
||||
#endif
|
||||
#if MICROPY_MALLOC_USES_ALLOCATED_SIZE
|
||||
DEBUG_printf("realloc %p, %d, %d : %p\n", ptr, old_num_bytes, new_num_bytes, new_ptr);
|
||||
#else
|
||||
DEBUG_printf("realloc %p, %d : %p\n", ptr, new_num_bytes, new_ptr);
|
||||
#endif
|
||||
return new_ptr;
|
||||
}
|
||||
|
||||
#if MICROPY_MALLOC_USES_ALLOCATED_SIZE
|
||||
void *m_realloc_maybe(void *ptr, size_t old_num_bytes, size_t new_num_bytes, bool allow_move)
|
||||
#else
|
||||
void *m_realloc_maybe(void *ptr, size_t new_num_bytes, bool allow_move)
|
||||
#endif
|
||||
{
|
||||
void *new_ptr = realloc_ext(ptr, new_num_bytes, allow_move);
|
||||
#if MICROPY_MEM_STATS
|
||||
// At first thought, "Total bytes allocated" should only grow,
|
||||
// after all, it's *total*. But consider for example 2K block
|
||||
// shrunk to 1K and then grown to 2K again. It's still 2K
|
||||
// allocated total. If we process only positive increments,
|
||||
// we'll count 3K.
|
||||
// Also, don't count failed reallocs.
|
||||
if (!(new_ptr == NULL && new_num_bytes != 0)) {
|
||||
size_t diff = new_num_bytes - old_num_bytes;
|
||||
MP_STATE_MEM(total_bytes_allocated) += diff;
|
||||
MP_STATE_MEM(current_bytes_allocated) += diff;
|
||||
UPDATE_PEAK();
|
||||
}
|
||||
#endif
|
||||
#if MICROPY_MALLOC_USES_ALLOCATED_SIZE
|
||||
DEBUG_printf("realloc %p, %d, %d : %p\n", ptr, old_num_bytes, new_num_bytes, new_ptr);
|
||||
#else
|
||||
DEBUG_printf("realloc %p, %d, %d : %p\n", ptr, new_num_bytes, new_ptr);
|
||||
#endif
|
||||
return new_ptr;
|
||||
}
|
||||
|
||||
#if MICROPY_MALLOC_USES_ALLOCATED_SIZE
|
||||
void m_free(void *ptr, size_t num_bytes)
|
||||
#else
|
||||
void m_free(void *ptr)
|
||||
#endif
|
||||
{
|
||||
free(ptr);
|
||||
#if MICROPY_MEM_STATS
|
||||
MP_STATE_MEM(current_bytes_allocated) -= num_bytes;
|
||||
#endif
|
||||
#if MICROPY_MALLOC_USES_ALLOCATED_SIZE
|
||||
DEBUG_printf("free %p, %d\n", ptr, num_bytes);
|
||||
#else
|
||||
DEBUG_printf("free %p\n", ptr);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if MICROPY_MEM_STATS
|
||||
size_t m_get_total_bytes_allocated(void) {
|
||||
return MP_STATE_MEM(total_bytes_allocated);
|
||||
}
|
||||
|
||||
size_t m_get_current_bytes_allocated(void) {
|
||||
return MP_STATE_MEM(current_bytes_allocated);
|
||||
}
|
||||
|
||||
size_t m_get_peak_bytes_allocated(void) {
|
||||
return MP_STATE_MEM(peak_bytes_allocated);
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,425 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013, 2014 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "py/mpconfig.h"
|
||||
#include "py/misc.h"
|
||||
#include "py/runtime.h"
|
||||
|
||||
#if MICROPY_DEBUG_VERBOSE // print debugging info
|
||||
#define DEBUG_PRINT (1)
|
||||
#else // don't print debugging info
|
||||
#define DEBUG_PRINT (0)
|
||||
#define DEBUG_printf(...) (void)0
|
||||
#endif
|
||||
|
||||
// This table of sizes is used to control the growth of hash tables.
|
||||
// The first set of sizes are chosen so the allocation fits exactly in a
|
||||
// 4-word GC block, and it's not so important for these small values to be
|
||||
// prime. The latter sizes are prime and increase at an increasing rate.
|
||||
STATIC const uint16_t hash_allocation_sizes[] = {
|
||||
0, 2, 4, 6, 8, 10, 12, // +2
|
||||
17, 23, 29, 37, 47, 59, 73, // *1.25
|
||||
97, 127, 167, 223, 293, 389, 521, 691, 919, 1223, 1627, 2161, // *1.33
|
||||
3229, 4831, 7243, 10861, 16273, 24407, 36607, 54907, // *1.5
|
||||
};
|
||||
|
||||
STATIC size_t get_hash_alloc_greater_or_equal_to(size_t x) {
|
||||
for (size_t i = 0; i < MP_ARRAY_SIZE(hash_allocation_sizes); i++) {
|
||||
if (hash_allocation_sizes[i] >= x) {
|
||||
return hash_allocation_sizes[i];
|
||||
}
|
||||
}
|
||||
// ran out of primes in the table!
|
||||
// return something sensible, at least make it odd
|
||||
return (x + x / 2) | 1;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/* map */
|
||||
|
||||
void mp_map_init(mp_map_t *map, size_t n) {
|
||||
if (n == 0) {
|
||||
map->alloc = 0;
|
||||
map->table = NULL;
|
||||
} else {
|
||||
map->alloc = n;
|
||||
map->table = m_new0(mp_map_elem_t, map->alloc);
|
||||
}
|
||||
map->used = 0;
|
||||
map->all_keys_are_qstrs = 1;
|
||||
map->is_fixed = 0;
|
||||
map->is_ordered = 0;
|
||||
}
|
||||
|
||||
void mp_map_init_fixed_table(mp_map_t *map, size_t n, const mp_obj_t *table) {
|
||||
map->alloc = n;
|
||||
map->used = n;
|
||||
map->all_keys_are_qstrs = 1;
|
||||
map->is_fixed = 1;
|
||||
map->is_ordered = 1;
|
||||
map->table = (mp_map_elem_t *)table;
|
||||
}
|
||||
|
||||
// Differentiate from mp_map_clear() - semantics is different
|
||||
void mp_map_deinit(mp_map_t *map) {
|
||||
if (!map->is_fixed) {
|
||||
m_del(mp_map_elem_t, map->table, map->alloc);
|
||||
}
|
||||
map->used = map->alloc = 0;
|
||||
}
|
||||
|
||||
void mp_map_clear(mp_map_t *map) {
|
||||
if (!map->is_fixed) {
|
||||
m_del(mp_map_elem_t, map->table, map->alloc);
|
||||
}
|
||||
map->alloc = 0;
|
||||
map->used = 0;
|
||||
map->all_keys_are_qstrs = 1;
|
||||
map->is_fixed = 0;
|
||||
map->table = NULL;
|
||||
}
|
||||
|
||||
STATIC void mp_map_rehash(mp_map_t *map) {
|
||||
size_t old_alloc = map->alloc;
|
||||
size_t new_alloc = get_hash_alloc_greater_or_equal_to(map->alloc + 1);
|
||||
DEBUG_printf("mp_map_rehash(%p): " UINT_FMT " -> " UINT_FMT "\n", map, old_alloc, new_alloc);
|
||||
mp_map_elem_t *old_table = map->table;
|
||||
mp_map_elem_t *new_table = m_new0(mp_map_elem_t, new_alloc);
|
||||
// If we reach this point, table resizing succeeded, now we can edit the old map.
|
||||
map->alloc = new_alloc;
|
||||
map->used = 0;
|
||||
map->all_keys_are_qstrs = 1;
|
||||
map->table = new_table;
|
||||
for (size_t i = 0; i < old_alloc; i++) {
|
||||
if (old_table[i].key != MP_OBJ_NULL && old_table[i].key != MP_OBJ_SENTINEL) {
|
||||
mp_map_lookup(map, old_table[i].key, MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)->value = old_table[i].value;
|
||||
}
|
||||
}
|
||||
m_del(mp_map_elem_t, old_table, old_alloc);
|
||||
}
|
||||
|
||||
// MP_MAP_LOOKUP behaviour:
|
||||
// - returns NULL if not found, else the slot it was found in with key,value non-null
|
||||
// MP_MAP_LOOKUP_ADD_IF_NOT_FOUND behaviour:
|
||||
// - returns slot, with key non-null and value=MP_OBJ_NULL if it was added
|
||||
// MP_MAP_LOOKUP_REMOVE_IF_FOUND behaviour:
|
||||
// - returns NULL if not found, else the slot if was found in with key null and value non-null
|
||||
mp_map_elem_t *mp_map_lookup(mp_map_t *map, mp_obj_t index, mp_map_lookup_kind_t lookup_kind) {
|
||||
// If the map is a fixed array then we must only be called for a lookup
|
||||
assert(!map->is_fixed || lookup_kind == MP_MAP_LOOKUP);
|
||||
|
||||
// Work out if we can compare just pointers
|
||||
bool compare_only_ptrs = map->all_keys_are_qstrs;
|
||||
if (compare_only_ptrs) {
|
||||
if (mp_obj_is_qstr(index)) {
|
||||
// Index is a qstr, so can just do ptr comparison.
|
||||
} else if (mp_obj_is_type(index, &mp_type_str)) {
|
||||
// Index is a non-interned string.
|
||||
// We can either intern the string, or force a full equality comparison.
|
||||
// We chose the latter, since interning costs time and potentially RAM,
|
||||
// and it won't necessarily benefit subsequent calls because these calls
|
||||
// most likely won't pass the newly-interned string.
|
||||
compare_only_ptrs = false;
|
||||
} else if (lookup_kind != MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) {
|
||||
// If we are not adding, then we can return straight away a failed
|
||||
// lookup because we know that the index will never be found.
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// if the map is an ordered array then we must do a brute force linear search
|
||||
if (map->is_ordered) {
|
||||
for (mp_map_elem_t *elem = &map->table[0], *top = &map->table[map->used]; elem < top; elem++) {
|
||||
if (elem->key == index || (!compare_only_ptrs && mp_obj_equal(elem->key, index))) {
|
||||
#if MICROPY_PY_COLLECTIONS_ORDEREDDICT
|
||||
if (MP_UNLIKELY(lookup_kind == MP_MAP_LOOKUP_REMOVE_IF_FOUND)) {
|
||||
// remove the found element by moving the rest of the array down
|
||||
mp_obj_t value = elem->value;
|
||||
--map->used;
|
||||
memmove(elem, elem + 1, (top - elem - 1) * sizeof(*elem));
|
||||
// put the found element after the end so the caller can access it if needed
|
||||
// note: caller must NULL the value so the GC can clean up (e.g. see dict_get_helper).
|
||||
elem = &map->table[map->used];
|
||||
elem->key = MP_OBJ_NULL;
|
||||
elem->value = value;
|
||||
}
|
||||
#endif
|
||||
return elem;
|
||||
}
|
||||
}
|
||||
#if MICROPY_PY_COLLECTIONS_ORDEREDDICT
|
||||
if (MP_LIKELY(lookup_kind != MP_MAP_LOOKUP_ADD_IF_NOT_FOUND)) {
|
||||
return NULL;
|
||||
}
|
||||
if (map->used == map->alloc) {
|
||||
// TODO: Alloc policy
|
||||
map->alloc += 4;
|
||||
map->table = m_renew(mp_map_elem_t, map->table, map->used, map->alloc);
|
||||
mp_seq_clear(map->table, map->used, map->alloc, sizeof(*map->table));
|
||||
}
|
||||
mp_map_elem_t *elem = map->table + map->used++;
|
||||
elem->key = index;
|
||||
if (!mp_obj_is_qstr(index)) {
|
||||
map->all_keys_are_qstrs = 0;
|
||||
}
|
||||
return elem;
|
||||
#else
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
// map is a hash table (not an ordered array), so do a hash lookup
|
||||
|
||||
if (map->alloc == 0) {
|
||||
if (lookup_kind == MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) {
|
||||
mp_map_rehash(map);
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// get hash of index, with fast path for common case of qstr
|
||||
mp_uint_t hash;
|
||||
if (mp_obj_is_qstr(index)) {
|
||||
hash = qstr_hash(MP_OBJ_QSTR_VALUE(index));
|
||||
} else {
|
||||
hash = MP_OBJ_SMALL_INT_VALUE(mp_unary_op(MP_UNARY_OP_HASH, index));
|
||||
}
|
||||
|
||||
size_t pos = hash % map->alloc;
|
||||
size_t start_pos = pos;
|
||||
mp_map_elem_t *avail_slot = NULL;
|
||||
for (;;) {
|
||||
mp_map_elem_t *slot = &map->table[pos];
|
||||
if (slot->key == MP_OBJ_NULL) {
|
||||
// found NULL slot, so index is not in table
|
||||
if (lookup_kind == MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) {
|
||||
map->used += 1;
|
||||
if (avail_slot == NULL) {
|
||||
avail_slot = slot;
|
||||
}
|
||||
avail_slot->key = index;
|
||||
avail_slot->value = MP_OBJ_NULL;
|
||||
if (!mp_obj_is_qstr(index)) {
|
||||
map->all_keys_are_qstrs = 0;
|
||||
}
|
||||
return avail_slot;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
} else if (slot->key == MP_OBJ_SENTINEL) {
|
||||
// found deleted slot, remember for later
|
||||
if (avail_slot == NULL) {
|
||||
avail_slot = slot;
|
||||
}
|
||||
} else if (slot->key == index || (!compare_only_ptrs && mp_obj_equal(slot->key, index))) {
|
||||
// found index
|
||||
// Note: CPython does not replace the index; try x={True:'true'};x[1]='one';x
|
||||
if (lookup_kind == MP_MAP_LOOKUP_REMOVE_IF_FOUND) {
|
||||
// delete element in this slot
|
||||
map->used--;
|
||||
if (map->table[(pos + 1) % map->alloc].key == MP_OBJ_NULL) {
|
||||
// optimisation if next slot is empty
|
||||
slot->key = MP_OBJ_NULL;
|
||||
} else {
|
||||
slot->key = MP_OBJ_SENTINEL;
|
||||
}
|
||||
// keep slot->value so that caller can access it if needed
|
||||
}
|
||||
return slot;
|
||||
}
|
||||
|
||||
// not yet found, keep searching in this table
|
||||
pos = (pos + 1) % map->alloc;
|
||||
|
||||
if (pos == start_pos) {
|
||||
// search got back to starting position, so index is not in table
|
||||
if (lookup_kind == MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) {
|
||||
if (avail_slot != NULL) {
|
||||
// there was an available slot, so use that
|
||||
map->used++;
|
||||
avail_slot->key = index;
|
||||
avail_slot->value = MP_OBJ_NULL;
|
||||
if (!mp_obj_is_qstr(index)) {
|
||||
map->all_keys_are_qstrs = 0;
|
||||
}
|
||||
return avail_slot;
|
||||
} else {
|
||||
// not enough room in table, rehash it
|
||||
mp_map_rehash(map);
|
||||
// restart the search for the new element
|
||||
start_pos = pos = hash % map->alloc;
|
||||
}
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/* set */
|
||||
|
||||
#if MICROPY_PY_BUILTINS_SET
|
||||
|
||||
void mp_set_init(mp_set_t *set, size_t n) {
|
||||
set->alloc = n;
|
||||
set->used = 0;
|
||||
set->table = m_new0(mp_obj_t, set->alloc);
|
||||
}
|
||||
|
||||
STATIC void mp_set_rehash(mp_set_t *set) {
|
||||
size_t old_alloc = set->alloc;
|
||||
mp_obj_t *old_table = set->table;
|
||||
set->alloc = get_hash_alloc_greater_or_equal_to(set->alloc + 1);
|
||||
set->used = 0;
|
||||
set->table = m_new0(mp_obj_t, set->alloc);
|
||||
for (size_t i = 0; i < old_alloc; i++) {
|
||||
if (old_table[i] != MP_OBJ_NULL && old_table[i] != MP_OBJ_SENTINEL) {
|
||||
mp_set_lookup(set, old_table[i], MP_MAP_LOOKUP_ADD_IF_NOT_FOUND);
|
||||
}
|
||||
}
|
||||
m_del(mp_obj_t, old_table, old_alloc);
|
||||
}
|
||||
|
||||
mp_obj_t mp_set_lookup(mp_set_t *set, mp_obj_t index, mp_map_lookup_kind_t lookup_kind) {
|
||||
// Note: lookup_kind can be MP_MAP_LOOKUP_ADD_IF_NOT_FOUND_OR_REMOVE_IF_FOUND which
|
||||
// is handled by using bitwise operations.
|
||||
|
||||
if (set->alloc == 0) {
|
||||
if (lookup_kind & MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) {
|
||||
mp_set_rehash(set);
|
||||
} else {
|
||||
return MP_OBJ_NULL;
|
||||
}
|
||||
}
|
||||
mp_uint_t hash = MP_OBJ_SMALL_INT_VALUE(mp_unary_op(MP_UNARY_OP_HASH, index));
|
||||
size_t pos = hash % set->alloc;
|
||||
size_t start_pos = pos;
|
||||
mp_obj_t *avail_slot = NULL;
|
||||
for (;;) {
|
||||
mp_obj_t elem = set->table[pos];
|
||||
if (elem == MP_OBJ_NULL) {
|
||||
// found NULL slot, so index is not in table
|
||||
if (lookup_kind & MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) {
|
||||
if (avail_slot == NULL) {
|
||||
avail_slot = &set->table[pos];
|
||||
}
|
||||
set->used++;
|
||||
*avail_slot = index;
|
||||
return index;
|
||||
} else {
|
||||
return MP_OBJ_NULL;
|
||||
}
|
||||
} else if (elem == MP_OBJ_SENTINEL) {
|
||||
// found deleted slot, remember for later
|
||||
if (avail_slot == NULL) {
|
||||
avail_slot = &set->table[pos];
|
||||
}
|
||||
} else if (mp_obj_equal(elem, index)) {
|
||||
// found index
|
||||
if (lookup_kind & MP_MAP_LOOKUP_REMOVE_IF_FOUND) {
|
||||
// delete element
|
||||
set->used--;
|
||||
if (set->table[(pos + 1) % set->alloc] == MP_OBJ_NULL) {
|
||||
// optimisation if next slot is empty
|
||||
set->table[pos] = MP_OBJ_NULL;
|
||||
} else {
|
||||
set->table[pos] = MP_OBJ_SENTINEL;
|
||||
}
|
||||
}
|
||||
return elem;
|
||||
}
|
||||
|
||||
// not yet found, keep searching in this table
|
||||
pos = (pos + 1) % set->alloc;
|
||||
|
||||
if (pos == start_pos) {
|
||||
// search got back to starting position, so index is not in table
|
||||
if (lookup_kind & MP_MAP_LOOKUP_ADD_IF_NOT_FOUND) {
|
||||
if (avail_slot != NULL) {
|
||||
// there was an available slot, so use that
|
||||
set->used++;
|
||||
*avail_slot = index;
|
||||
return index;
|
||||
} else {
|
||||
// not enough room in table, rehash it
|
||||
mp_set_rehash(set);
|
||||
// restart the search for the new element
|
||||
start_pos = pos = hash % set->alloc;
|
||||
}
|
||||
} else {
|
||||
return MP_OBJ_NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mp_obj_t mp_set_remove_first(mp_set_t *set) {
|
||||
for (size_t pos = 0; pos < set->alloc; pos++) {
|
||||
if (mp_set_slot_is_filled(set, pos)) {
|
||||
mp_obj_t elem = set->table[pos];
|
||||
// delete element
|
||||
set->used--;
|
||||
if (set->table[(pos + 1) % set->alloc] == MP_OBJ_NULL) {
|
||||
// optimisation if next slot is empty
|
||||
set->table[pos] = MP_OBJ_NULL;
|
||||
} else {
|
||||
set->table[pos] = MP_OBJ_SENTINEL;
|
||||
}
|
||||
return elem;
|
||||
}
|
||||
}
|
||||
return MP_OBJ_NULL;
|
||||
}
|
||||
|
||||
void mp_set_clear(mp_set_t *set) {
|
||||
m_del(mp_obj_t, set->table, set->alloc);
|
||||
set->alloc = 0;
|
||||
set->used = 0;
|
||||
set->table = NULL;
|
||||
}
|
||||
|
||||
#endif // MICROPY_PY_BUILTINS_SET
|
||||
|
||||
#if defined(DEBUG_PRINT) && DEBUG_PRINT
|
||||
void mp_map_dump(mp_map_t *map) {
|
||||
for (size_t i = 0; i < map->alloc; i++) {
|
||||
if (map->table[i].key != MP_OBJ_NULL) {
|
||||
mp_obj_print(map->table[i].key, PRINT_REPR);
|
||||
} else {
|
||||
DEBUG_printf("(nil)");
|
||||
}
|
||||
DEBUG_printf(": %p\n", map->table[i].value);
|
||||
}
|
||||
DEBUG_printf("---\n");
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,315 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013, 2014 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef MICROPY_INCLUDED_PY_MISC_H
|
||||
#define MICROPY_INCLUDED_PY_MISC_H
|
||||
|
||||
// a mini library of useful types and functions
|
||||
|
||||
/** types *******************************************************/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
typedef unsigned char byte;
|
||||
typedef unsigned int uint;
|
||||
|
||||
/** generic ops *************************************************/
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(x, y) ((x) < (y) ? (x) : (y))
|
||||
#endif
|
||||
#ifndef MAX
|
||||
#define MAX(x, y) ((x) > (y) ? (x) : (y))
|
||||
#endif
|
||||
|
||||
// Classical double-indirection stringification of preprocessor macro's value
|
||||
#define MP_STRINGIFY_HELPER(x) #x
|
||||
#define MP_STRINGIFY(x) MP_STRINGIFY_HELPER(x)
|
||||
|
||||
// Static assertion macro
|
||||
#define MP_STATIC_ASSERT(cond) ((void)sizeof(char[1 - 2 * !(cond)]))
|
||||
|
||||
/** memory allocation ******************************************/
|
||||
|
||||
// TODO make a lazy m_renew that can increase by a smaller amount than requested (but by at least 1 more element)
|
||||
|
||||
#define m_new(type, num) ((type *)(m_malloc(sizeof(type) * (num))))
|
||||
#define m_new_maybe(type, num) ((type *)(m_malloc_maybe(sizeof(type) * (num))))
|
||||
#define m_new0(type, num) ((type *)(m_malloc0(sizeof(type) * (num))))
|
||||
#define m_new_obj(type) (m_new(type, 1))
|
||||
#define m_new_obj_maybe(type) (m_new_maybe(type, 1))
|
||||
#define m_new_obj_var(obj_type, var_type, var_num) ((obj_type *)m_malloc(sizeof(obj_type) + sizeof(var_type) * (var_num)))
|
||||
#define m_new_obj_var_maybe(obj_type, var_type, var_num) ((obj_type *)m_malloc_maybe(sizeof(obj_type) + sizeof(var_type) * (var_num)))
|
||||
#if MICROPY_ENABLE_FINALISER
|
||||
#define m_new_obj_with_finaliser(type) ((type *)(m_malloc_with_finaliser(sizeof(type))))
|
||||
#define m_new_obj_var_with_finaliser(type, var_type, var_num) ((type *)m_malloc_with_finaliser(sizeof(type) + sizeof(var_type) * (var_num)))
|
||||
#else
|
||||
#define m_new_obj_with_finaliser(type) m_new_obj(type)
|
||||
#define m_new_obj_var_with_finaliser(type, var_type, var_num) m_new_obj_var(type, var_type, var_num)
|
||||
#endif
|
||||
#if MICROPY_MALLOC_USES_ALLOCATED_SIZE
|
||||
#define m_renew(type, ptr, old_num, new_num) ((type *)(m_realloc((ptr), sizeof(type) * (old_num), sizeof(type) * (new_num))))
|
||||
#define m_renew_maybe(type, ptr, old_num, new_num, allow_move) ((type *)(m_realloc_maybe((ptr), sizeof(type) * (old_num), sizeof(type) * (new_num), (allow_move))))
|
||||
#define m_del(type, ptr, num) m_free(ptr, sizeof(type) * (num))
|
||||
#define m_del_var(obj_type, var_type, var_num, ptr) (m_free(ptr, sizeof(obj_type) + sizeof(var_type) * (var_num)))
|
||||
#else
|
||||
#define m_renew(type, ptr, old_num, new_num) ((type *)(m_realloc((ptr), sizeof(type) * (new_num))))
|
||||
#define m_renew_maybe(type, ptr, old_num, new_num, allow_move) ((type *)(m_realloc_maybe((ptr), sizeof(type) * (new_num), (allow_move))))
|
||||
#define m_del(type, ptr, num) ((void)(num), m_free(ptr))
|
||||
#define m_del_var(obj_type, var_type, var_num, ptr) ((void)(var_num), m_free(ptr))
|
||||
#endif
|
||||
#define m_del_obj(type, ptr) (m_del(type, ptr, 1))
|
||||
|
||||
void *m_malloc(size_t num_bytes);
|
||||
void *m_malloc_maybe(size_t num_bytes);
|
||||
void *m_malloc_with_finaliser(size_t num_bytes);
|
||||
void *m_malloc0(size_t num_bytes);
|
||||
#if MICROPY_MALLOC_USES_ALLOCATED_SIZE
|
||||
void *m_realloc(void *ptr, size_t old_num_bytes, size_t new_num_bytes);
|
||||
void *m_realloc_maybe(void *ptr, size_t old_num_bytes, size_t new_num_bytes, bool allow_move);
|
||||
void m_free(void *ptr, size_t num_bytes);
|
||||
#else
|
||||
void *m_realloc(void *ptr, size_t new_num_bytes);
|
||||
void *m_realloc_maybe(void *ptr, size_t new_num_bytes, bool allow_move);
|
||||
void m_free(void *ptr);
|
||||
#endif
|
||||
NORETURN void m_malloc_fail(size_t num_bytes);
|
||||
|
||||
#if MICROPY_MEM_STATS
|
||||
size_t m_get_total_bytes_allocated(void);
|
||||
size_t m_get_current_bytes_allocated(void);
|
||||
size_t m_get_peak_bytes_allocated(void);
|
||||
#endif
|
||||
|
||||
/** array helpers ***********************************************/
|
||||
|
||||
// get the number of elements in a fixed-size array
|
||||
#define MP_ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
|
||||
|
||||
// align ptr to the nearest multiple of "alignment"
|
||||
#define MP_ALIGN(ptr, alignment) (void *)(((uintptr_t)(ptr) + ((alignment) - 1)) & ~((alignment) - 1))
|
||||
|
||||
/** unichar / UTF-8 *********************************************/
|
||||
|
||||
#if MICROPY_PY_BUILTINS_STR_UNICODE
|
||||
// with unicode enabled we need a type which can fit chars up to 0x10ffff
|
||||
typedef uint32_t unichar;
|
||||
#else
|
||||
// without unicode enabled we can only need to fit chars up to 0xff
|
||||
// (on 16-bit archs uint is 16-bits and more efficient than uint32_t)
|
||||
typedef uint unichar;
|
||||
#endif
|
||||
|
||||
#if MICROPY_PY_BUILTINS_STR_UNICODE
|
||||
unichar utf8_get_char(const byte *s);
|
||||
const byte *utf8_next_char(const byte *s);
|
||||
size_t utf8_charlen(const byte *str, size_t len);
|
||||
#else
|
||||
static inline unichar utf8_get_char(const byte *s) {
|
||||
return *s;
|
||||
}
|
||||
static inline const byte *utf8_next_char(const byte *s) {
|
||||
return s + 1;
|
||||
}
|
||||
static inline size_t utf8_charlen(const byte *str, size_t len) {
|
||||
(void)str;
|
||||
return len;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool unichar_isspace(unichar c);
|
||||
bool unichar_isalpha(unichar c);
|
||||
bool unichar_isprint(unichar c);
|
||||
bool unichar_isdigit(unichar c);
|
||||
bool unichar_isxdigit(unichar c);
|
||||
bool unichar_isident(unichar c);
|
||||
bool unichar_isalnum(unichar c);
|
||||
bool unichar_isupper(unichar c);
|
||||
bool unichar_islower(unichar c);
|
||||
unichar unichar_tolower(unichar c);
|
||||
unichar unichar_toupper(unichar c);
|
||||
mp_uint_t unichar_xdigit_value(unichar c);
|
||||
#define UTF8_IS_NONASCII(ch) ((ch) & 0x80)
|
||||
#define UTF8_IS_CONT(ch) (((ch) & 0xC0) == 0x80)
|
||||
|
||||
/** variable string *********************************************/
|
||||
|
||||
typedef struct _vstr_t {
|
||||
size_t alloc;
|
||||
size_t len;
|
||||
char *buf;
|
||||
bool fixed_buf : 1;
|
||||
} vstr_t;
|
||||
|
||||
// convenience macro to declare a vstr with a fixed size buffer on the stack
|
||||
#define VSTR_FIXED(vstr, alloc) vstr_t vstr; char vstr##_buf[(alloc)]; vstr_init_fixed_buf(&vstr, (alloc), vstr##_buf);
|
||||
|
||||
void vstr_init(vstr_t *vstr, size_t alloc);
|
||||
void vstr_init_len(vstr_t *vstr, size_t len);
|
||||
void vstr_init_fixed_buf(vstr_t *vstr, size_t alloc, char *buf);
|
||||
struct _mp_print_t;
|
||||
void vstr_init_print(vstr_t *vstr, size_t alloc, struct _mp_print_t *print);
|
||||
void vstr_clear(vstr_t *vstr);
|
||||
vstr_t *vstr_new(size_t alloc);
|
||||
void vstr_free(vstr_t *vstr);
|
||||
static inline void vstr_reset(vstr_t *vstr) {
|
||||
vstr->len = 0;
|
||||
}
|
||||
static inline char *vstr_str(vstr_t *vstr) {
|
||||
return vstr->buf;
|
||||
}
|
||||
static inline size_t vstr_len(vstr_t *vstr) {
|
||||
return vstr->len;
|
||||
}
|
||||
void vstr_hint_size(vstr_t *vstr, size_t size);
|
||||
char *vstr_extend(vstr_t *vstr, size_t size);
|
||||
char *vstr_add_len(vstr_t *vstr, size_t len);
|
||||
char *vstr_null_terminated_str(vstr_t *vstr);
|
||||
void vstr_add_byte(vstr_t *vstr, byte v);
|
||||
void vstr_add_char(vstr_t *vstr, unichar chr);
|
||||
void vstr_add_str(vstr_t *vstr, const char *str);
|
||||
void vstr_add_strn(vstr_t *vstr, const char *str, size_t len);
|
||||
void vstr_ins_byte(vstr_t *vstr, size_t byte_pos, byte b);
|
||||
void vstr_ins_char(vstr_t *vstr, size_t char_pos, unichar chr);
|
||||
void vstr_cut_head_bytes(vstr_t *vstr, size_t bytes_to_cut);
|
||||
void vstr_cut_tail_bytes(vstr_t *vstr, size_t bytes_to_cut);
|
||||
void vstr_cut_out_bytes(vstr_t *vstr, size_t byte_pos, size_t bytes_to_cut);
|
||||
void vstr_printf(vstr_t *vstr, const char *fmt, ...);
|
||||
|
||||
/** non-dynamic size-bounded variable buffer/string *************/
|
||||
|
||||
#define CHECKBUF(buf, max_size) char buf[max_size + 1]; size_t buf##_len = max_size; char *buf##_p = buf;
|
||||
#define CHECKBUF_RESET(buf, max_size) buf##_len = max_size; buf##_p = buf;
|
||||
#define CHECKBUF_APPEND(buf, src, src_len) \
|
||||
{ size_t l = MIN(src_len, buf##_len); \
|
||||
memcpy(buf##_p, src, l); \
|
||||
buf##_len -= l; \
|
||||
buf##_p += l; }
|
||||
#define CHECKBUF_APPEND_0(buf) { *buf##_p = 0; }
|
||||
#define CHECKBUF_LEN(buf) (buf##_p - buf)
|
||||
|
||||
#ifdef va_start
|
||||
void vstr_vprintf(vstr_t *vstr, const char *fmt, va_list ap);
|
||||
#endif
|
||||
|
||||
// Debugging helpers
|
||||
int DEBUG_printf(const char *fmt, ...);
|
||||
|
||||
extern mp_uint_t mp_verbose_flag;
|
||||
|
||||
/** float internals *************/
|
||||
|
||||
#if MICROPY_PY_BUILTINS_FLOAT
|
||||
|
||||
#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE
|
||||
#define MP_FLOAT_EXP_BITS (11)
|
||||
#define MP_FLOAT_FRAC_BITS (52)
|
||||
typedef uint64_t mp_float_uint_t;
|
||||
#elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT
|
||||
#define MP_FLOAT_EXP_BITS (8)
|
||||
#define MP_FLOAT_FRAC_BITS (23)
|
||||
typedef uint32_t mp_float_uint_t;
|
||||
#endif
|
||||
|
||||
#define MP_FLOAT_EXP_BIAS ((1 << (MP_FLOAT_EXP_BITS - 1)) - 1)
|
||||
|
||||
typedef union _mp_float_union_t {
|
||||
mp_float_t f;
|
||||
#if MP_ENDIANNESS_LITTLE
|
||||
struct {
|
||||
mp_float_uint_t frc : MP_FLOAT_FRAC_BITS;
|
||||
mp_float_uint_t exp : MP_FLOAT_EXP_BITS;
|
||||
mp_float_uint_t sgn : 1;
|
||||
} p;
|
||||
#else
|
||||
struct {
|
||||
mp_float_uint_t sgn : 1;
|
||||
mp_float_uint_t exp : MP_FLOAT_EXP_BITS;
|
||||
mp_float_uint_t frc : MP_FLOAT_FRAC_BITS;
|
||||
} p;
|
||||
#endif
|
||||
mp_float_uint_t i;
|
||||
} mp_float_union_t;
|
||||
|
||||
#endif // MICROPY_PY_BUILTINS_FLOAT
|
||||
|
||||
/** ROM string compression *************/
|
||||
|
||||
#if MICROPY_ROM_TEXT_COMPRESSION
|
||||
|
||||
#ifdef NO_QSTR
|
||||
|
||||
// Compression enabled but doing QSTR extraction.
|
||||
// So leave MP_COMPRESSED_ROM_TEXT in place for makeqstrdefs.py / makecompresseddata.py to find them.
|
||||
|
||||
#else
|
||||
|
||||
// Compression enabled and doing a regular build.
|
||||
// Map MP_COMPRESSED_ROM_TEXT to the compressed strings.
|
||||
|
||||
// Force usage of the MP_ERROR_TEXT macro by requiring an opaque type.
|
||||
typedef struct {
|
||||
#ifdef __clang__
|
||||
// Fix "error: empty struct has size 0 in C, size 1 in C++".
|
||||
char dummy;
|
||||
#endif
|
||||
} *mp_rom_error_text_t;
|
||||
|
||||
#include <string.h>
|
||||
|
||||
inline __attribute__((always_inline)) const char *MP_COMPRESSED_ROM_TEXT(const char *msg) {
|
||||
// "genhdr/compressed.data.h" contains an invocation of the MP_MATCH_COMPRESSED macro for each compressed string.
|
||||
// The giant if(strcmp) tree is optimized by the compiler, which turns this into a direct return of the compressed data.
|
||||
#define MP_MATCH_COMPRESSED(a, b) if (strcmp(msg, a) == 0) { return b; } else
|
||||
|
||||
// It also contains a single invocation of the MP_COMPRESSED_DATA macro, we don't need that here.
|
||||
#define MP_COMPRESSED_DATA(x)
|
||||
|
||||
#include "genhdr/compressed.data.h"
|
||||
|
||||
#undef MP_COMPRESSED_DATA
|
||||
#undef MP_MATCH_COMPRESSED
|
||||
|
||||
return msg;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#else
|
||||
|
||||
// Compression not enabled, just make it a no-op.
|
||||
|
||||
typedef const char *mp_rom_error_text_t;
|
||||
#define MP_COMPRESSED_ROM_TEXT(x) x
|
||||
|
||||
#endif // MICROPY_ROM_TEXT_COMPRESSION
|
||||
|
||||
// Might add more types of compressed text in the future.
|
||||
// For now, forward directly to MP_COMPRESSED_ROM_TEXT.
|
||||
#define MP_ERROR_TEXT(x) (mp_rom_error_text_t)MP_COMPRESSED_ROM_TEXT(x)
|
||||
|
||||
#endif // MICROPY_INCLUDED_PY_MISC_H
|
||||
@@ -0,0 +1,71 @@
|
||||
ifneq ($(lastword a b),b)
|
||||
$(error These Makefiles require make 3.81 or newer)
|
||||
endif
|
||||
|
||||
# Set TOP to be the path to get from the current directory (where make was
|
||||
# invoked) to the top of the tree. $(lastword $(MAKEFILE_LIST)) returns
|
||||
# the name of this makefile relative to where make was invoked.
|
||||
#
|
||||
# We assume that this file is in the py directory so we use $(dir ) twice
|
||||
# to get to the top of the tree.
|
||||
|
||||
THIS_MAKEFILE := $(lastword $(MAKEFILE_LIST))
|
||||
TOP := $(patsubst %/py/mkenv.mk,%,$(THIS_MAKEFILE))
|
||||
|
||||
# Turn on increased build verbosity by defining BUILD_VERBOSE in your main
|
||||
# Makefile or in your environment. You can also use V=1 on the make command
|
||||
# line.
|
||||
|
||||
ifeq ("$(origin V)", "command line")
|
||||
BUILD_VERBOSE=$(V)
|
||||
endif
|
||||
ifndef BUILD_VERBOSE
|
||||
BUILD_VERBOSE = 0
|
||||
endif
|
||||
ifeq ($(BUILD_VERBOSE),0)
|
||||
Q = @
|
||||
else
|
||||
Q =
|
||||
endif
|
||||
# Since this is a new feature, advertise it
|
||||
ifeq ($(BUILD_VERBOSE),0)
|
||||
$(info Use make V=1 or set BUILD_VERBOSE in your environment to increase build verbosity.)
|
||||
endif
|
||||
|
||||
# default settings; can be overridden in main Makefile
|
||||
|
||||
PY_SRC ?= $(TOP)/py
|
||||
BUILD ?= build
|
||||
|
||||
RM = rm
|
||||
ECHO = @echo
|
||||
CP = cp
|
||||
MKDIR = mkdir
|
||||
SED = sed
|
||||
CAT = cat
|
||||
TOUCH = touch
|
||||
PYTHON = python3
|
||||
|
||||
AS = $(CROSS_COMPILE)as
|
||||
CC = $(CROSS_COMPILE)gcc
|
||||
CXX = $(CROSS_COMPILE)g++
|
||||
GDB = $(CROSS_COMPILE)gdb
|
||||
LD = $(CROSS_COMPILE)ld
|
||||
OBJCOPY = $(CROSS_COMPILE)objcopy
|
||||
SIZE = $(CROSS_COMPILE)size
|
||||
STRIP = $(CROSS_COMPILE)strip
|
||||
AR = $(CROSS_COMPILE)ar
|
||||
|
||||
MAKE_MANIFEST = $(PYTHON) $(TOP)/tools/makemanifest.py
|
||||
MAKE_FROZEN = $(PYTHON) $(TOP)/tools/make-frozen.py
|
||||
MPY_CROSS = $(TOP)/mpy-cross/mpy-cross
|
||||
MPY_TOOL = $(PYTHON) $(TOP)/tools/mpy-tool.py
|
||||
|
||||
MPY_LIB_DIR = $(TOP)/../micropython-lib
|
||||
|
||||
all:
|
||||
.PHONY: all
|
||||
|
||||
.DELETE_ON_ERROR:
|
||||
|
||||
MKENV_INCLUDED = 1
|
||||
@@ -0,0 +1,258 @@
|
||||
ifneq ($(MKENV_INCLUDED),1)
|
||||
# We assume that mkenv is in the same directory as this file.
|
||||
THIS_MAKEFILE = $(lastword $(MAKEFILE_LIST))
|
||||
include $(dir $(THIS_MAKEFILE))mkenv.mk
|
||||
endif
|
||||
|
||||
# Extra deps that need to happen before object compilation.
|
||||
OBJ_EXTRA_ORDER_DEPS =
|
||||
|
||||
ifeq ($(MICROPY_ROM_TEXT_COMPRESSION),1)
|
||||
# If compression is enabled, trigger the build of compressed.data.h...
|
||||
OBJ_EXTRA_ORDER_DEPS += $(HEADER_BUILD)/compressed.data.h
|
||||
# ...and enable the MP_COMPRESSED_ROM_TEXT macro (used by MP_ERROR_TEXT).
|
||||
CFLAGS += -DMICROPY_ROM_TEXT_COMPRESSION=1
|
||||
endif
|
||||
|
||||
# QSTR generation uses the same CFLAGS, with these modifications.
|
||||
QSTR_GEN_FLAGS = -DNO_QSTR -I$(BUILD)/tmp
|
||||
# Note: := to force evalulation immediately.
|
||||
QSTR_GEN_CFLAGS := $(CFLAGS)
|
||||
QSTR_GEN_CFLAGS += $(QSTR_GEN_FLAGS)
|
||||
QSTR_GEN_CXXFLAGS := $(CXXFLAGS)
|
||||
QSTR_GEN_CXXFLAGS += $(QSTR_GEN_FLAGS)
|
||||
|
||||
# This file expects that OBJ contains a list of all of the object files.
|
||||
# The directory portion of each object file is used to locate the source
|
||||
# and should not contain any ..'s but rather be relative to the top of the
|
||||
# tree.
|
||||
#
|
||||
# So for example, py/map.c would have an object file name py/map.o
|
||||
# The object files will go into the build directory and mantain the same
|
||||
# directory structure as the source tree. So the final dependency will look
|
||||
# like this:
|
||||
#
|
||||
# build/py/map.o: py/map.c
|
||||
#
|
||||
# We set vpath to point to the top of the tree so that the source files
|
||||
# can be located. By following this scheme, it allows a single build rule
|
||||
# to be used to compile all .c files.
|
||||
|
||||
vpath %.S . $(TOP) $(USER_C_MODULES)
|
||||
$(BUILD)/%.o: %.S
|
||||
$(ECHO) "CC $<"
|
||||
$(Q)$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
vpath %.s . $(TOP) $(USER_C_MODULES)
|
||||
$(BUILD)/%.o: %.s
|
||||
$(ECHO) "AS $<"
|
||||
$(Q)$(AS) -o $@ $<
|
||||
|
||||
define compile_c
|
||||
$(ECHO) "CC $<"
|
||||
$(Q)$(CC) $(CFLAGS) -c -MD -o $@ $<
|
||||
@# The following fixes the dependency file.
|
||||
@# See http://make.paulandlesley.org/autodep.html for details.
|
||||
@# Regex adjusted from the above to play better with Windows paths, etc.
|
||||
@$(CP) $(@:.o=.d) $(@:.o=.P); \
|
||||
$(SED) -e 's/#.*//' -e 's/^.*: *//' -e 's/ *\\$$//' \
|
||||
-e '/^$$/ d' -e 's/$$/ :/' < $(@:.o=.d) >> $(@:.o=.P); \
|
||||
$(RM) -f $(@:.o=.d)
|
||||
endef
|
||||
|
||||
define compile_cxx
|
||||
$(ECHO) "CXX $<"
|
||||
$(Q)$(CXX) $(CXXFLAGS) -c -MD -o $@ $<
|
||||
@# The following fixes the dependency file.
|
||||
@# See http://make.paulandlesley.org/autodep.html for details.
|
||||
@# Regex adjusted from the above to play better with Windows paths, etc.
|
||||
@$(CP) $(@:.o=.d) $(@:.o=.P); \
|
||||
$(SED) -e 's/#.*//' -e 's/^.*: *//' -e 's/ *\\$$//' \
|
||||
-e '/^$$/ d' -e 's/$$/ :/' < $(@:.o=.d) >> $(@:.o=.P); \
|
||||
$(RM) -f $(@:.o=.d)
|
||||
endef
|
||||
|
||||
vpath %.c . $(TOP) $(USER_C_MODULES)
|
||||
$(BUILD)/%.o: %.c
|
||||
$(call compile_c)
|
||||
|
||||
vpath %.c . $(TOP) $(USER_C_MODULES)
|
||||
|
||||
vpath %.cpp . $(TOP) $(USER_C_MODULES)
|
||||
$(BUILD)/%.o: %.cpp
|
||||
$(call compile_cxx)
|
||||
|
||||
$(BUILD)/%.pp: %.c
|
||||
$(ECHO) "PreProcess $<"
|
||||
$(Q)$(CPP) $(CFLAGS) -Wp,-C,-dD,-dI -o $@ $<
|
||||
|
||||
# The following rule uses | to create an order only prerequisite. Order only
|
||||
# prerequisites only get built if they don't exist. They don't cause timestamp
|
||||
# checking to be performed.
|
||||
#
|
||||
# We don't know which source files actually need the generated.h (since
|
||||
# it is #included from str.h). The compiler generated dependencies will cause
|
||||
# the right .o's to get recompiled if the generated.h file changes. Adding
|
||||
# an order-only dependency to all of the .o's will cause the generated .h
|
||||
# to get built before we try to compile any of them.
|
||||
$(OBJ): | $(HEADER_BUILD)/qstrdefs.generated.h $(HEADER_BUILD)/mpversion.h $(OBJ_EXTRA_ORDER_DEPS)
|
||||
|
||||
# The logic for qstr regeneration (applied by makeqstrdefs.py) is:
|
||||
# - if anything in QSTR_GLOBAL_DEPENDENCIES is newer, then process all source files ($^)
|
||||
# - else, if list of newer prerequisites ($?) is not empty, then process just these ($?)
|
||||
# - else, process all source files ($^) [this covers "make -B" which can set $? to empty]
|
||||
# See more information about this process in docs/develop/qstr.rst.
|
||||
$(HEADER_BUILD)/qstr.i.last: $(SRC_QSTR) $(QSTR_GLOBAL_DEPENDENCIES) | $(QSTR_GLOBAL_REQUIREMENTS)
|
||||
$(ECHO) "GEN $@"
|
||||
$(Q)$(PYTHON) $(PY_SRC)/makeqstrdefs.py pp $(CPP) output $(HEADER_BUILD)/qstr.i.last cflags $(QSTR_GEN_CFLAGS) cxxflags $(QSTR_GEN_CXXFLAGS) sources $^ dependencies $(QSTR_GLOBAL_DEPENDENCIES) changed_sources $?
|
||||
|
||||
$(HEADER_BUILD)/qstr.split: $(HEADER_BUILD)/qstr.i.last
|
||||
$(ECHO) "GEN $@"
|
||||
$(Q)$(PYTHON) $(PY_SRC)/makeqstrdefs.py split qstr $< $(HEADER_BUILD)/qstr _
|
||||
$(Q)$(TOUCH) $@
|
||||
|
||||
$(QSTR_DEFS_COLLECTED): $(HEADER_BUILD)/qstr.split
|
||||
$(ECHO) "GEN $@"
|
||||
$(Q)$(PYTHON) $(PY_SRC)/makeqstrdefs.py cat qstr _ $(HEADER_BUILD)/qstr $@
|
||||
|
||||
# Compressed error strings.
|
||||
$(HEADER_BUILD)/compressed.split: $(HEADER_BUILD)/qstr.i.last
|
||||
$(ECHO) "GEN $@"
|
||||
$(Q)$(PYTHON) $(PY_SRC)/makeqstrdefs.py split compress $< $(HEADER_BUILD)/compress _
|
||||
$(Q)$(TOUCH) $@
|
||||
|
||||
$(HEADER_BUILD)/compressed.collected: $(HEADER_BUILD)/compressed.split
|
||||
$(ECHO) "GEN $@"
|
||||
$(Q)$(PYTHON) $(PY_SRC)/makeqstrdefs.py cat compress _ $(HEADER_BUILD)/compress $@
|
||||
|
||||
# $(sort $(var)) removes duplicates
|
||||
#
|
||||
# The net effect of this, is it causes the objects to depend on the
|
||||
# object directories (but only for existence), and the object directories
|
||||
# will be created if they don't exist.
|
||||
OBJ_DIRS = $(sort $(dir $(OBJ)))
|
||||
$(OBJ): | $(OBJ_DIRS)
|
||||
$(OBJ_DIRS):
|
||||
$(MKDIR) -p $@
|
||||
|
||||
$(HEADER_BUILD):
|
||||
$(MKDIR) -p $@
|
||||
|
||||
ifneq ($(FROZEN_MANIFEST),)
|
||||
# to build frozen_content.c from a manifest
|
||||
$(BUILD)/frozen_content.c: FORCE $(BUILD)/genhdr/qstrdefs.generated.h
|
||||
$(Q)$(MAKE_MANIFEST) -o $@ -v "MPY_DIR=$(TOP)" -v "MPY_LIB_DIR=$(MPY_LIB_DIR)" -v "PORT_DIR=$(shell pwd)" -v "BOARD_DIR=$(BOARD_DIR)" -b "$(BUILD)" $(if $(MPY_CROSS_FLAGS),-f"$(MPY_CROSS_FLAGS)",) $(FROZEN_MANIFEST)
|
||||
|
||||
ifneq ($(FROZEN_DIR),)
|
||||
$(error FROZEN_DIR cannot be used in conjunction with FROZEN_MANIFEST)
|
||||
endif
|
||||
|
||||
ifneq ($(FROZEN_MPY_DIR),)
|
||||
$(error FROZEN_MPY_DIR cannot be used in conjunction with FROZEN_MANIFEST)
|
||||
endif
|
||||
endif
|
||||
|
||||
ifneq ($(FROZEN_DIR),)
|
||||
$(info Warning: FROZEN_DIR is deprecated in favour of FROZEN_MANIFEST)
|
||||
$(BUILD)/frozen.c: $(wildcard $(FROZEN_DIR)/*) $(HEADER_BUILD) $(FROZEN_EXTRA_DEPS)
|
||||
$(ECHO) "GEN $@"
|
||||
$(Q)$(MAKE_FROZEN) $(FROZEN_DIR) > $@
|
||||
endif
|
||||
|
||||
ifneq ($(FROZEN_MPY_DIR),)
|
||||
$(info Warning: FROZEN_MPY_DIR is deprecated in favour of FROZEN_MANIFEST)
|
||||
# make a list of all the .py files that need compiling and freezing
|
||||
FROZEN_MPY_PY_FILES := $(shell find -L $(FROZEN_MPY_DIR) -type f -name '*.py' | $(SED) -e 's=^$(FROZEN_MPY_DIR)/==')
|
||||
FROZEN_MPY_MPY_FILES := $(addprefix $(BUILD)/frozen_mpy/,$(FROZEN_MPY_PY_FILES:.py=.mpy))
|
||||
|
||||
# to build .mpy files from .py files
|
||||
$(BUILD)/frozen_mpy/%.mpy: $(FROZEN_MPY_DIR)/%.py
|
||||
@$(ECHO) "MPY $<"
|
||||
$(Q)$(MKDIR) -p $(dir $@)
|
||||
$(Q)$(MPY_CROSS) -o $@ -s $(<:$(FROZEN_MPY_DIR)/%=%) $(MPY_CROSS_FLAGS) $<
|
||||
|
||||
# to build frozen_mpy.c from all .mpy files
|
||||
$(BUILD)/frozen_mpy.c: $(FROZEN_MPY_MPY_FILES) $(BUILD)/genhdr/qstrdefs.generated.h
|
||||
@$(ECHO) "GEN $@"
|
||||
$(Q)$(MPY_TOOL) -f -q $(BUILD)/genhdr/qstrdefs.preprocessed.h $(FROZEN_MPY_MPY_FILES) > $@
|
||||
endif
|
||||
|
||||
ifneq ($(PROG),)
|
||||
# Build a standalone executable (unix does this)
|
||||
|
||||
all: $(PROG)
|
||||
|
||||
$(PROG): $(OBJ)
|
||||
$(ECHO) "LINK $@"
|
||||
# Do not pass COPT here - it's *C* compiler optimizations. For example,
|
||||
# we may want to compile using Thumb, but link with non-Thumb libc.
|
||||
$(Q)$(CC) -o $@ $^ $(LIB) $(LDFLAGS)
|
||||
ifndef DEBUG
|
||||
$(Q)$(STRIP) $(STRIPFLAGS_EXTRA) $(PROG)
|
||||
endif
|
||||
$(Q)$(SIZE) $$(find $(BUILD) -path "$(BUILD)/build/frozen*.o") $(PROG)
|
||||
|
||||
clean: clean-prog
|
||||
clean-prog:
|
||||
$(RM) -f $(PROG)
|
||||
$(RM) -f $(PROG).map
|
||||
|
||||
.PHONY: clean-prog
|
||||
endif
|
||||
|
||||
submodules:
|
||||
$(ECHO) "Updating submodules: $(GIT_SUBMODULES)"
|
||||
ifneq ($(GIT_SUBMODULES),)
|
||||
$(Q)git submodule update --init $(addprefix $(TOP)/,$(GIT_SUBMODULES))
|
||||
endif
|
||||
.PHONY: submodules
|
||||
|
||||
LIBMICROPYTHON = libmicropython.a
|
||||
|
||||
# We can execute extra commands after library creation using
|
||||
# LIBMICROPYTHON_EXTRA_CMD. This may be needed e.g. to integrate
|
||||
# with 3rd-party projects which don't have proper dependency
|
||||
# tracking. Then LIBMICROPYTHON_EXTRA_CMD can e.g. touch some
|
||||
# other file to cause needed effect, e.g. relinking with new lib.
|
||||
lib $(LIBMICROPYTHON): $(OBJ)
|
||||
$(AR) rcs $(LIBMICROPYTHON) $^
|
||||
$(LIBMICROPYTHON_EXTRA_CMD)
|
||||
|
||||
clean:
|
||||
$(RM) -rf $(BUILD) $(CLEAN_EXTRA)
|
||||
.PHONY: clean
|
||||
|
||||
# Clean every non-git file from FROZEN_DIR/FROZEN_MPY_DIR, but making a backup.
|
||||
# We run rmdir below to avoid empty backup dir (it will silently fail if backup
|
||||
# is non-empty).
|
||||
clean-frozen:
|
||||
if [ -n "$(FROZEN_MPY_DIR)" ]; then \
|
||||
backup_dir=$(FROZEN_MPY_DIR).$$(date +%Y%m%dT%H%M%S); mkdir $$backup_dir; \
|
||||
cd $(FROZEN_MPY_DIR); git status --ignored -u all -s . | awk ' {print $$2}' \
|
||||
| xargs --no-run-if-empty cp --parents -t ../$$backup_dir; \
|
||||
rmdir ../$$backup_dir 2>/dev/null || true; \
|
||||
git clean -d -f .; \
|
||||
fi
|
||||
|
||||
if [ -n "$(FROZEN_DIR)" ]; then \
|
||||
backup_dir=$(FROZEN_DIR).$$(date +%Y%m%dT%H%M%S); mkdir $$backup_dir; \
|
||||
cd $(FROZEN_DIR); git status --ignored -u all -s . | awk ' {print $$2}' \
|
||||
| xargs --no-run-if-empty cp --parents -t ../$$backup_dir; \
|
||||
rmdir ../$$backup_dir 2>/dev/null || true; \
|
||||
git clean -d -f .; \
|
||||
fi
|
||||
.PHONY: clean-frozen
|
||||
|
||||
print-cfg:
|
||||
$(ECHO) "PY_SRC = $(PY_SRC)"
|
||||
$(ECHO) "BUILD = $(BUILD)"
|
||||
$(ECHO) "OBJ = $(OBJ)"
|
||||
.PHONY: print-cfg
|
||||
|
||||
print-def:
|
||||
@$(ECHO) "The following defines are built into the $(CC) compiler"
|
||||
$(TOUCH) __empty__.c
|
||||
@$(CC) -E -Wp,-dM __empty__.c
|
||||
@$(RM) -f __empty__.c
|
||||
|
||||
-include $(OBJ:.o=.P)
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013, 2014 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "py/builtin.h"
|
||||
|
||||
#if MICROPY_PY_ARRAY
|
||||
|
||||
STATIC const mp_rom_map_elem_t mp_module_array_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uarray) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_array), MP_ROM_PTR(&mp_type_array) },
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(mp_module_array_globals, mp_module_array_globals_table);
|
||||
|
||||
const mp_obj_module_t mp_module_uarray = {
|
||||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t *)&mp_module_array_globals,
|
||||
};
|
||||
|
||||
MP_REGISTER_MODULE(MP_QSTR_uarray, mp_module_uarray, MICROPY_PY_ARRAY);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,785 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013, 2014 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "py/smallint.h"
|
||||
#include "py/objint.h"
|
||||
#include "py/objstr.h"
|
||||
#include "py/objtype.h"
|
||||
#include "py/runtime.h"
|
||||
#include "py/builtin.h"
|
||||
#include "py/stream.h"
|
||||
|
||||
#if MICROPY_PY_BUILTINS_FLOAT
|
||||
#include <math.h>
|
||||
#endif
|
||||
|
||||
#if MICROPY_PY_IO
|
||||
extern struct _mp_dummy_t mp_sys_stdout_obj; // type is irrelevant, just need pointer
|
||||
#endif
|
||||
|
||||
// args[0] is function from class body
|
||||
// args[1] is class name
|
||||
// args[2:] are base objects
|
||||
STATIC mp_obj_t mp_builtin___build_class__(size_t n_args, const mp_obj_t *args) {
|
||||
assert(2 <= n_args);
|
||||
|
||||
// set the new classes __locals__ object
|
||||
mp_obj_dict_t *old_locals = mp_locals_get();
|
||||
mp_obj_t class_locals = mp_obj_new_dict(0);
|
||||
mp_locals_set(MP_OBJ_TO_PTR(class_locals));
|
||||
|
||||
// call the class code
|
||||
mp_obj_t cell = mp_call_function_0(args[0]);
|
||||
|
||||
// restore old __locals__ object
|
||||
mp_locals_set(old_locals);
|
||||
|
||||
// get the class type (meta object) from the base objects
|
||||
mp_obj_t meta;
|
||||
if (n_args == 2) {
|
||||
// no explicit bases, so use 'type'
|
||||
meta = MP_OBJ_FROM_PTR(&mp_type_type);
|
||||
} else {
|
||||
// use type of first base object
|
||||
meta = MP_OBJ_FROM_PTR(mp_obj_get_type(args[2]));
|
||||
}
|
||||
|
||||
// TODO do proper metaclass resolution for multiple base objects
|
||||
|
||||
// create the new class using a call to the meta object
|
||||
mp_obj_t meta_args[3];
|
||||
meta_args[0] = args[1]; // class name
|
||||
meta_args[1] = mp_obj_new_tuple(n_args - 2, args + 2); // tuple of bases
|
||||
meta_args[2] = class_locals; // dict of members
|
||||
mp_obj_t new_class = mp_call_function_n_kw(meta, 3, 0, meta_args);
|
||||
|
||||
// store into cell if neede
|
||||
if (cell != mp_const_none) {
|
||||
mp_obj_cell_set(cell, new_class);
|
||||
}
|
||||
|
||||
return new_class;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR(mp_builtin___build_class___obj, 2, mp_builtin___build_class__);
|
||||
|
||||
STATIC mp_obj_t mp_builtin_abs(mp_obj_t o_in) {
|
||||
return mp_unary_op(MP_UNARY_OP_ABS, o_in);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_abs_obj, mp_builtin_abs);
|
||||
|
||||
STATIC mp_obj_t mp_builtin_all(mp_obj_t o_in) {
|
||||
mp_obj_iter_buf_t iter_buf;
|
||||
mp_obj_t iterable = mp_getiter(o_in, &iter_buf);
|
||||
mp_obj_t item;
|
||||
while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
|
||||
if (!mp_obj_is_true(item)) {
|
||||
return mp_const_false;
|
||||
}
|
||||
}
|
||||
return mp_const_true;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_all_obj, mp_builtin_all);
|
||||
|
||||
STATIC mp_obj_t mp_builtin_any(mp_obj_t o_in) {
|
||||
mp_obj_iter_buf_t iter_buf;
|
||||
mp_obj_t iterable = mp_getiter(o_in, &iter_buf);
|
||||
mp_obj_t item;
|
||||
while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
|
||||
if (mp_obj_is_true(item)) {
|
||||
return mp_const_true;
|
||||
}
|
||||
}
|
||||
return mp_const_false;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_any_obj, mp_builtin_any);
|
||||
|
||||
STATIC mp_obj_t mp_builtin_bin(mp_obj_t o_in) {
|
||||
mp_obj_t args[] = { MP_OBJ_NEW_QSTR(MP_QSTR__brace_open__colon__hash_b_brace_close_), o_in };
|
||||
return mp_obj_str_format(MP_ARRAY_SIZE(args), args, NULL);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_bin_obj, mp_builtin_bin);
|
||||
|
||||
STATIC mp_obj_t mp_builtin_callable(mp_obj_t o_in) {
|
||||
if (mp_obj_is_callable(o_in)) {
|
||||
return mp_const_true;
|
||||
} else {
|
||||
return mp_const_false;
|
||||
}
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_callable_obj, mp_builtin_callable);
|
||||
|
||||
STATIC mp_obj_t mp_builtin_chr(mp_obj_t o_in) {
|
||||
#if MICROPY_PY_BUILTINS_STR_UNICODE
|
||||
mp_uint_t c = mp_obj_get_int(o_in);
|
||||
uint8_t str[4];
|
||||
int len = 0;
|
||||
if (c < 0x80) {
|
||||
*str = c;
|
||||
len = 1;
|
||||
} else if (c < 0x800) {
|
||||
str[0] = (c >> 6) | 0xC0;
|
||||
str[1] = (c & 0x3F) | 0x80;
|
||||
len = 2;
|
||||
} else if (c < 0x10000) {
|
||||
str[0] = (c >> 12) | 0xE0;
|
||||
str[1] = ((c >> 6) & 0x3F) | 0x80;
|
||||
str[2] = (c & 0x3F) | 0x80;
|
||||
len = 3;
|
||||
} else if (c < 0x110000) {
|
||||
str[0] = (c >> 18) | 0xF0;
|
||||
str[1] = ((c >> 12) & 0x3F) | 0x80;
|
||||
str[2] = ((c >> 6) & 0x3F) | 0x80;
|
||||
str[3] = (c & 0x3F) | 0x80;
|
||||
len = 4;
|
||||
} else {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("chr() arg not in range(0x110000)"));
|
||||
}
|
||||
return mp_obj_new_str_via_qstr((char *)str, len);
|
||||
#else
|
||||
mp_int_t ord = mp_obj_get_int(o_in);
|
||||
if (0 <= ord && ord <= 0xff) {
|
||||
uint8_t str[1] = {ord};
|
||||
return mp_obj_new_str_via_qstr((char *)str, 1);
|
||||
} else {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("chr() arg not in range(256)"));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_chr_obj, mp_builtin_chr);
|
||||
|
||||
STATIC mp_obj_t mp_builtin_dir(size_t n_args, const mp_obj_t *args) {
|
||||
mp_obj_t dir = mp_obj_new_list(0, NULL);
|
||||
if (n_args == 0) {
|
||||
// Make a list of names in the local namespace
|
||||
mp_obj_dict_t *dict = mp_locals_get();
|
||||
for (size_t i = 0; i < dict->map.alloc; i++) {
|
||||
if (mp_map_slot_is_filled(&dict->map, i)) {
|
||||
mp_obj_list_append(dir, dict->map.table[i].key);
|
||||
}
|
||||
}
|
||||
} else { // n_args == 1
|
||||
// Make a list of names in the given object
|
||||
// Implemented by probing all possible qstrs with mp_load_method_maybe
|
||||
size_t nqstr = QSTR_TOTAL();
|
||||
for (size_t i = MP_QSTR_ + 1; i < nqstr; ++i) {
|
||||
mp_obj_t dest[2];
|
||||
mp_load_method_protected(args[0], i, dest, false);
|
||||
if (dest[0] != MP_OBJ_NULL) {
|
||||
#if MICROPY_PY_ALL_SPECIAL_METHODS
|
||||
// Support for __dir__: see if we can dispatch to this special method
|
||||
// This relies on MP_QSTR__dir__ being first after MP_QSTR_
|
||||
if (i == MP_QSTR___dir__ && dest[1] != MP_OBJ_NULL) {
|
||||
return mp_call_method_n_kw(0, 0, dest);
|
||||
}
|
||||
#endif
|
||||
mp_obj_list_append(dir, MP_OBJ_NEW_QSTR(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
return dir;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_dir_obj, 0, 1, mp_builtin_dir);
|
||||
|
||||
STATIC mp_obj_t mp_builtin_divmod(mp_obj_t o1_in, mp_obj_t o2_in) {
|
||||
return mp_binary_op(MP_BINARY_OP_DIVMOD, o1_in, o2_in);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(mp_builtin_divmod_obj, mp_builtin_divmod);
|
||||
|
||||
STATIC mp_obj_t mp_builtin_hash(mp_obj_t o_in) {
|
||||
// result is guaranteed to be a (small) int
|
||||
return mp_unary_op(MP_UNARY_OP_HASH, o_in);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_hash_obj, mp_builtin_hash);
|
||||
|
||||
STATIC mp_obj_t mp_builtin_hex(mp_obj_t o_in) {
|
||||
#if MICROPY_PY_BUILTINS_STR_OP_MODULO
|
||||
return mp_binary_op(MP_BINARY_OP_MODULO, MP_OBJ_NEW_QSTR(MP_QSTR__percent__hash_x), o_in);
|
||||
#else
|
||||
mp_obj_t args[] = { MP_OBJ_NEW_QSTR(MP_QSTR__brace_open__colon__hash_x_brace_close_), o_in };
|
||||
return mp_obj_str_format(MP_ARRAY_SIZE(args), args, NULL);
|
||||
#endif
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_hex_obj, mp_builtin_hex);
|
||||
|
||||
#if MICROPY_PY_BUILTINS_INPUT
|
||||
|
||||
#include "py/mphal.h"
|
||||
#include "lib/mp-readline/readline.h"
|
||||
|
||||
// A port can define mp_hal_readline if they want to use a custom function here
|
||||
#ifndef mp_hal_readline
|
||||
#define mp_hal_readline readline
|
||||
#endif
|
||||
|
||||
STATIC mp_obj_t mp_builtin_input(size_t n_args, const mp_obj_t *args) {
|
||||
if (n_args == 1) {
|
||||
mp_obj_print(args[0], PRINT_STR);
|
||||
}
|
||||
vstr_t line;
|
||||
vstr_init(&line, 16);
|
||||
int ret = mp_hal_readline(&line, "");
|
||||
if (ret == CHAR_CTRL_C) {
|
||||
mp_raise_type(&mp_type_KeyboardInterrupt);
|
||||
}
|
||||
if (line.len == 0 && ret == CHAR_CTRL_D) {
|
||||
mp_raise_type(&mp_type_EOFError);
|
||||
}
|
||||
return mp_obj_new_str_from_vstr(&mp_type_str, &line);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_input_obj, 0, 1, mp_builtin_input);
|
||||
|
||||
#endif
|
||||
|
||||
STATIC mp_obj_t mp_builtin_iter(mp_obj_t o_in) {
|
||||
return mp_getiter(o_in, NULL);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_iter_obj, mp_builtin_iter);
|
||||
|
||||
#if MICROPY_PY_BUILTINS_MIN_MAX
|
||||
|
||||
STATIC mp_obj_t mp_builtin_min_max(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs, mp_uint_t op) {
|
||||
mp_map_elem_t *key_elem = mp_map_lookup(kwargs, MP_OBJ_NEW_QSTR(MP_QSTR_key), MP_MAP_LOOKUP);
|
||||
mp_map_elem_t *default_elem;
|
||||
mp_obj_t key_fn = key_elem == NULL ? MP_OBJ_NULL : key_elem->value;
|
||||
if (n_args == 1) {
|
||||
// given an iterable
|
||||
mp_obj_iter_buf_t iter_buf;
|
||||
mp_obj_t iterable = mp_getiter(args[0], &iter_buf);
|
||||
mp_obj_t best_key = MP_OBJ_NULL;
|
||||
mp_obj_t best_obj = MP_OBJ_NULL;
|
||||
mp_obj_t item;
|
||||
while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
|
||||
mp_obj_t key = key_fn == MP_OBJ_NULL ? item : mp_call_function_1(key_fn, item);
|
||||
if (best_obj == MP_OBJ_NULL || (mp_binary_op(op, key, best_key) == mp_const_true)) {
|
||||
best_key = key;
|
||||
best_obj = item;
|
||||
}
|
||||
}
|
||||
if (best_obj == MP_OBJ_NULL) {
|
||||
default_elem = mp_map_lookup(kwargs, MP_OBJ_NEW_QSTR(MP_QSTR_default), MP_MAP_LOOKUP);
|
||||
if (default_elem != NULL) {
|
||||
best_obj = default_elem->value;
|
||||
} else {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("arg is an empty sequence"));
|
||||
}
|
||||
}
|
||||
return best_obj;
|
||||
} else {
|
||||
// given many args
|
||||
mp_obj_t best_key = MP_OBJ_NULL;
|
||||
mp_obj_t best_obj = MP_OBJ_NULL;
|
||||
for (size_t i = 0; i < n_args; i++) {
|
||||
mp_obj_t key = key_fn == MP_OBJ_NULL ? args[i] : mp_call_function_1(key_fn, args[i]);
|
||||
if (best_obj == MP_OBJ_NULL || (mp_binary_op(op, key, best_key) == mp_const_true)) {
|
||||
best_key = key;
|
||||
best_obj = args[i];
|
||||
}
|
||||
}
|
||||
return best_obj;
|
||||
}
|
||||
}
|
||||
|
||||
STATIC mp_obj_t mp_builtin_max(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) {
|
||||
return mp_builtin_min_max(n_args, args, kwargs, MP_BINARY_OP_MORE);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(mp_builtin_max_obj, 1, mp_builtin_max);
|
||||
|
||||
STATIC mp_obj_t mp_builtin_min(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) {
|
||||
return mp_builtin_min_max(n_args, args, kwargs, MP_BINARY_OP_LESS);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(mp_builtin_min_obj, 1, mp_builtin_min);
|
||||
|
||||
#endif
|
||||
|
||||
#if MICROPY_PY_BUILTINS_NEXT2
|
||||
STATIC mp_obj_t mp_builtin_next(size_t n_args, const mp_obj_t *args) {
|
||||
if (n_args == 1) {
|
||||
mp_obj_t ret = mp_iternext_allow_raise(args[0]);
|
||||
if (ret == MP_OBJ_STOP_ITERATION) {
|
||||
mp_raise_type(&mp_type_StopIteration);
|
||||
} else {
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
mp_obj_t ret = mp_iternext(args[0]);
|
||||
return ret == MP_OBJ_STOP_ITERATION ? args[1] : ret;
|
||||
}
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_next_obj, 1, 2, mp_builtin_next);
|
||||
#else
|
||||
STATIC mp_obj_t mp_builtin_next(mp_obj_t o) {
|
||||
mp_obj_t ret = mp_iternext_allow_raise(o);
|
||||
if (ret == MP_OBJ_STOP_ITERATION) {
|
||||
mp_raise_type(&mp_type_StopIteration);
|
||||
} else {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_next_obj, mp_builtin_next);
|
||||
#endif
|
||||
|
||||
STATIC mp_obj_t mp_builtin_oct(mp_obj_t o_in) {
|
||||
#if MICROPY_PY_BUILTINS_STR_OP_MODULO
|
||||
return mp_binary_op(MP_BINARY_OP_MODULO, MP_OBJ_NEW_QSTR(MP_QSTR__percent__hash_o), o_in);
|
||||
#else
|
||||
mp_obj_t args[] = { MP_OBJ_NEW_QSTR(MP_QSTR__brace_open__colon__hash_o_brace_close_), o_in };
|
||||
return mp_obj_str_format(MP_ARRAY_SIZE(args), args, NULL);
|
||||
#endif
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_oct_obj, mp_builtin_oct);
|
||||
|
||||
STATIC mp_obj_t mp_builtin_ord(mp_obj_t o_in) {
|
||||
size_t len;
|
||||
const byte *str = (const byte *)mp_obj_str_get_data(o_in, &len);
|
||||
#if MICROPY_PY_BUILTINS_STR_UNICODE
|
||||
if (mp_obj_is_str(o_in)) {
|
||||
len = utf8_charlen(str, len);
|
||||
if (len == 1) {
|
||||
return mp_obj_new_int(utf8_get_char(str));
|
||||
}
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
// a bytes object, or a str without unicode support (don't sign extend the char)
|
||||
if (len == 1) {
|
||||
return MP_OBJ_NEW_SMALL_INT(str[0]);
|
||||
}
|
||||
}
|
||||
|
||||
#if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("ord expects a character"));
|
||||
#else
|
||||
mp_raise_msg_varg(&mp_type_TypeError,
|
||||
MP_ERROR_TEXT("ord() expected a character, but string of length %d found"), (int)len);
|
||||
#endif
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_ord_obj, mp_builtin_ord);
|
||||
|
||||
STATIC mp_obj_t mp_builtin_pow(size_t n_args, const mp_obj_t *args) {
|
||||
switch (n_args) {
|
||||
case 2:
|
||||
return mp_binary_op(MP_BINARY_OP_POWER, args[0], args[1]);
|
||||
default:
|
||||
#if !MICROPY_PY_BUILTINS_POW3
|
||||
mp_raise_NotImplementedError(MP_ERROR_TEXT("3-arg pow() not supported"));
|
||||
#elif MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_MPZ
|
||||
return mp_binary_op(MP_BINARY_OP_MODULO, mp_binary_op(MP_BINARY_OP_POWER, args[0], args[1]), args[2]);
|
||||
#else
|
||||
return mp_obj_int_pow3(args[0], args[1], args[2]);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_pow_obj, 2, 3, mp_builtin_pow);
|
||||
|
||||
STATIC mp_obj_t mp_builtin_print(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
enum { ARG_sep, ARG_end, ARG_file };
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{ MP_QSTR_sep, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_QSTR(MP_QSTR__space_)} },
|
||||
{ MP_QSTR_end, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_QSTR(MP_QSTR__0x0a_)} },
|
||||
#if MICROPY_PY_IO && MICROPY_PY_SYS_STDFILES
|
||||
{ MP_QSTR_file, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&mp_sys_stdout_obj)} },
|
||||
#endif
|
||||
};
|
||||
|
||||
// parse args (a union is used to reduce the amount of C stack that is needed)
|
||||
union {
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
size_t len[2];
|
||||
} u;
|
||||
mp_arg_parse_all(0, NULL, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, u.args);
|
||||
|
||||
#if MICROPY_PY_IO && MICROPY_PY_SYS_STDFILES
|
||||
mp_get_stream_raise(u.args[ARG_file].u_obj, MP_STREAM_OP_WRITE);
|
||||
mp_print_t print = {MP_OBJ_TO_PTR(u.args[ARG_file].u_obj), mp_stream_write_adaptor};
|
||||
#endif
|
||||
|
||||
// extract the objects first because we are going to use the other part of the union
|
||||
mp_obj_t sep = u.args[ARG_sep].u_obj;
|
||||
mp_obj_t end = u.args[ARG_end].u_obj;
|
||||
const char *sep_data = mp_obj_str_get_data(sep, &u.len[0]);
|
||||
const char *end_data = mp_obj_str_get_data(end, &u.len[1]);
|
||||
|
||||
for (size_t i = 0; i < n_args; i++) {
|
||||
if (i > 0) {
|
||||
#if MICROPY_PY_IO && MICROPY_PY_SYS_STDFILES
|
||||
mp_stream_write_adaptor(print.data, sep_data, u.len[0]);
|
||||
#else
|
||||
mp_print_strn(&mp_plat_print, sep_data, u.len[0], 0, 0, 0);
|
||||
#endif
|
||||
}
|
||||
#if MICROPY_PY_IO && MICROPY_PY_SYS_STDFILES
|
||||
mp_obj_print_helper(&print, pos_args[i], PRINT_STR);
|
||||
#else
|
||||
mp_obj_print_helper(&mp_plat_print, pos_args[i], PRINT_STR);
|
||||
#endif
|
||||
}
|
||||
#if MICROPY_PY_IO && MICROPY_PY_SYS_STDFILES
|
||||
mp_stream_write_adaptor(print.data, end_data, u.len[1]);
|
||||
#else
|
||||
mp_print_strn(&mp_plat_print, end_data, u.len[1], 0, 0, 0);
|
||||
#endif
|
||||
return mp_const_none;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(mp_builtin_print_obj, 0, mp_builtin_print);
|
||||
|
||||
STATIC mp_obj_t mp_builtin___repl_print__(mp_obj_t o) {
|
||||
if (o != mp_const_none) {
|
||||
mp_obj_print_helper(MP_PYTHON_PRINTER, o, PRINT_REPR);
|
||||
mp_print_str(MP_PYTHON_PRINTER, "\n");
|
||||
#if MICROPY_CAN_OVERRIDE_BUILTINS
|
||||
// Set "_" special variable
|
||||
mp_obj_t dest[2] = {MP_OBJ_SENTINEL, o};
|
||||
mp_type_module.attr(MP_OBJ_FROM_PTR(&mp_module_builtins), MP_QSTR__, dest);
|
||||
#endif
|
||||
}
|
||||
return mp_const_none;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin___repl_print___obj, mp_builtin___repl_print__);
|
||||
|
||||
STATIC mp_obj_t mp_builtin_repr(mp_obj_t o_in) {
|
||||
vstr_t vstr;
|
||||
mp_print_t print;
|
||||
vstr_init_print(&vstr, 16, &print);
|
||||
mp_obj_print_helper(&print, o_in, PRINT_REPR);
|
||||
return mp_obj_new_str_from_vstr(&mp_type_str, &vstr);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_repr_obj, mp_builtin_repr);
|
||||
|
||||
STATIC mp_obj_t mp_builtin_round(size_t n_args, const mp_obj_t *args) {
|
||||
mp_obj_t o_in = args[0];
|
||||
if (mp_obj_is_int(o_in)) {
|
||||
if (n_args <= 1) {
|
||||
return o_in;
|
||||
}
|
||||
|
||||
#if !MICROPY_PY_BUILTINS_ROUND_INT
|
||||
mp_raise_NotImplementedError(NULL);
|
||||
#else
|
||||
mp_int_t num_dig = mp_obj_get_int(args[1]);
|
||||
if (num_dig >= 0) {
|
||||
return o_in;
|
||||
}
|
||||
|
||||
mp_obj_t mult = mp_binary_op(MP_BINARY_OP_POWER, MP_OBJ_NEW_SMALL_INT(10), MP_OBJ_NEW_SMALL_INT(-num_dig));
|
||||
mp_obj_t half_mult = mp_binary_op(MP_BINARY_OP_FLOOR_DIVIDE, mult, MP_OBJ_NEW_SMALL_INT(2));
|
||||
mp_obj_t modulo = mp_binary_op(MP_BINARY_OP_MODULO, o_in, mult);
|
||||
mp_obj_t rounded = mp_binary_op(MP_BINARY_OP_SUBTRACT, o_in, modulo);
|
||||
if (mp_obj_is_true(mp_binary_op(MP_BINARY_OP_MORE, half_mult, modulo))) {
|
||||
return rounded;
|
||||
} else if (mp_obj_is_true(mp_binary_op(MP_BINARY_OP_MORE, modulo, half_mult))) {
|
||||
return mp_binary_op(MP_BINARY_OP_ADD, rounded, mult);
|
||||
} else {
|
||||
// round to even number
|
||||
mp_obj_t floor = mp_binary_op(MP_BINARY_OP_FLOOR_DIVIDE, o_in, mult);
|
||||
if (mp_obj_is_true(mp_binary_op(MP_BINARY_OP_AND, floor, MP_OBJ_NEW_SMALL_INT(1)))) {
|
||||
return mp_binary_op(MP_BINARY_OP_ADD, rounded, mult);
|
||||
} else {
|
||||
return rounded;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#if MICROPY_PY_BUILTINS_FLOAT
|
||||
mp_float_t val = mp_obj_get_float(o_in);
|
||||
if (n_args > 1) {
|
||||
mp_int_t num_dig = mp_obj_get_int(args[1]);
|
||||
mp_float_t mult = MICROPY_FLOAT_C_FUN(pow)(10, (mp_float_t)num_dig);
|
||||
// TODO may lead to overflow
|
||||
mp_float_t rounded = MICROPY_FLOAT_C_FUN(nearbyint)(val * mult) / mult;
|
||||
return mp_obj_new_float(rounded);
|
||||
}
|
||||
mp_float_t rounded = MICROPY_FLOAT_C_FUN(nearbyint)(val);
|
||||
return mp_obj_new_int_from_float(rounded);
|
||||
#else
|
||||
mp_int_t r = mp_obj_get_int(o_in);
|
||||
return mp_obj_new_int(r);
|
||||
#endif
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_round_obj, 1, 2, mp_builtin_round);
|
||||
|
||||
STATIC mp_obj_t mp_builtin_sum(size_t n_args, const mp_obj_t *args) {
|
||||
mp_obj_t value;
|
||||
switch (n_args) {
|
||||
case 1:
|
||||
value = MP_OBJ_NEW_SMALL_INT(0);
|
||||
break;
|
||||
default:
|
||||
value = args[1];
|
||||
break;
|
||||
}
|
||||
mp_obj_iter_buf_t iter_buf;
|
||||
mp_obj_t iterable = mp_getiter(args[0], &iter_buf);
|
||||
mp_obj_t item;
|
||||
while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
|
||||
value = mp_binary_op(MP_BINARY_OP_ADD, value, item);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_sum_obj, 1, 2, mp_builtin_sum);
|
||||
|
||||
STATIC mp_obj_t mp_builtin_sorted(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) {
|
||||
if (n_args > 1) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("must use keyword argument for key function"));
|
||||
}
|
||||
mp_obj_t self = mp_type_list.make_new(&mp_type_list, 1, 0, args);
|
||||
mp_obj_list_sort(1, &self, kwargs);
|
||||
|
||||
return self;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(mp_builtin_sorted_obj, 1, mp_builtin_sorted);
|
||||
|
||||
// See mp_load_attr() if making any changes
|
||||
static inline mp_obj_t mp_load_attr_default(mp_obj_t base, qstr attr, mp_obj_t defval) {
|
||||
mp_obj_t dest[2];
|
||||
// use load_method, raising or not raising exception
|
||||
if (defval == MP_OBJ_NULL) {
|
||||
mp_load_method(base, attr, dest);
|
||||
} else {
|
||||
mp_load_method_protected(base, attr, dest, false);
|
||||
}
|
||||
if (dest[0] == MP_OBJ_NULL) {
|
||||
return defval;
|
||||
} else if (dest[1] == MP_OBJ_NULL) {
|
||||
// load_method returned just a normal attribute
|
||||
return dest[0];
|
||||
} else {
|
||||
// load_method returned a method, so build a bound method object
|
||||
return mp_obj_new_bound_meth(dest[0], dest[1]);
|
||||
}
|
||||
}
|
||||
|
||||
STATIC mp_obj_t mp_builtin_getattr(size_t n_args, const mp_obj_t *args) {
|
||||
mp_obj_t defval = MP_OBJ_NULL;
|
||||
if (n_args > 2) {
|
||||
defval = args[2];
|
||||
}
|
||||
return mp_load_attr_default(args[0], mp_obj_str_get_qstr(args[1]), defval);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_getattr_obj, 2, 3, mp_builtin_getattr);
|
||||
|
||||
STATIC mp_obj_t mp_builtin_setattr(mp_obj_t base, mp_obj_t attr, mp_obj_t value) {
|
||||
mp_store_attr(base, mp_obj_str_get_qstr(attr), value);
|
||||
return mp_const_none;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_3(mp_builtin_setattr_obj, mp_builtin_setattr);
|
||||
|
||||
#if MICROPY_CPYTHON_COMPAT
|
||||
STATIC mp_obj_t mp_builtin_delattr(mp_obj_t base, mp_obj_t attr) {
|
||||
return mp_builtin_setattr(base, attr, MP_OBJ_NULL);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(mp_builtin_delattr_obj, mp_builtin_delattr);
|
||||
#endif
|
||||
|
||||
STATIC mp_obj_t mp_builtin_hasattr(mp_obj_t object_in, mp_obj_t attr_in) {
|
||||
qstr attr = mp_obj_str_get_qstr(attr_in);
|
||||
mp_obj_t dest[2];
|
||||
mp_load_method_protected(object_in, attr, dest, false);
|
||||
return mp_obj_new_bool(dest[0] != MP_OBJ_NULL);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_2(mp_builtin_hasattr_obj, mp_builtin_hasattr);
|
||||
|
||||
STATIC mp_obj_t mp_builtin_globals(void) {
|
||||
return MP_OBJ_FROM_PTR(mp_globals_get());
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_0(mp_builtin_globals_obj, mp_builtin_globals);
|
||||
|
||||
STATIC mp_obj_t mp_builtin_locals(void) {
|
||||
return MP_OBJ_FROM_PTR(mp_locals_get());
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_0(mp_builtin_locals_obj, mp_builtin_locals);
|
||||
|
||||
// These are defined in terms of MicroPython API functions right away
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_id_obj, mp_obj_id);
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(mp_builtin_len_obj, mp_obj_len);
|
||||
|
||||
STATIC const mp_rom_map_elem_t mp_module_builtins_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_builtins) },
|
||||
|
||||
// built-in core functions
|
||||
{ MP_ROM_QSTR(MP_QSTR___build_class__), MP_ROM_PTR(&mp_builtin___build_class___obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR___import__), MP_ROM_PTR(&mp_builtin___import___obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR___repl_print__), MP_ROM_PTR(&mp_builtin___repl_print___obj) },
|
||||
|
||||
// built-in types
|
||||
{ MP_ROM_QSTR(MP_QSTR_bool), MP_ROM_PTR(&mp_type_bool) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_bytes), MP_ROM_PTR(&mp_type_bytes) },
|
||||
#if MICROPY_PY_BUILTINS_BYTEARRAY
|
||||
{ MP_ROM_QSTR(MP_QSTR_bytearray), MP_ROM_PTR(&mp_type_bytearray) },
|
||||
#endif
|
||||
#if MICROPY_PY_BUILTINS_COMPLEX
|
||||
{ MP_ROM_QSTR(MP_QSTR_complex), MP_ROM_PTR(&mp_type_complex) },
|
||||
#endif
|
||||
{ MP_ROM_QSTR(MP_QSTR_dict), MP_ROM_PTR(&mp_type_dict) },
|
||||
#if MICROPY_PY_BUILTINS_ENUMERATE
|
||||
{ MP_ROM_QSTR(MP_QSTR_enumerate), MP_ROM_PTR(&mp_type_enumerate) },
|
||||
#endif
|
||||
#if MICROPY_PY_BUILTINS_FILTER
|
||||
{ MP_ROM_QSTR(MP_QSTR_filter), MP_ROM_PTR(&mp_type_filter) },
|
||||
#endif
|
||||
#if MICROPY_PY_BUILTINS_FLOAT
|
||||
{ MP_ROM_QSTR(MP_QSTR_float), MP_ROM_PTR(&mp_type_float) },
|
||||
#endif
|
||||
#if MICROPY_PY_BUILTINS_SET && MICROPY_PY_BUILTINS_FROZENSET
|
||||
{ MP_ROM_QSTR(MP_QSTR_frozenset), MP_ROM_PTR(&mp_type_frozenset) },
|
||||
#endif
|
||||
{ MP_ROM_QSTR(MP_QSTR_int), MP_ROM_PTR(&mp_type_int) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_list), MP_ROM_PTR(&mp_type_list) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_map), MP_ROM_PTR(&mp_type_map) },
|
||||
#if MICROPY_PY_BUILTINS_MEMORYVIEW
|
||||
{ MP_ROM_QSTR(MP_QSTR_memoryview), MP_ROM_PTR(&mp_type_memoryview) },
|
||||
#endif
|
||||
{ MP_ROM_QSTR(MP_QSTR_object), MP_ROM_PTR(&mp_type_object) },
|
||||
#if MICROPY_PY_BUILTINS_PROPERTY
|
||||
{ MP_ROM_QSTR(MP_QSTR_property), MP_ROM_PTR(&mp_type_property) },
|
||||
#endif
|
||||
{ MP_ROM_QSTR(MP_QSTR_range), MP_ROM_PTR(&mp_type_range) },
|
||||
#if MICROPY_PY_BUILTINS_REVERSED
|
||||
{ MP_ROM_QSTR(MP_QSTR_reversed), MP_ROM_PTR(&mp_type_reversed) },
|
||||
#endif
|
||||
#if MICROPY_PY_BUILTINS_SET
|
||||
{ MP_ROM_QSTR(MP_QSTR_set), MP_ROM_PTR(&mp_type_set) },
|
||||
#endif
|
||||
#if MICROPY_PY_BUILTINS_SLICE
|
||||
{ MP_ROM_QSTR(MP_QSTR_slice), MP_ROM_PTR(&mp_type_slice) },
|
||||
#endif
|
||||
{ MP_ROM_QSTR(MP_QSTR_str), MP_ROM_PTR(&mp_type_str) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_super), MP_ROM_PTR(&mp_type_super) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_tuple), MP_ROM_PTR(&mp_type_tuple) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_type), MP_ROM_PTR(&mp_type_type) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_zip), MP_ROM_PTR(&mp_type_zip) },
|
||||
|
||||
{ MP_ROM_QSTR(MP_QSTR_classmethod), MP_ROM_PTR(&mp_type_classmethod) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_staticmethod), MP_ROM_PTR(&mp_type_staticmethod) },
|
||||
|
||||
// built-in objects
|
||||
{ MP_ROM_QSTR(MP_QSTR_Ellipsis), MP_ROM_PTR(&mp_const_ellipsis_obj) },
|
||||
#if MICROPY_PY_BUILTINS_NOTIMPLEMENTED
|
||||
{ MP_ROM_QSTR(MP_QSTR_NotImplemented), MP_ROM_PTR(&mp_const_notimplemented_obj) },
|
||||
#endif
|
||||
|
||||
// built-in user functions
|
||||
{ MP_ROM_QSTR(MP_QSTR_abs), MP_ROM_PTR(&mp_builtin_abs_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_all), MP_ROM_PTR(&mp_builtin_all_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_any), MP_ROM_PTR(&mp_builtin_any_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_bin), MP_ROM_PTR(&mp_builtin_bin_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_callable), MP_ROM_PTR(&mp_builtin_callable_obj) },
|
||||
#if MICROPY_PY_BUILTINS_COMPILE
|
||||
{ MP_ROM_QSTR(MP_QSTR_compile), MP_ROM_PTR(&mp_builtin_compile_obj) },
|
||||
#endif
|
||||
{ MP_ROM_QSTR(MP_QSTR_chr), MP_ROM_PTR(&mp_builtin_chr_obj) },
|
||||
#if MICROPY_CPYTHON_COMPAT
|
||||
{ MP_ROM_QSTR(MP_QSTR_delattr), MP_ROM_PTR(&mp_builtin_delattr_obj) },
|
||||
#endif
|
||||
{ MP_ROM_QSTR(MP_QSTR_dir), MP_ROM_PTR(&mp_builtin_dir_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_divmod), MP_ROM_PTR(&mp_builtin_divmod_obj) },
|
||||
#if MICROPY_PY_BUILTINS_EVAL_EXEC
|
||||
{ MP_ROM_QSTR(MP_QSTR_eval), MP_ROM_PTR(&mp_builtin_eval_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_exec), MP_ROM_PTR(&mp_builtin_exec_obj) },
|
||||
#endif
|
||||
#if MICROPY_PY_BUILTINS_EXECFILE
|
||||
{ MP_ROM_QSTR(MP_QSTR_execfile), MP_ROM_PTR(&mp_builtin_execfile_obj) },
|
||||
#endif
|
||||
{ MP_ROM_QSTR(MP_QSTR_getattr), MP_ROM_PTR(&mp_builtin_getattr_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_setattr), MP_ROM_PTR(&mp_builtin_setattr_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_globals), MP_ROM_PTR(&mp_builtin_globals_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_hasattr), MP_ROM_PTR(&mp_builtin_hasattr_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_hash), MP_ROM_PTR(&mp_builtin_hash_obj) },
|
||||
#if MICROPY_PY_BUILTINS_HELP
|
||||
{ MP_ROM_QSTR(MP_QSTR_help), MP_ROM_PTR(&mp_builtin_help_obj) },
|
||||
#endif
|
||||
{ MP_ROM_QSTR(MP_QSTR_hex), MP_ROM_PTR(&mp_builtin_hex_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_id), MP_ROM_PTR(&mp_builtin_id_obj) },
|
||||
#if MICROPY_PY_BUILTINS_INPUT
|
||||
{ MP_ROM_QSTR(MP_QSTR_input), MP_ROM_PTR(&mp_builtin_input_obj) },
|
||||
#endif
|
||||
{ MP_ROM_QSTR(MP_QSTR_isinstance), MP_ROM_PTR(&mp_builtin_isinstance_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_issubclass), MP_ROM_PTR(&mp_builtin_issubclass_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_iter), MP_ROM_PTR(&mp_builtin_iter_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_len), MP_ROM_PTR(&mp_builtin_len_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_locals), MP_ROM_PTR(&mp_builtin_locals_obj) },
|
||||
#if MICROPY_PY_BUILTINS_MIN_MAX
|
||||
{ MP_ROM_QSTR(MP_QSTR_max), MP_ROM_PTR(&mp_builtin_max_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_min), MP_ROM_PTR(&mp_builtin_min_obj) },
|
||||
#endif
|
||||
{ MP_ROM_QSTR(MP_QSTR_next), MP_ROM_PTR(&mp_builtin_next_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_oct), MP_ROM_PTR(&mp_builtin_oct_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_ord), MP_ROM_PTR(&mp_builtin_ord_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_pow), MP_ROM_PTR(&mp_builtin_pow_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_print), MP_ROM_PTR(&mp_builtin_print_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_repr), MP_ROM_PTR(&mp_builtin_repr_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_round), MP_ROM_PTR(&mp_builtin_round_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_sorted), MP_ROM_PTR(&mp_builtin_sorted_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_sum), MP_ROM_PTR(&mp_builtin_sum_obj) },
|
||||
|
||||
// built-in exceptions
|
||||
{ MP_ROM_QSTR(MP_QSTR_BaseException), MP_ROM_PTR(&mp_type_BaseException) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_ArithmeticError), MP_ROM_PTR(&mp_type_ArithmeticError) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_AssertionError), MP_ROM_PTR(&mp_type_AssertionError) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_AttributeError), MP_ROM_PTR(&mp_type_AttributeError) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_EOFError), MP_ROM_PTR(&mp_type_EOFError) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_Exception), MP_ROM_PTR(&mp_type_Exception) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_GeneratorExit), MP_ROM_PTR(&mp_type_GeneratorExit) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_ImportError), MP_ROM_PTR(&mp_type_ImportError) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_IndentationError), MP_ROM_PTR(&mp_type_IndentationError) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_IndexError), MP_ROM_PTR(&mp_type_IndexError) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_KeyboardInterrupt), MP_ROM_PTR(&mp_type_KeyboardInterrupt) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_KeyError), MP_ROM_PTR(&mp_type_KeyError) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_LookupError), MP_ROM_PTR(&mp_type_LookupError) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_MemoryError), MP_ROM_PTR(&mp_type_MemoryError) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_NameError), MP_ROM_PTR(&mp_type_NameError) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_NotImplementedError), MP_ROM_PTR(&mp_type_NotImplementedError) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_OSError), MP_ROM_PTR(&mp_type_OSError) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_OverflowError), MP_ROM_PTR(&mp_type_OverflowError) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_RuntimeError), MP_ROM_PTR(&mp_type_RuntimeError) },
|
||||
#if MICROPY_PY_ASYNC_AWAIT
|
||||
{ MP_ROM_QSTR(MP_QSTR_StopAsyncIteration), MP_ROM_PTR(&mp_type_StopAsyncIteration) },
|
||||
#endif
|
||||
{ MP_ROM_QSTR(MP_QSTR_StopIteration), MP_ROM_PTR(&mp_type_StopIteration) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_SyntaxError), MP_ROM_PTR(&mp_type_SyntaxError) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_SystemExit), MP_ROM_PTR(&mp_type_SystemExit) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_TypeError), MP_ROM_PTR(&mp_type_TypeError) },
|
||||
#if MICROPY_PY_BUILTINS_STR_UNICODE
|
||||
{ MP_ROM_QSTR(MP_QSTR_UnicodeError), MP_ROM_PTR(&mp_type_UnicodeError) },
|
||||
#endif
|
||||
{ MP_ROM_QSTR(MP_QSTR_ValueError), MP_ROM_PTR(&mp_type_ValueError) },
|
||||
#if MICROPY_EMIT_NATIVE
|
||||
{ MP_ROM_QSTR(MP_QSTR_ViperTypeError), MP_ROM_PTR(&mp_type_ViperTypeError) },
|
||||
#endif
|
||||
{ MP_ROM_QSTR(MP_QSTR_ZeroDivisionError), MP_ROM_PTR(&mp_type_ZeroDivisionError) },
|
||||
|
||||
// Extra builtins as defined by a port
|
||||
MICROPY_PORT_BUILTINS
|
||||
};
|
||||
|
||||
MP_DEFINE_CONST_DICT(mp_module_builtins_globals, mp_module_builtins_globals_table);
|
||||
|
||||
const mp_obj_module_t mp_module_builtins = {
|
||||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t *)&mp_module_builtins_globals,
|
||||
};
|
||||
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013, 2014 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "py/builtin.h"
|
||||
|
||||
#if MICROPY_PY_BUILTINS_FLOAT && MICROPY_PY_BUILTINS_COMPLEX && MICROPY_PY_CMATH
|
||||
|
||||
#include <math.h>
|
||||
|
||||
// phase(z): returns the phase of the number z in the range (-pi, +pi]
|
||||
STATIC mp_obj_t mp_cmath_phase(mp_obj_t z_obj) {
|
||||
mp_float_t real, imag;
|
||||
mp_obj_get_complex(z_obj, &real, &imag);
|
||||
return mp_obj_new_float(MICROPY_FLOAT_C_FUN(atan2)(imag, real));
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_cmath_phase_obj, mp_cmath_phase);
|
||||
|
||||
// polar(z): returns the polar form of z as a tuple
|
||||
STATIC mp_obj_t mp_cmath_polar(mp_obj_t z_obj) {
|
||||
mp_float_t real, imag;
|
||||
mp_obj_get_complex(z_obj, &real, &imag);
|
||||
mp_obj_t tuple[2] = {
|
||||
mp_obj_new_float(MICROPY_FLOAT_C_FUN(sqrt)(real * real + imag * imag)),
|
||||
mp_obj_new_float(MICROPY_FLOAT_C_FUN(atan2)(imag, real)),
|
||||
};
|
||||
return mp_obj_new_tuple(2, tuple);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_cmath_polar_obj, mp_cmath_polar);
|
||||
|
||||
// rect(r, phi): returns the complex number with modulus r and phase phi
|
||||
STATIC mp_obj_t mp_cmath_rect(mp_obj_t r_obj, mp_obj_t phi_obj) {
|
||||
mp_float_t r = mp_obj_get_float(r_obj);
|
||||
mp_float_t phi = mp_obj_get_float(phi_obj);
|
||||
return mp_obj_new_complex(r * MICROPY_FLOAT_C_FUN(cos)(phi), r * MICROPY_FLOAT_C_FUN(sin)(phi));
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(mp_cmath_rect_obj, mp_cmath_rect);
|
||||
|
||||
// exp(z): return the exponential of z
|
||||
STATIC mp_obj_t mp_cmath_exp(mp_obj_t z_obj) {
|
||||
mp_float_t real, imag;
|
||||
mp_obj_get_complex(z_obj, &real, &imag);
|
||||
mp_float_t exp_real = MICROPY_FLOAT_C_FUN(exp)(real);
|
||||
return mp_obj_new_complex(exp_real * MICROPY_FLOAT_C_FUN(cos)(imag), exp_real * MICROPY_FLOAT_C_FUN(sin)(imag));
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_cmath_exp_obj, mp_cmath_exp);
|
||||
|
||||
// log(z): return the natural logarithm of z, with branch cut along the negative real axis
|
||||
// TODO can take second argument, being the base
|
||||
STATIC mp_obj_t mp_cmath_log(mp_obj_t z_obj) {
|
||||
mp_float_t real, imag;
|
||||
mp_obj_get_complex(z_obj, &real, &imag);
|
||||
return mp_obj_new_complex(MICROPY_FLOAT_CONST(0.5) * MICROPY_FLOAT_C_FUN(log)(real * real + imag * imag), MICROPY_FLOAT_C_FUN(atan2)(imag, real));
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_cmath_log_obj, mp_cmath_log);
|
||||
|
||||
#if MICROPY_PY_MATH_SPECIAL_FUNCTIONS
|
||||
// log10(z): return the base-10 logarithm of z, with branch cut along the negative real axis
|
||||
STATIC mp_obj_t mp_cmath_log10(mp_obj_t z_obj) {
|
||||
mp_float_t real, imag;
|
||||
mp_obj_get_complex(z_obj, &real, &imag);
|
||||
return mp_obj_new_complex(MICROPY_FLOAT_CONST(0.5) * MICROPY_FLOAT_C_FUN(log10)(real * real + imag * imag), MICROPY_FLOAT_CONST(0.4342944819032518) * MICROPY_FLOAT_C_FUN(atan2)(imag, real));
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_cmath_log10_obj, mp_cmath_log10);
|
||||
#endif
|
||||
|
||||
// sqrt(z): return the square-root of z
|
||||
STATIC mp_obj_t mp_cmath_sqrt(mp_obj_t z_obj) {
|
||||
mp_float_t real, imag;
|
||||
mp_obj_get_complex(z_obj, &real, &imag);
|
||||
mp_float_t sqrt_abs = MICROPY_FLOAT_C_FUN(pow)(real * real + imag * imag, MICROPY_FLOAT_CONST(0.25));
|
||||
mp_float_t theta = MICROPY_FLOAT_CONST(0.5) * MICROPY_FLOAT_C_FUN(atan2)(imag, real);
|
||||
return mp_obj_new_complex(sqrt_abs * MICROPY_FLOAT_C_FUN(cos)(theta), sqrt_abs * MICROPY_FLOAT_C_FUN(sin)(theta));
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_cmath_sqrt_obj, mp_cmath_sqrt);
|
||||
|
||||
// cos(z): return the cosine of z
|
||||
STATIC mp_obj_t mp_cmath_cos(mp_obj_t z_obj) {
|
||||
mp_float_t real, imag;
|
||||
mp_obj_get_complex(z_obj, &real, &imag);
|
||||
return mp_obj_new_complex(MICROPY_FLOAT_C_FUN(cos)(real) * MICROPY_FLOAT_C_FUN(cosh)(imag), -MICROPY_FLOAT_C_FUN(sin)(real) * MICROPY_FLOAT_C_FUN(sinh)(imag));
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_cmath_cos_obj, mp_cmath_cos);
|
||||
|
||||
// sin(z): return the sine of z
|
||||
STATIC mp_obj_t mp_cmath_sin(mp_obj_t z_obj) {
|
||||
mp_float_t real, imag;
|
||||
mp_obj_get_complex(z_obj, &real, &imag);
|
||||
return mp_obj_new_complex(MICROPY_FLOAT_C_FUN(sin)(real) * MICROPY_FLOAT_C_FUN(cosh)(imag), MICROPY_FLOAT_C_FUN(cos)(real) * MICROPY_FLOAT_C_FUN(sinh)(imag));
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_cmath_sin_obj, mp_cmath_sin);
|
||||
|
||||
STATIC const mp_rom_map_elem_t mp_module_cmath_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_cmath) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_e), mp_const_float_e },
|
||||
{ MP_ROM_QSTR(MP_QSTR_pi), mp_const_float_pi },
|
||||
{ MP_ROM_QSTR(MP_QSTR_phase), MP_ROM_PTR(&mp_cmath_phase_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_polar), MP_ROM_PTR(&mp_cmath_polar_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_rect), MP_ROM_PTR(&mp_cmath_rect_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_exp), MP_ROM_PTR(&mp_cmath_exp_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_log), MP_ROM_PTR(&mp_cmath_log_obj) },
|
||||
#if MICROPY_PY_MATH_SPECIAL_FUNCTIONS
|
||||
{ MP_ROM_QSTR(MP_QSTR_log10), MP_ROM_PTR(&mp_cmath_log10_obj) },
|
||||
#endif
|
||||
{ MP_ROM_QSTR(MP_QSTR_sqrt), MP_ROM_PTR(&mp_cmath_sqrt_obj) },
|
||||
// { MP_ROM_QSTR(MP_QSTR_acos), MP_ROM_PTR(&mp_cmath_acos_obj) },
|
||||
// { MP_ROM_QSTR(MP_QSTR_asin), MP_ROM_PTR(&mp_cmath_asin_obj) },
|
||||
// { MP_ROM_QSTR(MP_QSTR_atan), MP_ROM_PTR(&mp_cmath_atan_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_cos), MP_ROM_PTR(&mp_cmath_cos_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_sin), MP_ROM_PTR(&mp_cmath_sin_obj) },
|
||||
// { MP_ROM_QSTR(MP_QSTR_tan), MP_ROM_PTR(&mp_cmath_tan_obj) },
|
||||
// { MP_ROM_QSTR(MP_QSTR_acosh), MP_ROM_PTR(&mp_cmath_acosh_obj) },
|
||||
// { MP_ROM_QSTR(MP_QSTR_asinh), MP_ROM_PTR(&mp_cmath_asinh_obj) },
|
||||
// { MP_ROM_QSTR(MP_QSTR_atanh), MP_ROM_PTR(&mp_cmath_atanh_obj) },
|
||||
// { MP_ROM_QSTR(MP_QSTR_cosh), MP_ROM_PTR(&mp_cmath_cosh_obj) },
|
||||
// { MP_ROM_QSTR(MP_QSTR_sinh), MP_ROM_PTR(&mp_cmath_sinh_obj) },
|
||||
// { MP_ROM_QSTR(MP_QSTR_tanh), MP_ROM_PTR(&mp_cmath_tanh_obj) },
|
||||
// { MP_ROM_QSTR(MP_QSTR_isfinite), MP_ROM_PTR(&mp_cmath_isfinite_obj) },
|
||||
// { MP_ROM_QSTR(MP_QSTR_isinf), MP_ROM_PTR(&mp_cmath_isinf_obj) },
|
||||
// { MP_ROM_QSTR(MP_QSTR_isnan), MP_ROM_PTR(&mp_cmath_isnan_obj) },
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(mp_module_cmath_globals, mp_module_cmath_globals_table);
|
||||
|
||||
const mp_obj_module_t mp_module_cmath = {
|
||||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t *)&mp_module_cmath_globals,
|
||||
};
|
||||
|
||||
#endif // MICROPY_PY_BUILTINS_FLOAT && MICROPY_PY_CMATH
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013, 2014 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "py/builtin.h"
|
||||
|
||||
#if MICROPY_PY_COLLECTIONS
|
||||
|
||||
STATIC const mp_rom_map_elem_t mp_module_collections_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ucollections) },
|
||||
#if MICROPY_PY_COLLECTIONS_DEQUE
|
||||
{ MP_ROM_QSTR(MP_QSTR_deque), MP_ROM_PTR(&mp_type_deque) },
|
||||
#endif
|
||||
{ MP_ROM_QSTR(MP_QSTR_namedtuple), MP_ROM_PTR(&mp_namedtuple_obj) },
|
||||
#if MICROPY_PY_COLLECTIONS_ORDEREDDICT
|
||||
{ MP_ROM_QSTR(MP_QSTR_OrderedDict), MP_ROM_PTR(&mp_type_ordereddict) },
|
||||
#endif
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(mp_module_collections_globals, mp_module_collections_globals_table);
|
||||
|
||||
const mp_obj_module_t mp_module_collections = {
|
||||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t *)&mp_module_collections_globals,
|
||||
};
|
||||
|
||||
#endif // MICROPY_PY_COLLECTIONS
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013, 2014 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "py/mpstate.h"
|
||||
#include "py/obj.h"
|
||||
#include "py/gc.h"
|
||||
|
||||
#if MICROPY_PY_GC && MICROPY_ENABLE_GC
|
||||
|
||||
// collect(): run a garbage collection
|
||||
STATIC mp_obj_t py_gc_collect(void) {
|
||||
gc_collect();
|
||||
#if MICROPY_PY_GC_COLLECT_RETVAL
|
||||
return MP_OBJ_NEW_SMALL_INT(MP_STATE_MEM(gc_collected));
|
||||
#else
|
||||
return mp_const_none;
|
||||
#endif
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_0(gc_collect_obj, py_gc_collect);
|
||||
|
||||
// disable(): disable the garbage collector
|
||||
STATIC mp_obj_t gc_disable(void) {
|
||||
MP_STATE_MEM(gc_auto_collect_enabled) = 0;
|
||||
return mp_const_none;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_0(gc_disable_obj, gc_disable);
|
||||
|
||||
// enable(): enable the garbage collector
|
||||
STATIC mp_obj_t gc_enable(void) {
|
||||
MP_STATE_MEM(gc_auto_collect_enabled) = 1;
|
||||
return mp_const_none;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_0(gc_enable_obj, gc_enable);
|
||||
|
||||
STATIC mp_obj_t gc_isenabled(void) {
|
||||
return mp_obj_new_bool(MP_STATE_MEM(gc_auto_collect_enabled));
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_0(gc_isenabled_obj, gc_isenabled);
|
||||
|
||||
// mem_free(): return the number of bytes of available heap RAM
|
||||
STATIC mp_obj_t gc_mem_free(void) {
|
||||
gc_info_t info;
|
||||
gc_info(&info);
|
||||
return MP_OBJ_NEW_SMALL_INT(info.free);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_0(gc_mem_free_obj, gc_mem_free);
|
||||
|
||||
// mem_alloc(): return the number of bytes of heap RAM that are allocated
|
||||
STATIC mp_obj_t gc_mem_alloc(void) {
|
||||
gc_info_t info;
|
||||
gc_info(&info);
|
||||
return MP_OBJ_NEW_SMALL_INT(info.used);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_0(gc_mem_alloc_obj, gc_mem_alloc);
|
||||
|
||||
#if MICROPY_GC_ALLOC_THRESHOLD
|
||||
STATIC mp_obj_t gc_threshold(size_t n_args, const mp_obj_t *args) {
|
||||
if (n_args == 0) {
|
||||
if (MP_STATE_MEM(gc_alloc_threshold) == (size_t)-1) {
|
||||
return MP_OBJ_NEW_SMALL_INT(-1);
|
||||
}
|
||||
return mp_obj_new_int(MP_STATE_MEM(gc_alloc_threshold) * MICROPY_BYTES_PER_GC_BLOCK);
|
||||
}
|
||||
mp_int_t val = mp_obj_get_int(args[0]);
|
||||
if (val < 0) {
|
||||
MP_STATE_MEM(gc_alloc_threshold) = (size_t)-1;
|
||||
} else {
|
||||
MP_STATE_MEM(gc_alloc_threshold) = val / MICROPY_BYTES_PER_GC_BLOCK;
|
||||
}
|
||||
return mp_const_none;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(gc_threshold_obj, 0, 1, gc_threshold);
|
||||
#endif
|
||||
|
||||
STATIC const mp_rom_map_elem_t mp_module_gc_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_gc) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_collect), MP_ROM_PTR(&gc_collect_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_disable), MP_ROM_PTR(&gc_disable_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_enable), MP_ROM_PTR(&gc_enable_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_isenabled), MP_ROM_PTR(&gc_isenabled_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_mem_free), MP_ROM_PTR(&gc_mem_free_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_mem_alloc), MP_ROM_PTR(&gc_mem_alloc_obj) },
|
||||
#if MICROPY_GC_ALLOC_THRESHOLD
|
||||
{ MP_ROM_QSTR(MP_QSTR_threshold), MP_ROM_PTR(&gc_threshold_obj) },
|
||||
#endif
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(mp_module_gc_globals, mp_module_gc_globals_table);
|
||||
|
||||
const mp_obj_module_t mp_module_gc = {
|
||||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t *)&mp_module_gc_globals,
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,284 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013, 2014 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "py/runtime.h"
|
||||
#include "py/builtin.h"
|
||||
#include "py/stream.h"
|
||||
#include "py/binary.h"
|
||||
#include "py/objarray.h"
|
||||
#include "py/objstringio.h"
|
||||
#include "py/frozenmod.h"
|
||||
|
||||
#if MICROPY_PY_IO
|
||||
|
||||
extern const mp_obj_type_t mp_type_fileio;
|
||||
extern const mp_obj_type_t mp_type_textio;
|
||||
|
||||
#if MICROPY_PY_IO_IOBASE
|
||||
|
||||
STATIC const mp_obj_type_t mp_type_iobase;
|
||||
|
||||
STATIC const mp_obj_base_t iobase_singleton = {&mp_type_iobase};
|
||||
|
||||
STATIC mp_obj_t iobase_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
|
||||
(void)type;
|
||||
(void)n_args;
|
||||
(void)n_kw;
|
||||
(void)args;
|
||||
return MP_OBJ_FROM_PTR(&iobase_singleton);
|
||||
}
|
||||
|
||||
STATIC mp_uint_t iobase_read_write(mp_obj_t obj, void *buf, mp_uint_t size, int *errcode, qstr qst) {
|
||||
mp_obj_t dest[3];
|
||||
mp_load_method(obj, qst, dest);
|
||||
mp_obj_array_t ar = {{&mp_type_bytearray}, BYTEARRAY_TYPECODE, 0, size, buf};
|
||||
dest[2] = MP_OBJ_FROM_PTR(&ar);
|
||||
mp_obj_t ret_obj = mp_call_method_n_kw(1, 0, dest);
|
||||
if (ret_obj == mp_const_none) {
|
||||
*errcode = MP_EAGAIN;
|
||||
return MP_STREAM_ERROR;
|
||||
}
|
||||
mp_int_t ret = mp_obj_get_int(ret_obj);
|
||||
if (ret >= 0) {
|
||||
return ret;
|
||||
} else {
|
||||
*errcode = -ret;
|
||||
return MP_STREAM_ERROR;
|
||||
}
|
||||
}
|
||||
STATIC mp_uint_t iobase_read(mp_obj_t obj, void *buf, mp_uint_t size, int *errcode) {
|
||||
return iobase_read_write(obj, buf, size, errcode, MP_QSTR_readinto);
|
||||
}
|
||||
|
||||
STATIC mp_uint_t iobase_write(mp_obj_t obj, const void *buf, mp_uint_t size, int *errcode) {
|
||||
return iobase_read_write(obj, (void *)buf, size, errcode, MP_QSTR_write);
|
||||
}
|
||||
|
||||
STATIC mp_uint_t iobase_ioctl(mp_obj_t obj, mp_uint_t request, uintptr_t arg, int *errcode) {
|
||||
mp_obj_t dest[4];
|
||||
mp_load_method(obj, MP_QSTR_ioctl, dest);
|
||||
dest[2] = mp_obj_new_int_from_uint(request);
|
||||
dest[3] = mp_obj_new_int_from_uint(arg);
|
||||
mp_int_t ret = mp_obj_get_int(mp_call_method_n_kw(2, 0, dest));
|
||||
if (ret >= 0) {
|
||||
return ret;
|
||||
} else {
|
||||
*errcode = -ret;
|
||||
return MP_STREAM_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
STATIC const mp_stream_p_t iobase_p = {
|
||||
.read = iobase_read,
|
||||
.write = iobase_write,
|
||||
.ioctl = iobase_ioctl,
|
||||
};
|
||||
|
||||
STATIC const mp_obj_type_t mp_type_iobase = {
|
||||
{ &mp_type_type },
|
||||
.name = MP_QSTR_IOBase,
|
||||
.make_new = iobase_make_new,
|
||||
.protocol = &iobase_p,
|
||||
};
|
||||
|
||||
#endif // MICROPY_PY_IO_IOBASE
|
||||
|
||||
#if MICROPY_PY_IO_BUFFEREDWRITER
|
||||
typedef struct _mp_obj_bufwriter_t {
|
||||
mp_obj_base_t base;
|
||||
mp_obj_t stream;
|
||||
size_t alloc;
|
||||
size_t len;
|
||||
byte buf[0];
|
||||
} mp_obj_bufwriter_t;
|
||||
|
||||
STATIC mp_obj_t bufwriter_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
|
||||
mp_arg_check_num(n_args, n_kw, 2, 2, false);
|
||||
size_t alloc = mp_obj_get_int(args[1]);
|
||||
mp_obj_bufwriter_t *o = m_new_obj_var(mp_obj_bufwriter_t, byte, alloc);
|
||||
o->base.type = type;
|
||||
o->stream = args[0];
|
||||
o->alloc = alloc;
|
||||
o->len = 0;
|
||||
return o;
|
||||
}
|
||||
|
||||
STATIC mp_uint_t bufwriter_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode) {
|
||||
mp_obj_bufwriter_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
|
||||
mp_uint_t org_size = size;
|
||||
|
||||
while (size > 0) {
|
||||
mp_uint_t rem = self->alloc - self->len;
|
||||
if (size < rem) {
|
||||
memcpy(self->buf + self->len, buf, size);
|
||||
self->len += size;
|
||||
return org_size;
|
||||
}
|
||||
|
||||
// Buffer flushing policy here is to flush entire buffer all the time.
|
||||
// This allows e.g. to have a block device as backing storage and write
|
||||
// entire block to it. memcpy below is not ideal and could be optimized
|
||||
// in some cases. But the way it is now it at least ensures that buffer
|
||||
// is word-aligned, to guard against obscure cases when it matters, e.g.
|
||||
// https://github.com/micropython/micropython/issues/1863
|
||||
memcpy(self->buf + self->len, buf, rem);
|
||||
buf = (byte *)buf + rem;
|
||||
size -= rem;
|
||||
mp_uint_t out_sz = mp_stream_write_exactly(self->stream, self->buf, self->alloc, errcode);
|
||||
(void)out_sz;
|
||||
if (*errcode != 0) {
|
||||
return MP_STREAM_ERROR;
|
||||
}
|
||||
// TODO: try to recover from a case of non-blocking stream, e.g. move
|
||||
// remaining chunk to the beginning of buffer.
|
||||
assert(out_sz == self->alloc);
|
||||
self->len = 0;
|
||||
}
|
||||
|
||||
return org_size;
|
||||
}
|
||||
|
||||
STATIC mp_obj_t bufwriter_flush(mp_obj_t self_in) {
|
||||
mp_obj_bufwriter_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
|
||||
if (self->len != 0) {
|
||||
int err;
|
||||
mp_uint_t out_sz = mp_stream_write_exactly(self->stream, self->buf, self->len, &err);
|
||||
(void)out_sz;
|
||||
// TODO: try to recover from a case of non-blocking stream, e.g. move
|
||||
// remaining chunk to the beginning of buffer.
|
||||
assert(out_sz == self->len);
|
||||
self->len = 0;
|
||||
if (err != 0) {
|
||||
mp_raise_OSError(err);
|
||||
}
|
||||
}
|
||||
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(bufwriter_flush_obj, bufwriter_flush);
|
||||
|
||||
STATIC const mp_rom_map_elem_t bufwriter_locals_dict_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_flush), MP_ROM_PTR(&bufwriter_flush_obj) },
|
||||
};
|
||||
STATIC MP_DEFINE_CONST_DICT(bufwriter_locals_dict, bufwriter_locals_dict_table);
|
||||
|
||||
STATIC const mp_stream_p_t bufwriter_stream_p = {
|
||||
.write = bufwriter_write,
|
||||
};
|
||||
|
||||
STATIC const mp_obj_type_t bufwriter_type = {
|
||||
{ &mp_type_type },
|
||||
.name = MP_QSTR_BufferedWriter,
|
||||
.make_new = bufwriter_make_new,
|
||||
.protocol = &bufwriter_stream_p,
|
||||
.locals_dict = (mp_obj_dict_t *)&bufwriter_locals_dict,
|
||||
};
|
||||
#endif // MICROPY_PY_IO_BUFFEREDWRITER
|
||||
|
||||
#if MICROPY_PY_IO_RESOURCE_STREAM
|
||||
STATIC mp_obj_t resource_stream(mp_obj_t package_in, mp_obj_t path_in) {
|
||||
VSTR_FIXED(path_buf, MICROPY_ALLOC_PATH_MAX);
|
||||
size_t len;
|
||||
|
||||
// As an extension to pkg_resources.resource_stream(), we support
|
||||
// package parameter being None, the path_in is interpreted as a
|
||||
// raw path.
|
||||
if (package_in != mp_const_none) {
|
||||
// Pass "True" as sentinel value in fromlist to force returning of leaf module
|
||||
mp_obj_t pkg = mp_import_name(mp_obj_str_get_qstr(package_in), mp_const_true, MP_OBJ_NEW_SMALL_INT(0));
|
||||
|
||||
mp_obj_t dest[2];
|
||||
mp_load_method_maybe(pkg, MP_QSTR___path__, dest);
|
||||
if (dest[0] == MP_OBJ_NULL) {
|
||||
mp_raise_TypeError(NULL);
|
||||
}
|
||||
|
||||
const char *path = mp_obj_str_get_data(dest[0], &len);
|
||||
vstr_add_strn(&path_buf, path, len);
|
||||
vstr_add_byte(&path_buf, '/');
|
||||
}
|
||||
|
||||
const char *path = mp_obj_str_get_data(path_in, &len);
|
||||
vstr_add_strn(&path_buf, path, len);
|
||||
|
||||
len = path_buf.len;
|
||||
const char *data = mp_find_frozen_str(path_buf.buf, &len);
|
||||
if (data != NULL) {
|
||||
mp_obj_stringio_t *o = m_new_obj(mp_obj_stringio_t);
|
||||
o->base.type = &mp_type_bytesio;
|
||||
o->vstr = m_new_obj(vstr_t);
|
||||
vstr_init_fixed_buf(o->vstr, len + 1, (char *)data);
|
||||
o->vstr->len = len;
|
||||
o->pos = 0;
|
||||
return MP_OBJ_FROM_PTR(o);
|
||||
}
|
||||
|
||||
mp_obj_t path_out = mp_obj_new_str(path_buf.buf, path_buf.len);
|
||||
return mp_builtin_open(1, &path_out, (mp_map_t *)&mp_const_empty_map);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(resource_stream_obj, resource_stream);
|
||||
#endif
|
||||
|
||||
STATIC const mp_rom_map_elem_t mp_module_io_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uio) },
|
||||
// Note: mp_builtin_open_obj should be defined by port, it's not
|
||||
// part of the core.
|
||||
{ MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&mp_builtin_open_obj) },
|
||||
#if MICROPY_PY_IO_IOBASE
|
||||
{ MP_ROM_QSTR(MP_QSTR_IOBase), MP_ROM_PTR(&mp_type_iobase) },
|
||||
#endif
|
||||
#if MICROPY_PY_IO_RESOURCE_STREAM
|
||||
{ MP_ROM_QSTR(MP_QSTR_resource_stream), MP_ROM_PTR(&resource_stream_obj) },
|
||||
#endif
|
||||
#if MICROPY_PY_IO_FILEIO
|
||||
{ MP_ROM_QSTR(MP_QSTR_FileIO), MP_ROM_PTR(&mp_type_fileio) },
|
||||
#if MICROPY_CPYTHON_COMPAT
|
||||
{ MP_ROM_QSTR(MP_QSTR_TextIOWrapper), MP_ROM_PTR(&mp_type_textio) },
|
||||
#endif
|
||||
#endif
|
||||
{ MP_ROM_QSTR(MP_QSTR_StringIO), MP_ROM_PTR(&mp_type_stringio) },
|
||||
#if MICROPY_PY_IO_BYTESIO
|
||||
{ MP_ROM_QSTR(MP_QSTR_BytesIO), MP_ROM_PTR(&mp_type_bytesio) },
|
||||
#endif
|
||||
#if MICROPY_PY_IO_BUFFEREDWRITER
|
||||
{ MP_ROM_QSTR(MP_QSTR_BufferedWriter), MP_ROM_PTR(&bufwriter_type) },
|
||||
#endif
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(mp_module_io_globals, mp_module_io_globals_table);
|
||||
|
||||
const mp_obj_module_t mp_module_io = {
|
||||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t *)&mp_module_io_globals,
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,435 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013-2017 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "py/builtin.h"
|
||||
#include "py/runtime.h"
|
||||
|
||||
#if MICROPY_PY_BUILTINS_FLOAT && MICROPY_PY_MATH
|
||||
|
||||
#include <math.h>
|
||||
|
||||
// M_PI is not part of the math.h standard and may not be defined
|
||||
// And by defining our own we can ensure it uses the correct const format.
|
||||
#define MP_PI MICROPY_FLOAT_CONST(3.14159265358979323846)
|
||||
#define MP_PI_4 MICROPY_FLOAT_CONST(0.78539816339744830962)
|
||||
#define MP_3_PI_4 MICROPY_FLOAT_CONST(2.35619449019234492885)
|
||||
|
||||
STATIC NORETURN void math_error(void) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("math domain error"));
|
||||
}
|
||||
|
||||
STATIC mp_obj_t math_generic_1(mp_obj_t x_obj, mp_float_t (*f)(mp_float_t)) {
|
||||
mp_float_t x = mp_obj_get_float(x_obj);
|
||||
mp_float_t ans = f(x);
|
||||
if ((isnan(ans) && !isnan(x)) || (isinf(ans) && !isinf(x))) {
|
||||
math_error();
|
||||
}
|
||||
return mp_obj_new_float(ans);
|
||||
}
|
||||
|
||||
STATIC mp_obj_t math_generic_2(mp_obj_t x_obj, mp_obj_t y_obj, mp_float_t (*f)(mp_float_t, mp_float_t)) {
|
||||
mp_float_t x = mp_obj_get_float(x_obj);
|
||||
mp_float_t y = mp_obj_get_float(y_obj);
|
||||
mp_float_t ans = f(x, y);
|
||||
if ((isnan(ans) && !isnan(x) && !isnan(y)) || (isinf(ans) && !isinf(x))) {
|
||||
math_error();
|
||||
}
|
||||
return mp_obj_new_float(ans);
|
||||
}
|
||||
|
||||
#define MATH_FUN_1(py_name, c_name) \
|
||||
STATIC mp_obj_t mp_math_##py_name(mp_obj_t x_obj) { \
|
||||
return math_generic_1(x_obj, MICROPY_FLOAT_C_FUN(c_name)); \
|
||||
} \
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_math_##py_name##_obj, mp_math_##py_name);
|
||||
|
||||
#define MATH_FUN_1_TO_BOOL(py_name, c_name) \
|
||||
STATIC mp_obj_t mp_math_##py_name(mp_obj_t x_obj) { return mp_obj_new_bool(c_name(mp_obj_get_float(x_obj))); } \
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_math_##py_name##_obj, mp_math_##py_name);
|
||||
|
||||
#define MATH_FUN_1_TO_INT(py_name, c_name) \
|
||||
STATIC mp_obj_t mp_math_##py_name(mp_obj_t x_obj) { return mp_obj_new_int_from_float(MICROPY_FLOAT_C_FUN(c_name)(mp_obj_get_float(x_obj))); } \
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_math_##py_name##_obj, mp_math_##py_name);
|
||||
|
||||
#define MATH_FUN_2(py_name, c_name) \
|
||||
STATIC mp_obj_t mp_math_##py_name(mp_obj_t x_obj, mp_obj_t y_obj) { \
|
||||
return math_generic_2(x_obj, y_obj, MICROPY_FLOAT_C_FUN(c_name)); \
|
||||
} \
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(mp_math_##py_name##_obj, mp_math_##py_name);
|
||||
|
||||
#define MATH_FUN_2_FLT_INT(py_name, c_name) \
|
||||
STATIC mp_obj_t mp_math_##py_name(mp_obj_t x_obj, mp_obj_t y_obj) { \
|
||||
return mp_obj_new_float(MICROPY_FLOAT_C_FUN(c_name)(mp_obj_get_float(x_obj), mp_obj_get_int(y_obj))); \
|
||||
} \
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(mp_math_##py_name##_obj, mp_math_##py_name);
|
||||
|
||||
#if MP_NEED_LOG2
|
||||
#undef log2
|
||||
#undef log2f
|
||||
// 1.442695040888963407354163704 is 1/_M_LN2
|
||||
mp_float_t MICROPY_FLOAT_C_FUN(log2)(mp_float_t x) {
|
||||
return MICROPY_FLOAT_C_FUN(log)(x) * MICROPY_FLOAT_CONST(1.442695040888963407354163704);
|
||||
}
|
||||
#endif
|
||||
|
||||
// sqrt(x): returns the square root of x
|
||||
MATH_FUN_1(sqrt, sqrt)
|
||||
// pow(x, y): returns x to the power of y
|
||||
#if MICROPY_PY_MATH_POW_FIX_NAN
|
||||
mp_float_t pow_func(mp_float_t x, mp_float_t y) {
|
||||
// pow(base, 0) returns 1 for any base, even when base is NaN
|
||||
// pow(+1, exponent) returns 1 for any exponent, even when exponent is NaN
|
||||
if (x == MICROPY_FLOAT_CONST(1.0) || y == MICROPY_FLOAT_CONST(0.0)) {
|
||||
return MICROPY_FLOAT_CONST(1.0);
|
||||
}
|
||||
return MICROPY_FLOAT_C_FUN(pow)(x, y);
|
||||
}
|
||||
MATH_FUN_2(pow, pow_func)
|
||||
#else
|
||||
MATH_FUN_2(pow, pow)
|
||||
#endif
|
||||
// exp(x)
|
||||
MATH_FUN_1(exp, exp)
|
||||
#if MICROPY_PY_MATH_SPECIAL_FUNCTIONS
|
||||
// expm1(x)
|
||||
MATH_FUN_1(expm1, expm1)
|
||||
// log2(x)
|
||||
MATH_FUN_1(log2, log2)
|
||||
// log10(x)
|
||||
MATH_FUN_1(log10, log10)
|
||||
// cosh(x)
|
||||
MATH_FUN_1(cosh, cosh)
|
||||
// sinh(x)
|
||||
MATH_FUN_1(sinh, sinh)
|
||||
// tanh(x)
|
||||
MATH_FUN_1(tanh, tanh)
|
||||
// acosh(x)
|
||||
MATH_FUN_1(acosh, acosh)
|
||||
// asinh(x)
|
||||
MATH_FUN_1(asinh, asinh)
|
||||
// atanh(x)
|
||||
MATH_FUN_1(atanh, atanh)
|
||||
#endif
|
||||
// cos(x)
|
||||
MATH_FUN_1(cos, cos)
|
||||
// sin(x)
|
||||
MATH_FUN_1(sin, sin)
|
||||
// tan(x)
|
||||
MATH_FUN_1(tan, tan)
|
||||
// acos(x)
|
||||
MATH_FUN_1(acos, acos)
|
||||
// asin(x)
|
||||
MATH_FUN_1(asin, asin)
|
||||
// atan(x)
|
||||
MATH_FUN_1(atan, atan)
|
||||
// atan2(y, x)
|
||||
#if MICROPY_PY_MATH_ATAN2_FIX_INFNAN
|
||||
mp_float_t atan2_func(mp_float_t x, mp_float_t y) {
|
||||
if (isinf(x) && isinf(y)) {
|
||||
return copysign(y < 0 ? MP_3_PI_4 : MP_PI_4, x);
|
||||
}
|
||||
return atan2(x, y);
|
||||
}
|
||||
MATH_FUN_2(atan2, atan2_func)
|
||||
#else
|
||||
MATH_FUN_2(atan2, atan2)
|
||||
#endif
|
||||
// ceil(x)
|
||||
MATH_FUN_1_TO_INT(ceil, ceil)
|
||||
// copysign(x, y)
|
||||
STATIC mp_float_t MICROPY_FLOAT_C_FUN(copysign_func)(mp_float_t x, mp_float_t y) {
|
||||
return MICROPY_FLOAT_C_FUN(copysign)(x, y);
|
||||
}
|
||||
MATH_FUN_2(copysign, copysign_func)
|
||||
// fabs(x)
|
||||
STATIC mp_float_t MICROPY_FLOAT_C_FUN(fabs_func)(mp_float_t x) {
|
||||
return MICROPY_FLOAT_C_FUN(fabs)(x);
|
||||
}
|
||||
MATH_FUN_1(fabs, fabs_func)
|
||||
// floor(x)
|
||||
MATH_FUN_1_TO_INT(floor, floor) // TODO: delegate to x.__floor__() if x is not a float
|
||||
// fmod(x, y)
|
||||
#if MICROPY_PY_MATH_FMOD_FIX_INFNAN
|
||||
mp_float_t fmod_func(mp_float_t x, mp_float_t y) {
|
||||
return (!isinf(x) && isinf(y)) ? x : fmod(x, y);
|
||||
}
|
||||
MATH_FUN_2(fmod, fmod_func)
|
||||
#else
|
||||
MATH_FUN_2(fmod, fmod)
|
||||
#endif
|
||||
// isfinite(x)
|
||||
MATH_FUN_1_TO_BOOL(isfinite, isfinite)
|
||||
// isinf(x)
|
||||
MATH_FUN_1_TO_BOOL(isinf, isinf)
|
||||
// isnan(x)
|
||||
MATH_FUN_1_TO_BOOL(isnan, isnan)
|
||||
// trunc(x)
|
||||
MATH_FUN_1_TO_INT(trunc, trunc)
|
||||
// ldexp(x, exp)
|
||||
MATH_FUN_2_FLT_INT(ldexp, ldexp)
|
||||
#if MICROPY_PY_MATH_SPECIAL_FUNCTIONS
|
||||
// erf(x): return the error function of x
|
||||
MATH_FUN_1(erf, erf)
|
||||
// erfc(x): return the complementary error function of x
|
||||
MATH_FUN_1(erfc, erfc)
|
||||
// gamma(x): return the gamma function of x
|
||||
MATH_FUN_1(gamma, tgamma)
|
||||
// lgamma(x): return the natural logarithm of the gamma function of x
|
||||
MATH_FUN_1(lgamma, lgamma)
|
||||
#endif
|
||||
// TODO: fsum
|
||||
|
||||
#if MICROPY_PY_MATH_ISCLOSE
|
||||
STATIC mp_obj_t mp_math_isclose(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
|
||||
enum { ARG_a, ARG_b, ARG_rel_tol, ARG_abs_tol };
|
||||
static const mp_arg_t allowed_args[] = {
|
||||
{MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}},
|
||||
{MP_QSTR_, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}},
|
||||
{MP_QSTR_rel_tol, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}},
|
||||
{MP_QSTR_abs_tol, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NEW_SMALL_INT(0)}},
|
||||
};
|
||||
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
|
||||
mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
|
||||
const mp_float_t a = mp_obj_get_float(args[ARG_a].u_obj);
|
||||
const mp_float_t b = mp_obj_get_float(args[ARG_b].u_obj);
|
||||
const mp_float_t rel_tol = args[ARG_rel_tol].u_obj == MP_OBJ_NULL
|
||||
? (mp_float_t)1e-9 : mp_obj_get_float(args[ARG_rel_tol].u_obj);
|
||||
const mp_float_t abs_tol = mp_obj_get_float(args[ARG_abs_tol].u_obj);
|
||||
if (rel_tol < (mp_float_t)0.0 || abs_tol < (mp_float_t)0.0) {
|
||||
math_error();
|
||||
}
|
||||
if (a == b) {
|
||||
return mp_const_true;
|
||||
}
|
||||
const mp_float_t difference = MICROPY_FLOAT_C_FUN(fabs)(a - b);
|
||||
if (isinf(difference)) { // Either a or b is inf
|
||||
return mp_const_false;
|
||||
}
|
||||
if ((difference <= abs_tol) ||
|
||||
(difference <= MICROPY_FLOAT_C_FUN(fabs)(rel_tol * a)) ||
|
||||
(difference <= MICROPY_FLOAT_C_FUN(fabs)(rel_tol * b))) {
|
||||
return mp_const_true;
|
||||
}
|
||||
return mp_const_false;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_KW(mp_math_isclose_obj, 2, mp_math_isclose);
|
||||
#endif
|
||||
|
||||
// Function that takes a variable number of arguments
|
||||
|
||||
// log(x[, base])
|
||||
STATIC mp_obj_t mp_math_log(size_t n_args, const mp_obj_t *args) {
|
||||
mp_float_t x = mp_obj_get_float(args[0]);
|
||||
if (x <= (mp_float_t)0.0) {
|
||||
math_error();
|
||||
}
|
||||
mp_float_t l = MICROPY_FLOAT_C_FUN(log)(x);
|
||||
if (n_args == 1) {
|
||||
return mp_obj_new_float(l);
|
||||
} else {
|
||||
mp_float_t base = mp_obj_get_float(args[1]);
|
||||
if (base <= (mp_float_t)0.0) {
|
||||
math_error();
|
||||
} else if (base == (mp_float_t)1.0) {
|
||||
mp_raise_msg(&mp_type_ZeroDivisionError, MP_ERROR_TEXT("divide by zero"));
|
||||
}
|
||||
return mp_obj_new_float(l / MICROPY_FLOAT_C_FUN(log)(base));
|
||||
}
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_math_log_obj, 1, 2, mp_math_log);
|
||||
|
||||
// Functions that return a tuple
|
||||
|
||||
// frexp(x): converts a floating-point number to fractional and integral components
|
||||
STATIC mp_obj_t mp_math_frexp(mp_obj_t x_obj) {
|
||||
int int_exponent = 0;
|
||||
mp_float_t significand = MICROPY_FLOAT_C_FUN(frexp)(mp_obj_get_float(x_obj), &int_exponent);
|
||||
mp_obj_t tuple[2];
|
||||
tuple[0] = mp_obj_new_float(significand);
|
||||
tuple[1] = mp_obj_new_int(int_exponent);
|
||||
return mp_obj_new_tuple(2, tuple);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_math_frexp_obj, mp_math_frexp);
|
||||
|
||||
// modf(x)
|
||||
STATIC mp_obj_t mp_math_modf(mp_obj_t x_obj) {
|
||||
mp_float_t int_part = 0.0;
|
||||
mp_float_t x = mp_obj_get_float(x_obj);
|
||||
mp_float_t fractional_part = MICROPY_FLOAT_C_FUN(modf)(x, &int_part);
|
||||
#if MICROPY_PY_MATH_MODF_FIX_NEGZERO
|
||||
if (fractional_part == MICROPY_FLOAT_CONST(0.0)) {
|
||||
fractional_part = copysign(fractional_part, x);
|
||||
}
|
||||
#endif
|
||||
mp_obj_t tuple[2];
|
||||
tuple[0] = mp_obj_new_float(fractional_part);
|
||||
tuple[1] = mp_obj_new_float(int_part);
|
||||
return mp_obj_new_tuple(2, tuple);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_math_modf_obj, mp_math_modf);
|
||||
|
||||
// Angular conversions
|
||||
|
||||
// radians(x)
|
||||
STATIC mp_obj_t mp_math_radians(mp_obj_t x_obj) {
|
||||
return mp_obj_new_float(mp_obj_get_float(x_obj) * (MP_PI / MICROPY_FLOAT_CONST(180.0)));
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_math_radians_obj, mp_math_radians);
|
||||
|
||||
// degrees(x)
|
||||
STATIC mp_obj_t mp_math_degrees(mp_obj_t x_obj) {
|
||||
return mp_obj_new_float(mp_obj_get_float(x_obj) * (MICROPY_FLOAT_CONST(180.0) / MP_PI));
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_math_degrees_obj, mp_math_degrees);
|
||||
|
||||
#if MICROPY_PY_MATH_FACTORIAL
|
||||
|
||||
#if MICROPY_OPT_MATH_FACTORIAL
|
||||
|
||||
// factorial(x): slightly efficient recursive implementation
|
||||
STATIC mp_obj_t mp_math_factorial_inner(mp_uint_t start, mp_uint_t end) {
|
||||
if (start == end) {
|
||||
return mp_obj_new_int(start);
|
||||
} else if (end - start == 1) {
|
||||
return mp_binary_op(MP_BINARY_OP_MULTIPLY, MP_OBJ_NEW_SMALL_INT(start), MP_OBJ_NEW_SMALL_INT(end));
|
||||
} else if (end - start == 2) {
|
||||
mp_obj_t left = MP_OBJ_NEW_SMALL_INT(start);
|
||||
mp_obj_t middle = MP_OBJ_NEW_SMALL_INT(start + 1);
|
||||
mp_obj_t right = MP_OBJ_NEW_SMALL_INT(end);
|
||||
mp_obj_t tmp = mp_binary_op(MP_BINARY_OP_MULTIPLY, left, middle);
|
||||
return mp_binary_op(MP_BINARY_OP_MULTIPLY, tmp, right);
|
||||
} else {
|
||||
mp_uint_t middle = start + ((end - start) >> 1);
|
||||
mp_obj_t left = mp_math_factorial_inner(start, middle);
|
||||
mp_obj_t right = mp_math_factorial_inner(middle + 1, end);
|
||||
return mp_binary_op(MP_BINARY_OP_MULTIPLY, left, right);
|
||||
}
|
||||
}
|
||||
STATIC mp_obj_t mp_math_factorial(mp_obj_t x_obj) {
|
||||
mp_int_t max = mp_obj_get_int(x_obj);
|
||||
if (max < 0) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("negative factorial"));
|
||||
} else if (max == 0) {
|
||||
return MP_OBJ_NEW_SMALL_INT(1);
|
||||
}
|
||||
return mp_math_factorial_inner(1, max);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
// factorial(x): squared difference implementation
|
||||
// based on http://www.luschny.de/math/factorial/index.html
|
||||
STATIC mp_obj_t mp_math_factorial(mp_obj_t x_obj) {
|
||||
mp_int_t max = mp_obj_get_int(x_obj);
|
||||
if (max < 0) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("negative factorial"));
|
||||
} else if (max <= 1) {
|
||||
return MP_OBJ_NEW_SMALL_INT(1);
|
||||
}
|
||||
mp_int_t h = max >> 1;
|
||||
mp_int_t q = h * h;
|
||||
mp_int_t r = q << 1;
|
||||
if (max & 1) {
|
||||
r *= max;
|
||||
}
|
||||
mp_obj_t prod = MP_OBJ_NEW_SMALL_INT(r);
|
||||
for (mp_int_t num = 1; num < max - 2; num += 2) {
|
||||
q -= num;
|
||||
prod = mp_binary_op(MP_BINARY_OP_MULTIPLY, prod, MP_OBJ_NEW_SMALL_INT(q));
|
||||
}
|
||||
return prod;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_math_factorial_obj, mp_math_factorial);
|
||||
|
||||
#endif
|
||||
|
||||
STATIC const mp_rom_map_elem_t mp_module_math_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_math) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_e), mp_const_float_e },
|
||||
{ MP_ROM_QSTR(MP_QSTR_pi), mp_const_float_pi },
|
||||
{ MP_ROM_QSTR(MP_QSTR_sqrt), MP_ROM_PTR(&mp_math_sqrt_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_pow), MP_ROM_PTR(&mp_math_pow_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_exp), MP_ROM_PTR(&mp_math_exp_obj) },
|
||||
#if MICROPY_PY_MATH_SPECIAL_FUNCTIONS
|
||||
{ MP_ROM_QSTR(MP_QSTR_expm1), MP_ROM_PTR(&mp_math_expm1_obj) },
|
||||
#endif
|
||||
{ MP_ROM_QSTR(MP_QSTR_log), MP_ROM_PTR(&mp_math_log_obj) },
|
||||
#if MICROPY_PY_MATH_SPECIAL_FUNCTIONS
|
||||
{ MP_ROM_QSTR(MP_QSTR_log2), MP_ROM_PTR(&mp_math_log2_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_log10), MP_ROM_PTR(&mp_math_log10_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_cosh), MP_ROM_PTR(&mp_math_cosh_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_sinh), MP_ROM_PTR(&mp_math_sinh_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_tanh), MP_ROM_PTR(&mp_math_tanh_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_acosh), MP_ROM_PTR(&mp_math_acosh_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_asinh), MP_ROM_PTR(&mp_math_asinh_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_atanh), MP_ROM_PTR(&mp_math_atanh_obj) },
|
||||
#endif
|
||||
{ MP_ROM_QSTR(MP_QSTR_cos), MP_ROM_PTR(&mp_math_cos_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_sin), MP_ROM_PTR(&mp_math_sin_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_tan), MP_ROM_PTR(&mp_math_tan_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_acos), MP_ROM_PTR(&mp_math_acos_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_asin), MP_ROM_PTR(&mp_math_asin_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_atan), MP_ROM_PTR(&mp_math_atan_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_atan2), MP_ROM_PTR(&mp_math_atan2_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_ceil), MP_ROM_PTR(&mp_math_ceil_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_copysign), MP_ROM_PTR(&mp_math_copysign_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_fabs), MP_ROM_PTR(&mp_math_fabs_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_floor), MP_ROM_PTR(&mp_math_floor_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_fmod), MP_ROM_PTR(&mp_math_fmod_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_frexp), MP_ROM_PTR(&mp_math_frexp_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_ldexp), MP_ROM_PTR(&mp_math_ldexp_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_modf), MP_ROM_PTR(&mp_math_modf_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_isfinite), MP_ROM_PTR(&mp_math_isfinite_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_isinf), MP_ROM_PTR(&mp_math_isinf_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_isnan), MP_ROM_PTR(&mp_math_isnan_obj) },
|
||||
#if MICROPY_PY_MATH_ISCLOSE
|
||||
{ MP_ROM_QSTR(MP_QSTR_isclose), MP_ROM_PTR(&mp_math_isclose_obj) },
|
||||
#endif
|
||||
{ MP_ROM_QSTR(MP_QSTR_trunc), MP_ROM_PTR(&mp_math_trunc_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_radians), MP_ROM_PTR(&mp_math_radians_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_degrees), MP_ROM_PTR(&mp_math_degrees_obj) },
|
||||
#if MICROPY_PY_MATH_FACTORIAL
|
||||
{ MP_ROM_QSTR(MP_QSTR_factorial), MP_ROM_PTR(&mp_math_factorial_obj) },
|
||||
#endif
|
||||
#if MICROPY_PY_MATH_SPECIAL_FUNCTIONS
|
||||
{ MP_ROM_QSTR(MP_QSTR_erf), MP_ROM_PTR(&mp_math_erf_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_erfc), MP_ROM_PTR(&mp_math_erfc_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_gamma), MP_ROM_PTR(&mp_math_gamma_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_lgamma), MP_ROM_PTR(&mp_math_lgamma_obj) },
|
||||
#endif
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(mp_module_math_globals, mp_module_math_globals_table);
|
||||
|
||||
const mp_obj_module_t mp_module_math = {
|
||||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t *)&mp_module_math_globals,
|
||||
};
|
||||
|
||||
#endif // MICROPY_PY_BUILTINS_FLOAT && MICROPY_PY_MATH
|
||||
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013, 2014 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "py/builtin.h"
|
||||
#include "py/stackctrl.h"
|
||||
#include "py/runtime.h"
|
||||
#include "py/gc.h"
|
||||
#include "py/mphal.h"
|
||||
|
||||
// Various builtins specific to MicroPython runtime,
|
||||
// living in micropython module
|
||||
|
||||
#if MICROPY_ENABLE_COMPILER
|
||||
STATIC mp_obj_t mp_micropython_opt_level(size_t n_args, const mp_obj_t *args) {
|
||||
if (n_args == 0) {
|
||||
return MP_OBJ_NEW_SMALL_INT(MP_STATE_VM(mp_optimise_value));
|
||||
} else {
|
||||
MP_STATE_VM(mp_optimise_value) = mp_obj_get_int(args[0]);
|
||||
return mp_const_none;
|
||||
}
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_micropython_opt_level_obj, 0, 1, mp_micropython_opt_level);
|
||||
#endif
|
||||
|
||||
#if MICROPY_PY_MICROPYTHON_MEM_INFO
|
||||
|
||||
#if MICROPY_MEM_STATS
|
||||
STATIC mp_obj_t mp_micropython_mem_total(void) {
|
||||
return MP_OBJ_NEW_SMALL_INT(m_get_total_bytes_allocated());
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_micropython_mem_total_obj, mp_micropython_mem_total);
|
||||
|
||||
STATIC mp_obj_t mp_micropython_mem_current(void) {
|
||||
return MP_OBJ_NEW_SMALL_INT(m_get_current_bytes_allocated());
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_micropython_mem_current_obj, mp_micropython_mem_current);
|
||||
|
||||
STATIC mp_obj_t mp_micropython_mem_peak(void) {
|
||||
return MP_OBJ_NEW_SMALL_INT(m_get_peak_bytes_allocated());
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_micropython_mem_peak_obj, mp_micropython_mem_peak);
|
||||
#endif
|
||||
|
||||
mp_obj_t mp_micropython_mem_info(size_t n_args, const mp_obj_t *args) {
|
||||
(void)args;
|
||||
#if MICROPY_MEM_STATS
|
||||
mp_printf(&mp_plat_print, "mem: total=" UINT_FMT ", current=" UINT_FMT ", peak=" UINT_FMT "\n",
|
||||
(mp_uint_t)m_get_total_bytes_allocated(), (mp_uint_t)m_get_current_bytes_allocated(), (mp_uint_t)m_get_peak_bytes_allocated());
|
||||
#endif
|
||||
#if MICROPY_STACK_CHECK
|
||||
mp_printf(&mp_plat_print, "stack: " UINT_FMT " out of " UINT_FMT "\n",
|
||||
mp_stack_usage(), (mp_uint_t)MP_STATE_THREAD(stack_limit));
|
||||
#else
|
||||
mp_printf(&mp_plat_print, "stack: " UINT_FMT "\n", mp_stack_usage());
|
||||
#endif
|
||||
#if MICROPY_ENABLE_GC
|
||||
gc_dump_info();
|
||||
if (n_args == 1) {
|
||||
// arg given means dump gc allocation table
|
||||
gc_dump_alloc_table();
|
||||
}
|
||||
#else
|
||||
(void)n_args;
|
||||
#endif
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_micropython_mem_info_obj, 0, 1, mp_micropython_mem_info);
|
||||
|
||||
STATIC mp_obj_t mp_micropython_qstr_info(size_t n_args, const mp_obj_t *args) {
|
||||
(void)args;
|
||||
size_t n_pool, n_qstr, n_str_data_bytes, n_total_bytes;
|
||||
qstr_pool_info(&n_pool, &n_qstr, &n_str_data_bytes, &n_total_bytes);
|
||||
mp_printf(&mp_plat_print, "qstr pool: n_pool=%u, n_qstr=%u, n_str_data_bytes=%u, n_total_bytes=%u\n",
|
||||
n_pool, n_qstr, n_str_data_bytes, n_total_bytes);
|
||||
if (n_args == 1) {
|
||||
// arg given means dump qstr data
|
||||
qstr_dump_data();
|
||||
}
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_micropython_qstr_info_obj, 0, 1, mp_micropython_qstr_info);
|
||||
|
||||
#endif // MICROPY_PY_MICROPYTHON_MEM_INFO
|
||||
|
||||
#if MICROPY_PY_MICROPYTHON_STACK_USE
|
||||
STATIC mp_obj_t mp_micropython_stack_use(void) {
|
||||
return MP_OBJ_NEW_SMALL_INT(mp_stack_usage());
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_micropython_stack_use_obj, mp_micropython_stack_use);
|
||||
#endif
|
||||
|
||||
#if MICROPY_ENABLE_PYSTACK
|
||||
STATIC mp_obj_t mp_micropython_pystack_use(void) {
|
||||
return MP_OBJ_NEW_SMALL_INT(mp_pystack_usage());
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_micropython_pystack_use_obj, mp_micropython_pystack_use);
|
||||
#endif
|
||||
|
||||
#if MICROPY_ENABLE_GC
|
||||
STATIC mp_obj_t mp_micropython_heap_lock(void) {
|
||||
gc_lock();
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_micropython_heap_lock_obj, mp_micropython_heap_lock);
|
||||
|
||||
STATIC mp_obj_t mp_micropython_heap_unlock(void) {
|
||||
gc_unlock();
|
||||
return MP_OBJ_NEW_SMALL_INT(MP_STATE_MEM(gc_lock_depth));
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_micropython_heap_unlock_obj, mp_micropython_heap_unlock);
|
||||
|
||||
#if MICROPY_PY_MICROPYTHON_HEAP_LOCKED
|
||||
STATIC mp_obj_t mp_micropython_heap_locked(void) {
|
||||
return MP_OBJ_NEW_SMALL_INT(MP_STATE_MEM(gc_lock_depth));
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_micropython_heap_locked_obj, mp_micropython_heap_locked);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF && (MICROPY_EMERGENCY_EXCEPTION_BUF_SIZE == 0)
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_alloc_emergency_exception_buf_obj, mp_alloc_emergency_exception_buf);
|
||||
#endif
|
||||
|
||||
#if MICROPY_KBD_EXCEPTION
|
||||
STATIC mp_obj_t mp_micropython_kbd_intr(mp_obj_t int_chr_in) {
|
||||
mp_hal_set_interrupt_char(mp_obj_get_int(int_chr_in));
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_micropython_kbd_intr_obj, mp_micropython_kbd_intr);
|
||||
#endif
|
||||
|
||||
#if MICROPY_ENABLE_SCHEDULER
|
||||
STATIC mp_obj_t mp_micropython_schedule(mp_obj_t function, mp_obj_t arg) {
|
||||
if (!mp_sched_schedule(function, arg)) {
|
||||
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("schedule queue full"));
|
||||
}
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(mp_micropython_schedule_obj, mp_micropython_schedule);
|
||||
#endif
|
||||
|
||||
STATIC const mp_rom_map_elem_t mp_module_micropython_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_micropython) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_const), MP_ROM_PTR(&mp_identity_obj) },
|
||||
#if MICROPY_ENABLE_COMPILER
|
||||
{ MP_ROM_QSTR(MP_QSTR_opt_level), MP_ROM_PTR(&mp_micropython_opt_level_obj) },
|
||||
#endif
|
||||
#if MICROPY_PY_MICROPYTHON_MEM_INFO
|
||||
#if MICROPY_MEM_STATS
|
||||
{ MP_ROM_QSTR(MP_QSTR_mem_total), MP_ROM_PTR(&mp_micropython_mem_total_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_mem_current), MP_ROM_PTR(&mp_micropython_mem_current_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_mem_peak), MP_ROM_PTR(&mp_micropython_mem_peak_obj) },
|
||||
#endif
|
||||
{ MP_ROM_QSTR(MP_QSTR_mem_info), MP_ROM_PTR(&mp_micropython_mem_info_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_qstr_info), MP_ROM_PTR(&mp_micropython_qstr_info_obj) },
|
||||
#endif
|
||||
#if MICROPY_PY_MICROPYTHON_STACK_USE
|
||||
{ MP_ROM_QSTR(MP_QSTR_stack_use), MP_ROM_PTR(&mp_micropython_stack_use_obj) },
|
||||
#endif
|
||||
#if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF && (MICROPY_EMERGENCY_EXCEPTION_BUF_SIZE == 0)
|
||||
{ MP_ROM_QSTR(MP_QSTR_alloc_emergency_exception_buf), MP_ROM_PTR(&mp_alloc_emergency_exception_buf_obj) },
|
||||
#endif
|
||||
#if MICROPY_ENABLE_PYSTACK
|
||||
{ MP_ROM_QSTR(MP_QSTR_pystack_use), MP_ROM_PTR(&mp_micropython_pystack_use_obj) },
|
||||
#endif
|
||||
#if MICROPY_ENABLE_GC
|
||||
{ MP_ROM_QSTR(MP_QSTR_heap_lock), MP_ROM_PTR(&mp_micropython_heap_lock_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_heap_unlock), MP_ROM_PTR(&mp_micropython_heap_unlock_obj) },
|
||||
#if MICROPY_PY_MICROPYTHON_HEAP_LOCKED
|
||||
{ MP_ROM_QSTR(MP_QSTR_heap_locked), MP_ROM_PTR(&mp_micropython_heap_locked_obj) },
|
||||
#endif
|
||||
#endif
|
||||
#if MICROPY_KBD_EXCEPTION
|
||||
{ MP_ROM_QSTR(MP_QSTR_kbd_intr), MP_ROM_PTR(&mp_micropython_kbd_intr_obj) },
|
||||
#endif
|
||||
#if MICROPY_ENABLE_SCHEDULER
|
||||
{ MP_ROM_QSTR(MP_QSTR_schedule), MP_ROM_PTR(&mp_micropython_schedule_obj) },
|
||||
#endif
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(mp_module_micropython_globals, mp_module_micropython_globals_table);
|
||||
|
||||
const mp_obj_module_t mp_module_micropython = {
|
||||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t *)&mp_module_micropython_globals,
|
||||
};
|
||||
@@ -0,0 +1,269 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013, 2014 Damien P. George
|
||||
* Copyright (c) 2014 Paul Sokolovsky
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "py/runtime.h"
|
||||
#include "py/builtin.h"
|
||||
#include "py/objtuple.h"
|
||||
#include "py/binary.h"
|
||||
#include "py/parsenum.h"
|
||||
|
||||
#if MICROPY_PY_STRUCT
|
||||
|
||||
/*
|
||||
This module implements most of character typecodes from CPython, with
|
||||
some extensions:
|
||||
|
||||
O - (Pointer to) an arbitrary Python object. This is useful for callback
|
||||
data, etc. Note that you must keep reference to passed object in
|
||||
your Python application, otherwise it may be garbage-collected,
|
||||
and then when you get back this value from callback it may be
|
||||
invalid (and lead to crash).
|
||||
S - Pointer to a string (returned as a Python string). Note the
|
||||
difference from "Ns", - the latter says "in this place of structure
|
||||
is character data of up to N bytes length", while "S" means
|
||||
"in this place of a structure is a pointer to zero-terminated
|
||||
character data".
|
||||
*/
|
||||
|
||||
STATIC char get_fmt_type(const char **fmt) {
|
||||
char t = **fmt;
|
||||
switch (t) {
|
||||
case '!':
|
||||
t = '>';
|
||||
break;
|
||||
case '@':
|
||||
case '=':
|
||||
case '<':
|
||||
case '>':
|
||||
break;
|
||||
default:
|
||||
return '@';
|
||||
}
|
||||
// Skip type char
|
||||
(*fmt)++;
|
||||
return t;
|
||||
}
|
||||
|
||||
STATIC mp_uint_t get_fmt_num(const char **p) {
|
||||
const char *num = *p;
|
||||
uint len = 1;
|
||||
while (unichar_isdigit(*++num)) {
|
||||
len++;
|
||||
}
|
||||
mp_uint_t val = (mp_uint_t)MP_OBJ_SMALL_INT_VALUE(mp_parse_num_integer(*p, len, 10, NULL));
|
||||
*p = num;
|
||||
return val;
|
||||
}
|
||||
|
||||
STATIC size_t calc_size_items(const char *fmt, size_t *total_sz) {
|
||||
char fmt_type = get_fmt_type(&fmt);
|
||||
size_t total_cnt = 0;
|
||||
size_t size;
|
||||
for (size = 0; *fmt; fmt++) {
|
||||
mp_uint_t cnt = 1;
|
||||
if (unichar_isdigit(*fmt)) {
|
||||
cnt = get_fmt_num(&fmt);
|
||||
}
|
||||
|
||||
if (*fmt == 's') {
|
||||
total_cnt += 1;
|
||||
size += cnt;
|
||||
} else {
|
||||
total_cnt += cnt;
|
||||
size_t align;
|
||||
size_t sz = mp_binary_get_size(fmt_type, *fmt, &align);
|
||||
while (cnt--) {
|
||||
// Apply alignment
|
||||
size = (size + align - 1) & ~(align - 1);
|
||||
size += sz;
|
||||
}
|
||||
}
|
||||
}
|
||||
*total_sz = size;
|
||||
return total_cnt;
|
||||
}
|
||||
|
||||
STATIC mp_obj_t struct_calcsize(mp_obj_t fmt_in) {
|
||||
const char *fmt = mp_obj_str_get_str(fmt_in);
|
||||
size_t size;
|
||||
calc_size_items(fmt, &size);
|
||||
return MP_OBJ_NEW_SMALL_INT(size);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(struct_calcsize_obj, struct_calcsize);
|
||||
|
||||
STATIC mp_obj_t struct_unpack_from(size_t n_args, const mp_obj_t *args) {
|
||||
// unpack requires that the buffer be exactly the right size.
|
||||
// unpack_from requires that the buffer be "big enough".
|
||||
// Since we implement unpack and unpack_from using the same function
|
||||
// we relax the "exact" requirement, and only implement "big enough".
|
||||
const char *fmt = mp_obj_str_get_str(args[0]);
|
||||
size_t total_sz;
|
||||
size_t num_items = calc_size_items(fmt, &total_sz);
|
||||
char fmt_type = get_fmt_type(&fmt);
|
||||
mp_obj_tuple_t *res = MP_OBJ_TO_PTR(mp_obj_new_tuple(num_items, NULL));
|
||||
mp_buffer_info_t bufinfo;
|
||||
mp_get_buffer_raise(args[1], &bufinfo, MP_BUFFER_READ);
|
||||
byte *p = bufinfo.buf;
|
||||
byte *end_p = &p[bufinfo.len];
|
||||
mp_int_t offset = 0;
|
||||
|
||||
if (n_args > 2) {
|
||||
// offset arg provided
|
||||
offset = mp_obj_get_int(args[2]);
|
||||
if (offset < 0) {
|
||||
// negative offsets are relative to the end of the buffer
|
||||
offset = bufinfo.len + offset;
|
||||
if (offset < 0) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("buffer too small"));
|
||||
}
|
||||
}
|
||||
p += offset;
|
||||
}
|
||||
byte *p_base = p;
|
||||
|
||||
// Check that the input buffer is big enough to unpack all the values
|
||||
if (p + total_sz > end_p) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("buffer too small"));
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < num_items;) {
|
||||
mp_uint_t cnt = 1;
|
||||
if (unichar_isdigit(*fmt)) {
|
||||
cnt = get_fmt_num(&fmt);
|
||||
}
|
||||
mp_obj_t item;
|
||||
if (*fmt == 's') {
|
||||
item = mp_obj_new_bytes(p, cnt);
|
||||
p += cnt;
|
||||
res->items[i++] = item;
|
||||
} else {
|
||||
while (cnt--) {
|
||||
item = mp_binary_get_val(fmt_type, *fmt, p_base, &p);
|
||||
res->items[i++] = item;
|
||||
}
|
||||
}
|
||||
fmt++;
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(res);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(struct_unpack_from_obj, 2, 3, struct_unpack_from);
|
||||
|
||||
// This function assumes there is enough room in p to store all the values
|
||||
STATIC void struct_pack_into_internal(mp_obj_t fmt_in, byte *p, size_t n_args, const mp_obj_t *args) {
|
||||
const char *fmt = mp_obj_str_get_str(fmt_in);
|
||||
char fmt_type = get_fmt_type(&fmt);
|
||||
|
||||
byte *p_base = p;
|
||||
size_t i;
|
||||
for (i = 0; i < n_args;) {
|
||||
mp_uint_t cnt = 1;
|
||||
if (*fmt == '\0') {
|
||||
// more arguments given than used by format string; CPython raises struct.error here
|
||||
break;
|
||||
}
|
||||
if (unichar_isdigit(*fmt)) {
|
||||
cnt = get_fmt_num(&fmt);
|
||||
}
|
||||
|
||||
if (*fmt == 's') {
|
||||
mp_buffer_info_t bufinfo;
|
||||
mp_get_buffer_raise(args[i++], &bufinfo, MP_BUFFER_READ);
|
||||
mp_uint_t to_copy = cnt;
|
||||
if (bufinfo.len < to_copy) {
|
||||
to_copy = bufinfo.len;
|
||||
}
|
||||
memcpy(p, bufinfo.buf, to_copy);
|
||||
memset(p + to_copy, 0, cnt - to_copy);
|
||||
p += cnt;
|
||||
} else {
|
||||
// If we run out of args then we just finish; CPython would raise struct.error
|
||||
while (cnt-- && i < n_args) {
|
||||
mp_binary_set_val(fmt_type, *fmt, args[i++], p_base, &p);
|
||||
}
|
||||
}
|
||||
fmt++;
|
||||
}
|
||||
}
|
||||
|
||||
STATIC mp_obj_t struct_pack(size_t n_args, const mp_obj_t *args) {
|
||||
// TODO: "The arguments must match the values required by the format exactly."
|
||||
mp_int_t size = MP_OBJ_SMALL_INT_VALUE(struct_calcsize(args[0]));
|
||||
vstr_t vstr;
|
||||
vstr_init_len(&vstr, size);
|
||||
byte *p = (byte *)vstr.buf;
|
||||
memset(p, 0, size);
|
||||
struct_pack_into_internal(args[0], p, n_args - 1, &args[1]);
|
||||
return mp_obj_new_str_from_vstr(&mp_type_bytes, &vstr);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(struct_pack_obj, 1, MP_OBJ_FUN_ARGS_MAX, struct_pack);
|
||||
|
||||
STATIC mp_obj_t struct_pack_into(size_t n_args, const mp_obj_t *args) {
|
||||
mp_buffer_info_t bufinfo;
|
||||
mp_get_buffer_raise(args[1], &bufinfo, MP_BUFFER_WRITE);
|
||||
mp_int_t offset = mp_obj_get_int(args[2]);
|
||||
if (offset < 0) {
|
||||
// negative offsets are relative to the end of the buffer
|
||||
offset = (mp_int_t)bufinfo.len + offset;
|
||||
if (offset < 0) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("buffer too small"));
|
||||
}
|
||||
}
|
||||
byte *p = (byte *)bufinfo.buf;
|
||||
byte *end_p = &p[bufinfo.len];
|
||||
p += offset;
|
||||
|
||||
// Check that the output buffer is big enough to hold all the values
|
||||
mp_int_t sz = MP_OBJ_SMALL_INT_VALUE(struct_calcsize(args[0]));
|
||||
if (p + sz > end_p) {
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("buffer too small"));
|
||||
}
|
||||
|
||||
struct_pack_into_internal(args[0], p, n_args - 3, &args[3]);
|
||||
return mp_const_none;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(struct_pack_into_obj, 3, MP_OBJ_FUN_ARGS_MAX, struct_pack_into);
|
||||
|
||||
STATIC const mp_rom_map_elem_t mp_module_struct_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_ustruct) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_calcsize), MP_ROM_PTR(&struct_calcsize_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_pack), MP_ROM_PTR(&struct_pack_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_pack_into), MP_ROM_PTR(&struct_pack_into_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_unpack), MP_ROM_PTR(&struct_unpack_from_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_unpack_from), MP_ROM_PTR(&struct_unpack_from_obj) },
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(mp_module_struct_globals, mp_module_struct_globals_table);
|
||||
|
||||
const mp_obj_module_t mp_module_ustruct = {
|
||||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t *)&mp_module_struct_globals,
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,258 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013, 2014 Damien P. George
|
||||
* Copyright (c) 2014-2017 Paul Sokolovsky
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "py/builtin.h"
|
||||
#include "py/objlist.h"
|
||||
#include "py/objtuple.h"
|
||||
#include "py/objstr.h"
|
||||
#include "py/objint.h"
|
||||
#include "py/objtype.h"
|
||||
#include "py/stream.h"
|
||||
#include "py/smallint.h"
|
||||
#include "py/runtime.h"
|
||||
#include "py/persistentcode.h"
|
||||
|
||||
#if MICROPY_PY_SYS_SETTRACE
|
||||
#include "py/objmodule.h"
|
||||
#include "py/profile.h"
|
||||
#endif
|
||||
|
||||
#if MICROPY_PY_SYS
|
||||
|
||||
// defined per port; type of these is irrelevant, just need pointer
|
||||
extern struct _mp_dummy_t mp_sys_stdin_obj;
|
||||
extern struct _mp_dummy_t mp_sys_stdout_obj;
|
||||
extern struct _mp_dummy_t mp_sys_stderr_obj;
|
||||
|
||||
#if MICROPY_PY_IO && MICROPY_PY_SYS_STDFILES
|
||||
const mp_print_t mp_sys_stdout_print = {&mp_sys_stdout_obj, mp_stream_write_adaptor};
|
||||
#endif
|
||||
|
||||
// version - Python language version that this implementation conforms to, as a string
|
||||
STATIC const MP_DEFINE_STR_OBJ(mp_sys_version_obj, "3.4.0");
|
||||
|
||||
// version_info - Python language version that this implementation conforms to, as a tuple of ints
|
||||
#define I(n) MP_OBJ_NEW_SMALL_INT(n)
|
||||
// TODO: CPython is now at 5-element array, but save 2 els so far...
|
||||
STATIC const mp_obj_tuple_t mp_sys_version_info_obj = {{&mp_type_tuple}, 3, {I(3), I(4), I(0)}};
|
||||
|
||||
// sys.implementation object
|
||||
// this holds the MicroPython version
|
||||
STATIC const mp_obj_tuple_t mp_sys_implementation_version_info_obj = {
|
||||
{&mp_type_tuple},
|
||||
3,
|
||||
{ I(MICROPY_VERSION_MAJOR), I(MICROPY_VERSION_MINOR), I(MICROPY_VERSION_MICRO) }
|
||||
};
|
||||
#if MICROPY_PERSISTENT_CODE_LOAD
|
||||
#define SYS_IMPLEMENTATION_ELEMS \
|
||||
MP_ROM_QSTR(MP_QSTR_micropython), \
|
||||
MP_ROM_PTR(&mp_sys_implementation_version_info_obj), \
|
||||
MP_ROM_INT(MPY_FILE_HEADER_INT)
|
||||
#else
|
||||
#define SYS_IMPLEMENTATION_ELEMS \
|
||||
MP_ROM_QSTR(MP_QSTR_micropython), \
|
||||
MP_ROM_PTR(&mp_sys_implementation_version_info_obj)
|
||||
#endif
|
||||
#if MICROPY_PY_ATTRTUPLE
|
||||
STATIC const qstr impl_fields[] = {
|
||||
MP_QSTR_name,
|
||||
MP_QSTR_version,
|
||||
#if MICROPY_PERSISTENT_CODE_LOAD
|
||||
MP_QSTR_mpy,
|
||||
#endif
|
||||
};
|
||||
STATIC MP_DEFINE_ATTRTUPLE(
|
||||
mp_sys_implementation_obj,
|
||||
impl_fields,
|
||||
2 + MICROPY_PERSISTENT_CODE_LOAD,
|
||||
SYS_IMPLEMENTATION_ELEMS
|
||||
);
|
||||
#else
|
||||
STATIC const mp_rom_obj_tuple_t mp_sys_implementation_obj = {
|
||||
{&mp_type_tuple},
|
||||
2 + MICROPY_PERSISTENT_CODE_LOAD,
|
||||
{
|
||||
SYS_IMPLEMENTATION_ELEMS
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
#undef I
|
||||
|
||||
#ifdef MICROPY_PY_SYS_PLATFORM
|
||||
// platform - the platform that MicroPython is running on
|
||||
STATIC const MP_DEFINE_STR_OBJ(mp_sys_platform_obj, MICROPY_PY_SYS_PLATFORM);
|
||||
#endif
|
||||
|
||||
// exit([retval]): raise SystemExit, with optional argument given to the exception
|
||||
STATIC mp_obj_t mp_sys_exit(size_t n_args, const mp_obj_t *args) {
|
||||
mp_obj_t exc;
|
||||
if (n_args == 0) {
|
||||
exc = mp_obj_new_exception(&mp_type_SystemExit);
|
||||
} else {
|
||||
exc = mp_obj_new_exception_arg1(&mp_type_SystemExit, args[0]);
|
||||
}
|
||||
nlr_raise(exc);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_sys_exit_obj, 0, 1, mp_sys_exit);
|
||||
|
||||
STATIC mp_obj_t mp_sys_print_exception(size_t n_args, const mp_obj_t *args) {
|
||||
#if MICROPY_PY_IO && MICROPY_PY_SYS_STDFILES
|
||||
void *stream_obj = &mp_sys_stdout_obj;
|
||||
if (n_args > 1) {
|
||||
mp_get_stream_raise(args[1], MP_STREAM_OP_WRITE);
|
||||
stream_obj = MP_OBJ_TO_PTR(args[1]);
|
||||
}
|
||||
|
||||
mp_print_t print = {stream_obj, mp_stream_write_adaptor};
|
||||
mp_obj_print_exception(&print, args[0]);
|
||||
#else
|
||||
(void)n_args;
|
||||
mp_obj_print_exception(&mp_plat_print, args[0]);
|
||||
#endif
|
||||
|
||||
return mp_const_none;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_sys_print_exception_obj, 1, 2, mp_sys_print_exception);
|
||||
|
||||
#if MICROPY_PY_SYS_EXC_INFO
|
||||
STATIC mp_obj_t mp_sys_exc_info(void) {
|
||||
mp_obj_t cur_exc = MP_OBJ_FROM_PTR(MP_STATE_VM(cur_exception));
|
||||
mp_obj_tuple_t *t = MP_OBJ_TO_PTR(mp_obj_new_tuple(3, NULL));
|
||||
|
||||
if (cur_exc == MP_OBJ_NULL) {
|
||||
t->items[0] = mp_const_none;
|
||||
t->items[1] = mp_const_none;
|
||||
t->items[2] = mp_const_none;
|
||||
return MP_OBJ_FROM_PTR(t);
|
||||
}
|
||||
|
||||
t->items[0] = MP_OBJ_FROM_PTR(mp_obj_get_type(cur_exc));
|
||||
t->items[1] = cur_exc;
|
||||
t->items[2] = mp_const_none;
|
||||
return MP_OBJ_FROM_PTR(t);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_0(mp_sys_exc_info_obj, mp_sys_exc_info);
|
||||
#endif
|
||||
|
||||
#if MICROPY_PY_SYS_GETSIZEOF
|
||||
STATIC mp_obj_t mp_sys_getsizeof(mp_obj_t obj) {
|
||||
return mp_unary_op(MP_UNARY_OP_SIZEOF, obj);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_sys_getsizeof_obj, mp_sys_getsizeof);
|
||||
#endif
|
||||
|
||||
#if MICROPY_PY_SYS_ATEXIT
|
||||
// atexit(callback): Callback is called when sys.exit is called.
|
||||
STATIC mp_obj_t mp_sys_atexit(mp_obj_t obj) {
|
||||
mp_obj_t old = MP_STATE_VM(sys_exitfunc);
|
||||
MP_STATE_VM(sys_exitfunc) = obj;
|
||||
return old;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_sys_atexit_obj, mp_sys_atexit);
|
||||
#endif
|
||||
|
||||
#if MICROPY_PY_SYS_SETTRACE
|
||||
// settrace(tracefunc): Set the system’s trace function.
|
||||
STATIC mp_obj_t mp_sys_settrace(mp_obj_t obj) {
|
||||
return mp_prof_settrace(obj);
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(mp_sys_settrace_obj, mp_sys_settrace);
|
||||
#endif // MICROPY_PY_SYS_SETTRACE
|
||||
|
||||
STATIC const mp_rom_map_elem_t mp_module_sys_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_sys) },
|
||||
|
||||
{ MP_ROM_QSTR(MP_QSTR_path), MP_ROM_PTR(&MP_STATE_VM(mp_sys_path_obj)) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_argv), MP_ROM_PTR(&MP_STATE_VM(mp_sys_argv_obj)) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_version), MP_ROM_PTR(&mp_sys_version_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_version_info), MP_ROM_PTR(&mp_sys_version_info_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_implementation), MP_ROM_PTR(&mp_sys_implementation_obj) },
|
||||
#ifdef MICROPY_PY_SYS_PLATFORM
|
||||
{ MP_ROM_QSTR(MP_QSTR_platform), MP_ROM_PTR(&mp_sys_platform_obj) },
|
||||
#endif
|
||||
#if MP_ENDIANNESS_LITTLE
|
||||
{ MP_ROM_QSTR(MP_QSTR_byteorder), MP_ROM_QSTR(MP_QSTR_little) },
|
||||
#else
|
||||
{ MP_ROM_QSTR(MP_QSTR_byteorder), MP_ROM_QSTR(MP_QSTR_big) },
|
||||
#endif
|
||||
|
||||
#if MICROPY_PY_SYS_MAXSIZE
|
||||
#if MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_NONE
|
||||
// Maximum mp_int_t value is not representable as small int, so we have
|
||||
// little choice but to use MP_SMALL_INT_MAX. Apps also should be careful
|
||||
// to not try to compare sys.maxsize to some literal number (as this
|
||||
// number might not fit in available int size), but instead count number
|
||||
// of "one" bits in sys.maxsize.
|
||||
{ MP_ROM_QSTR(MP_QSTR_maxsize), MP_ROM_INT(MP_SMALL_INT_MAX) },
|
||||
#else
|
||||
{ MP_ROM_QSTR(MP_QSTR_maxsize), MP_ROM_PTR(&mp_sys_maxsize_obj) },
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if MICROPY_PY_SYS_EXIT
|
||||
{ MP_ROM_QSTR(MP_QSTR_exit), MP_ROM_PTR(&mp_sys_exit_obj) },
|
||||
#endif
|
||||
|
||||
#if MICROPY_PY_SYS_SETTRACE
|
||||
{ MP_ROM_QSTR(MP_QSTR_settrace), MP_ROM_PTR(&mp_sys_settrace_obj) },
|
||||
#endif
|
||||
|
||||
#if MICROPY_PY_SYS_STDFILES
|
||||
{ MP_ROM_QSTR(MP_QSTR_stdin), MP_ROM_PTR(&mp_sys_stdin_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_stdout), MP_ROM_PTR(&mp_sys_stdout_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_stderr), MP_ROM_PTR(&mp_sys_stderr_obj) },
|
||||
#endif
|
||||
|
||||
#if MICROPY_PY_SYS_MODULES
|
||||
{ MP_ROM_QSTR(MP_QSTR_modules), MP_ROM_PTR(&MP_STATE_VM(mp_loaded_modules_dict)) },
|
||||
#endif
|
||||
#if MICROPY_PY_SYS_EXC_INFO
|
||||
{ MP_ROM_QSTR(MP_QSTR_exc_info), MP_ROM_PTR(&mp_sys_exc_info_obj) },
|
||||
#endif
|
||||
#if MICROPY_PY_SYS_GETSIZEOF
|
||||
{ MP_ROM_QSTR(MP_QSTR_getsizeof), MP_ROM_PTR(&mp_sys_getsizeof_obj) },
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Extensions to CPython
|
||||
*/
|
||||
|
||||
{ MP_ROM_QSTR(MP_QSTR_print_exception), MP_ROM_PTR(&mp_sys_print_exception_obj) },
|
||||
#if MICROPY_PY_SYS_ATEXIT
|
||||
{ MP_ROM_QSTR(MP_QSTR_atexit), MP_ROM_PTR(&mp_sys_atexit_obj) },
|
||||
#endif
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(mp_module_sys_globals, mp_module_sys_globals_table);
|
||||
|
||||
const mp_obj_module_t mp_module_sys = {
|
||||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t *)&mp_module_sys_globals,
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,300 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "py/runtime.h"
|
||||
#include "py/stackctrl.h"
|
||||
|
||||
#if MICROPY_PY_THREAD
|
||||
|
||||
#include "py/mpthread.h"
|
||||
|
||||
#if MICROPY_DEBUG_VERBOSE // print debugging info
|
||||
#define DEBUG_PRINT (1)
|
||||
#define DEBUG_printf DEBUG_printf
|
||||
#else // don't print debugging info
|
||||
#define DEBUG_PRINT (0)
|
||||
#define DEBUG_printf(...) (void)0
|
||||
#endif
|
||||
|
||||
/****************************************************************/
|
||||
// Lock object
|
||||
|
||||
STATIC const mp_obj_type_t mp_type_thread_lock;
|
||||
|
||||
typedef struct _mp_obj_thread_lock_t {
|
||||
mp_obj_base_t base;
|
||||
mp_thread_mutex_t mutex;
|
||||
volatile bool locked;
|
||||
} mp_obj_thread_lock_t;
|
||||
|
||||
STATIC mp_obj_thread_lock_t *mp_obj_new_thread_lock(void) {
|
||||
mp_obj_thread_lock_t *self = m_new_obj(mp_obj_thread_lock_t);
|
||||
self->base.type = &mp_type_thread_lock;
|
||||
mp_thread_mutex_init(&self->mutex);
|
||||
self->locked = false;
|
||||
return self;
|
||||
}
|
||||
|
||||
STATIC mp_obj_t thread_lock_acquire(size_t n_args, const mp_obj_t *args) {
|
||||
mp_obj_thread_lock_t *self = MP_OBJ_TO_PTR(args[0]);
|
||||
bool wait = true;
|
||||
if (n_args > 1) {
|
||||
wait = mp_obj_get_int(args[1]);
|
||||
// TODO support timeout arg
|
||||
}
|
||||
MP_THREAD_GIL_EXIT();
|
||||
int ret = mp_thread_mutex_lock(&self->mutex, wait);
|
||||
MP_THREAD_GIL_ENTER();
|
||||
if (ret == 0) {
|
||||
return mp_const_false;
|
||||
} else if (ret == 1) {
|
||||
self->locked = true;
|
||||
return mp_const_true;
|
||||
} else {
|
||||
mp_raise_OSError(-ret);
|
||||
}
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(thread_lock_acquire_obj, 1, 3, thread_lock_acquire);
|
||||
|
||||
STATIC mp_obj_t thread_lock_release(mp_obj_t self_in) {
|
||||
mp_obj_thread_lock_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
if (!self->locked) {
|
||||
mp_raise_msg(&mp_type_RuntimeError, NULL);
|
||||
}
|
||||
self->locked = false;
|
||||
MP_THREAD_GIL_EXIT();
|
||||
mp_thread_mutex_unlock(&self->mutex);
|
||||
MP_THREAD_GIL_ENTER();
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(thread_lock_release_obj, thread_lock_release);
|
||||
|
||||
STATIC mp_obj_t thread_lock_locked(mp_obj_t self_in) {
|
||||
mp_obj_thread_lock_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
return mp_obj_new_bool(self->locked);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(thread_lock_locked_obj, thread_lock_locked);
|
||||
|
||||
STATIC mp_obj_t thread_lock___exit__(size_t n_args, const mp_obj_t *args) {
|
||||
(void)n_args; // unused
|
||||
return thread_lock_release(args[0]);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(thread_lock___exit___obj, 4, 4, thread_lock___exit__);
|
||||
|
||||
STATIC const mp_rom_map_elem_t thread_lock_locals_dict_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR_acquire), MP_ROM_PTR(&thread_lock_acquire_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_release), MP_ROM_PTR(&thread_lock_release_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_locked), MP_ROM_PTR(&thread_lock_locked_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&thread_lock_acquire_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&thread_lock___exit___obj) },
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(thread_lock_locals_dict, thread_lock_locals_dict_table);
|
||||
|
||||
STATIC const mp_obj_type_t mp_type_thread_lock = {
|
||||
{ &mp_type_type },
|
||||
.name = MP_QSTR_lock,
|
||||
.locals_dict = (mp_obj_dict_t *)&thread_lock_locals_dict,
|
||||
};
|
||||
|
||||
/****************************************************************/
|
||||
// _thread module
|
||||
|
||||
STATIC size_t thread_stack_size = 0;
|
||||
|
||||
STATIC mp_obj_t mod_thread_get_ident(void) {
|
||||
return mp_obj_new_int_from_uint((uintptr_t)mp_thread_get_state());
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_thread_get_ident_obj, mod_thread_get_ident);
|
||||
|
||||
STATIC mp_obj_t mod_thread_stack_size(size_t n_args, const mp_obj_t *args) {
|
||||
mp_obj_t ret = mp_obj_new_int_from_uint(thread_stack_size);
|
||||
if (n_args == 0) {
|
||||
thread_stack_size = 0;
|
||||
} else {
|
||||
thread_stack_size = mp_obj_get_int(args[0]);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_thread_stack_size_obj, 0, 1, mod_thread_stack_size);
|
||||
|
||||
typedef struct _thread_entry_args_t {
|
||||
mp_obj_dict_t *dict_locals;
|
||||
mp_obj_dict_t *dict_globals;
|
||||
size_t stack_size;
|
||||
mp_obj_t fun;
|
||||
size_t n_args;
|
||||
size_t n_kw;
|
||||
mp_obj_t args[];
|
||||
} thread_entry_args_t;
|
||||
|
||||
STATIC void *thread_entry(void *args_in) {
|
||||
// Execution begins here for a new thread. We do not have the GIL.
|
||||
|
||||
thread_entry_args_t *args = (thread_entry_args_t *)args_in;
|
||||
|
||||
mp_state_thread_t ts;
|
||||
mp_thread_set_state(&ts);
|
||||
|
||||
mp_stack_set_top(&ts + 1); // need to include ts in root-pointer scan
|
||||
mp_stack_set_limit(args->stack_size);
|
||||
|
||||
#if MICROPY_ENABLE_PYSTACK
|
||||
// TODO threading and pystack is not fully supported, for now just make a small stack
|
||||
mp_obj_t mini_pystack[128];
|
||||
mp_pystack_init(mini_pystack, &mini_pystack[128]);
|
||||
#endif
|
||||
|
||||
// set locals and globals from the calling context
|
||||
mp_locals_set(args->dict_locals);
|
||||
mp_globals_set(args->dict_globals);
|
||||
|
||||
MP_THREAD_GIL_ENTER();
|
||||
|
||||
// signal that we are set up and running
|
||||
mp_thread_start();
|
||||
|
||||
// TODO set more thread-specific state here:
|
||||
// mp_pending_exception? (root pointer)
|
||||
// cur_exception (root pointer)
|
||||
|
||||
DEBUG_printf("[thread] start ts=%p args=%p stack=%p\n", &ts, &args, MP_STATE_THREAD(stack_top));
|
||||
|
||||
nlr_buf_t nlr;
|
||||
if (nlr_push(&nlr) == 0) {
|
||||
mp_call_function_n_kw(args->fun, args->n_args, args->n_kw, args->args);
|
||||
nlr_pop();
|
||||
} else {
|
||||
// uncaught exception
|
||||
// check for SystemExit
|
||||
mp_obj_base_t *exc = (mp_obj_base_t *)nlr.ret_val;
|
||||
if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(exc->type), MP_OBJ_FROM_PTR(&mp_type_SystemExit))) {
|
||||
// swallow exception silently
|
||||
} else {
|
||||
// print exception out
|
||||
mp_printf(MICROPY_ERROR_PRINTER, "Unhandled exception in thread started by ");
|
||||
mp_obj_print_helper(MICROPY_ERROR_PRINTER, args->fun, PRINT_REPR);
|
||||
mp_printf(MICROPY_ERROR_PRINTER, "\n");
|
||||
mp_obj_print_exception(MICROPY_ERROR_PRINTER, MP_OBJ_FROM_PTR(exc));
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_printf("[thread] finish ts=%p\n", &ts);
|
||||
|
||||
// signal that we are finished
|
||||
mp_thread_finish();
|
||||
|
||||
MP_THREAD_GIL_EXIT();
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
STATIC mp_obj_t mod_thread_start_new_thread(size_t n_args, const mp_obj_t *args) {
|
||||
// This structure holds the Python function and arguments for thread entry.
|
||||
// We copy all arguments into this structure to keep ownership of them.
|
||||
// We must be very careful about root pointers because this pointer may
|
||||
// disappear from our address space before the thread is created.
|
||||
thread_entry_args_t *th_args;
|
||||
|
||||
// get positional arguments
|
||||
size_t pos_args_len;
|
||||
mp_obj_t *pos_args_items;
|
||||
mp_obj_get_array(args[1], &pos_args_len, &pos_args_items);
|
||||
|
||||
// check for keyword arguments
|
||||
if (n_args == 2) {
|
||||
// just position arguments
|
||||
th_args = m_new_obj_var(thread_entry_args_t, mp_obj_t, pos_args_len);
|
||||
th_args->n_kw = 0;
|
||||
} else {
|
||||
// positional and keyword arguments
|
||||
if (mp_obj_get_type(args[2]) != &mp_type_dict) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("expecting a dict for keyword args"));
|
||||
}
|
||||
mp_map_t *map = &((mp_obj_dict_t *)MP_OBJ_TO_PTR(args[2]))->map;
|
||||
th_args = m_new_obj_var(thread_entry_args_t, mp_obj_t, pos_args_len + 2 * map->used);
|
||||
th_args->n_kw = map->used;
|
||||
// copy across the keyword arguments
|
||||
for (size_t i = 0, n = pos_args_len; i < map->alloc; ++i) {
|
||||
if (mp_map_slot_is_filled(map, i)) {
|
||||
th_args->args[n++] = map->table[i].key;
|
||||
th_args->args[n++] = map->table[i].value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// copy across the positional arguments
|
||||
th_args->n_args = pos_args_len;
|
||||
memcpy(th_args->args, pos_args_items, pos_args_len * sizeof(mp_obj_t));
|
||||
|
||||
// pass our locals and globals into the new thread
|
||||
th_args->dict_locals = mp_locals_get();
|
||||
th_args->dict_globals = mp_globals_get();
|
||||
|
||||
// set the stack size to use
|
||||
th_args->stack_size = thread_stack_size;
|
||||
|
||||
// set the function for thread entry
|
||||
th_args->fun = args[0];
|
||||
|
||||
// spawn the thread!
|
||||
mp_thread_create(thread_entry, th_args, &th_args->stack_size);
|
||||
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mod_thread_start_new_thread_obj, 2, 3, mod_thread_start_new_thread);
|
||||
|
||||
STATIC mp_obj_t mod_thread_exit(void) {
|
||||
mp_raise_type(&mp_type_SystemExit);
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_thread_exit_obj, mod_thread_exit);
|
||||
|
||||
STATIC mp_obj_t mod_thread_allocate_lock(void) {
|
||||
return MP_OBJ_FROM_PTR(mp_obj_new_thread_lock());
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mod_thread_allocate_lock_obj, mod_thread_allocate_lock);
|
||||
|
||||
STATIC const mp_rom_map_elem_t mp_module_thread_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR__thread) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_LockType), MP_ROM_PTR(&mp_type_thread_lock) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_get_ident), MP_ROM_PTR(&mod_thread_get_ident_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_stack_size), MP_ROM_PTR(&mod_thread_stack_size_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_start_new_thread), MP_ROM_PTR(&mod_thread_start_new_thread_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_exit), MP_ROM_PTR(&mod_thread_exit_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_allocate_lock), MP_ROM_PTR(&mod_thread_allocate_lock_obj) },
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(mp_module_thread_globals, mp_module_thread_globals_table);
|
||||
|
||||
const mp_obj_module_t mp_module_thread = {
|
||||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t *)&mp_module_thread_globals,
|
||||
};
|
||||
|
||||
#endif // MICROPY_PY_THREAD
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2016 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "py/obj.h"
|
||||
#include "py/mperrno.h"
|
||||
|
||||
#if MICROPY_PY_UERRNO
|
||||
|
||||
// This list can be defined per port in mpconfigport.h to tailor it to a
|
||||
// specific port's needs. If it's not defined then we provide a default.
|
||||
#ifndef MICROPY_PY_UERRNO_LIST
|
||||
#define MICROPY_PY_UERRNO_LIST \
|
||||
X(EPERM) \
|
||||
X(ENOENT) \
|
||||
X(EIO) \
|
||||
X(EBADF) \
|
||||
X(EAGAIN) \
|
||||
X(ENOMEM) \
|
||||
X(EACCES) \
|
||||
X(EEXIST) \
|
||||
X(ENODEV) \
|
||||
X(EISDIR) \
|
||||
X(EINVAL) \
|
||||
X(EOPNOTSUPP) \
|
||||
X(EADDRINUSE) \
|
||||
X(ECONNABORTED) \
|
||||
X(ECONNRESET) \
|
||||
X(ENOBUFS) \
|
||||
X(ENOTCONN) \
|
||||
X(ETIMEDOUT) \
|
||||
X(ECONNREFUSED) \
|
||||
X(EHOSTUNREACH) \
|
||||
X(EALREADY) \
|
||||
X(EINPROGRESS) \
|
||||
|
||||
#endif
|
||||
|
||||
#if MICROPY_PY_UERRNO_ERRORCODE
|
||||
STATIC const mp_rom_map_elem_t errorcode_table[] = {
|
||||
#define X(e) { MP_ROM_INT(MP_##e), MP_ROM_QSTR(MP_QSTR_##e) },
|
||||
MICROPY_PY_UERRNO_LIST
|
||||
#undef X
|
||||
};
|
||||
|
||||
STATIC const mp_obj_dict_t errorcode_dict = {
|
||||
.base = {&mp_type_dict},
|
||||
.map = {
|
||||
.all_keys_are_qstrs = 0, // keys are integers
|
||||
.is_fixed = 1,
|
||||
.is_ordered = 1,
|
||||
.used = MP_ARRAY_SIZE(errorcode_table),
|
||||
.alloc = MP_ARRAY_SIZE(errorcode_table),
|
||||
.table = (mp_map_elem_t *)(mp_rom_map_elem_t *)errorcode_table,
|
||||
},
|
||||
};
|
||||
#endif
|
||||
|
||||
STATIC const mp_rom_map_elem_t mp_module_uerrno_globals_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_uerrno) },
|
||||
#if MICROPY_PY_UERRNO_ERRORCODE
|
||||
{ MP_ROM_QSTR(MP_QSTR_errorcode), MP_ROM_PTR(&errorcode_dict) },
|
||||
#endif
|
||||
|
||||
#define X(e) { MP_ROM_QSTR(MP_QSTR_##e), MP_ROM_INT(MP_##e) },
|
||||
MICROPY_PY_UERRNO_LIST
|
||||
#undef X
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(mp_module_uerrno_globals, mp_module_uerrno_globals_table);
|
||||
|
||||
const mp_obj_module_t mp_module_uerrno = {
|
||||
.base = { &mp_type_module },
|
||||
.globals = (mp_obj_dict_t *)&mp_module_uerrno_globals,
|
||||
};
|
||||
|
||||
qstr mp_errno_to_str(mp_obj_t errno_val) {
|
||||
#if MICROPY_PY_UERRNO_ERRORCODE
|
||||
// We have the errorcode dict so can do a lookup using the hash map
|
||||
mp_map_elem_t *elem = mp_map_lookup((mp_map_t *)&errorcode_dict.map, errno_val, MP_MAP_LOOKUP);
|
||||
if (elem == NULL) {
|
||||
return MP_QSTRnull;
|
||||
} else {
|
||||
return MP_OBJ_QSTR_VALUE(elem->value);
|
||||
}
|
||||
#else
|
||||
// We don't have the errorcode dict so do a simple search in the modules dict
|
||||
for (size_t i = 0; i < MP_ARRAY_SIZE(mp_module_uerrno_globals_table); ++i) {
|
||||
if (errno_val == mp_module_uerrno_globals_table[i].value) {
|
||||
return MP_OBJ_QSTR_VALUE(mp_module_uerrno_globals_table[i].key);
|
||||
}
|
||||
}
|
||||
return MP_QSTRnull;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // MICROPY_PY_UERRNO
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2016 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef MICROPY_INCLUDED_PY_MPERRNO_H
|
||||
#define MICROPY_INCLUDED_PY_MPERRNO_H
|
||||
|
||||
#include "py/mpconfig.h"
|
||||
|
||||
#if MICROPY_USE_INTERNAL_ERRNO
|
||||
|
||||
// MP_Exxx errno's are defined directly as numeric values
|
||||
// (Linux constants are used as a reference)
|
||||
|
||||
#define MP_EPERM (1) // Operation not permitted
|
||||
#define MP_ENOENT (2) // No such file or directory
|
||||
#define MP_ESRCH (3) // No such process
|
||||
#define MP_EINTR (4) // Interrupted system call
|
||||
#define MP_EIO (5) // I/O error
|
||||
#define MP_ENXIO (6) // No such device or address
|
||||
#define MP_E2BIG (7) // Argument list too long
|
||||
#define MP_ENOEXEC (8) // Exec format error
|
||||
#define MP_EBADF (9) // Bad file number
|
||||
#define MP_ECHILD (10) // No child processes
|
||||
#define MP_EAGAIN (11) // Try again
|
||||
#define MP_ENOMEM (12) // Out of memory
|
||||
#define MP_EACCES (13) // Permission denied
|
||||
#define MP_EFAULT (14) // Bad address
|
||||
#define MP_ENOTBLK (15) // Block device required
|
||||
#define MP_EBUSY (16) // Device or resource busy
|
||||
#define MP_EEXIST (17) // File exists
|
||||
#define MP_EXDEV (18) // Cross-device link
|
||||
#define MP_ENODEV (19) // No such device
|
||||
#define MP_ENOTDIR (20) // Not a directory
|
||||
#define MP_EISDIR (21) // Is a directory
|
||||
#define MP_EINVAL (22) // Invalid argument
|
||||
#define MP_ENFILE (23) // File table overflow
|
||||
#define MP_EMFILE (24) // Too many open files
|
||||
#define MP_ENOTTY (25) // Not a typewriter
|
||||
#define MP_ETXTBSY (26) // Text file busy
|
||||
#define MP_EFBIG (27) // File too large
|
||||
#define MP_ENOSPC (28) // No space left on device
|
||||
#define MP_ESPIPE (29) // Illegal seek
|
||||
#define MP_EROFS (30) // Read-only file system
|
||||
#define MP_EMLINK (31) // Too many links
|
||||
#define MP_EPIPE (32) // Broken pipe
|
||||
#define MP_EDOM (33) // Math argument out of domain of func
|
||||
#define MP_ERANGE (34) // Math result not representable
|
||||
#define MP_EWOULDBLOCK MP_EAGAIN // Operation would block
|
||||
#define MP_EOPNOTSUPP (95) // Operation not supported on transport endpoint
|
||||
#define MP_EAFNOSUPPORT (97) // Address family not supported by protocol
|
||||
#define MP_EADDRINUSE (98) // Address already in use
|
||||
#define MP_ECONNABORTED (103) // Software caused connection abort
|
||||
#define MP_ECONNRESET (104) // Connection reset by peer
|
||||
#define MP_ENOBUFS (105) // No buffer space available
|
||||
#define MP_EISCONN (106) // Transport endpoint is already connected
|
||||
#define MP_ENOTCONN (107) // Transport endpoint is not connected
|
||||
#define MP_ETIMEDOUT (110) // Connection timed out
|
||||
#define MP_ECONNREFUSED (111) // Connection refused
|
||||
#define MP_EHOSTUNREACH (113) // No route to host
|
||||
#define MP_EALREADY (114) // Operation already in progress
|
||||
#define MP_EINPROGRESS (115) // Operation now in progress
|
||||
|
||||
#else
|
||||
|
||||
// MP_Exxx errno's are defined in terms of system supplied ones
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#define MP_EPERM EPERM
|
||||
#define MP_ENOENT ENOENT
|
||||
#define MP_ESRCH ESRCH
|
||||
#define MP_EINTR EINTR
|
||||
#define MP_EIO EIO
|
||||
#define MP_ENXIO ENXIO
|
||||
#define MP_E2BIG E2BIG
|
||||
#define MP_ENOEXEC ENOEXEC
|
||||
#define MP_EBADF EBADF
|
||||
#define MP_ECHILD ECHILD
|
||||
#define MP_EAGAIN EAGAIN
|
||||
#define MP_ENOMEM ENOMEM
|
||||
#define MP_EACCES EACCES
|
||||
#define MP_EFAULT EFAULT
|
||||
#define MP_ENOTBLK ENOTBLK
|
||||
#define MP_EBUSY EBUSY
|
||||
#define MP_EEXIST EEXIST
|
||||
#define MP_EXDEV EXDEV
|
||||
#define MP_ENODEV ENODEV
|
||||
#define MP_ENOTDIR ENOTDIR
|
||||
#define MP_EISDIR EISDIR
|
||||
#define MP_EINVAL EINVAL
|
||||
#define MP_ENFILE ENFILE
|
||||
#define MP_EMFILE EMFILE
|
||||
#define MP_ENOTTY ENOTTY
|
||||
#define MP_ETXTBSY ETXTBSY
|
||||
#define MP_EFBIG EFBIG
|
||||
#define MP_ENOSPC ENOSPC
|
||||
#define MP_ESPIPE ESPIPE
|
||||
#define MP_EROFS EROFS
|
||||
#define MP_EMLINK EMLINK
|
||||
#define MP_EPIPE EPIPE
|
||||
#define MP_EDOM EDOM
|
||||
#define MP_ERANGE ERANGE
|
||||
#define MP_EWOULDBLOCK EWOULDBLOCK
|
||||
#define MP_EOPNOTSUPP EOPNOTSUPP
|
||||
#define MP_EAFNOSUPPORT EAFNOSUPPORT
|
||||
#define MP_EADDRINUSE EADDRINUSE
|
||||
#define MP_ECONNABORTED ECONNABORTED
|
||||
#define MP_ECONNRESET ECONNRESET
|
||||
#define MP_ENOBUFS ENOBUFS
|
||||
#define MP_EISCONN EISCONN
|
||||
#define MP_ENOTCONN ENOTCONN
|
||||
#define MP_ETIMEDOUT ETIMEDOUT
|
||||
#define MP_ECONNREFUSED ECONNREFUSED
|
||||
#define MP_EHOSTUNREACH EHOSTUNREACH
|
||||
#define MP_EALREADY EALREADY
|
||||
#define MP_EINPROGRESS EINPROGRESS
|
||||
|
||||
#endif
|
||||
|
||||
#if MICROPY_PY_UERRNO
|
||||
|
||||
#include "py/obj.h"
|
||||
|
||||
qstr mp_errno_to_str(mp_obj_t errno_val);
|
||||
|
||||
#endif
|
||||
|
||||
#endif // MICROPY_INCLUDED_PY_MPERRNO_H
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef MICROPY_INCLUDED_PY_MPHAL_H
|
||||
#define MICROPY_INCLUDED_PY_MPHAL_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "py/mpconfig.h"
|
||||
|
||||
#ifdef MICROPY_MPHALPORT_H
|
||||
#include MICROPY_MPHALPORT_H
|
||||
#else
|
||||
#include <mphalport.h>
|
||||
#endif
|
||||
|
||||
#ifndef mp_hal_stdio_poll
|
||||
uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags);
|
||||
#endif
|
||||
|
||||
#ifndef mp_hal_stdin_rx_chr
|
||||
int mp_hal_stdin_rx_chr(void);
|
||||
#endif
|
||||
|
||||
#ifndef mp_hal_stdout_tx_str
|
||||
void mp_hal_stdout_tx_str(const char *str);
|
||||
#endif
|
||||
|
||||
#ifndef mp_hal_stdout_tx_strn
|
||||
void mp_hal_stdout_tx_strn(const char *str, size_t len);
|
||||
#endif
|
||||
|
||||
#ifndef mp_hal_stdout_tx_strn_cooked
|
||||
void mp_hal_stdout_tx_strn_cooked(const char *str, size_t len);
|
||||
#endif
|
||||
|
||||
#ifndef mp_hal_delay_ms
|
||||
void mp_hal_delay_ms(mp_uint_t ms);
|
||||
#endif
|
||||
|
||||
#ifndef mp_hal_delay_us
|
||||
void mp_hal_delay_us(mp_uint_t us);
|
||||
#endif
|
||||
|
||||
#ifndef mp_hal_ticks_ms
|
||||
mp_uint_t mp_hal_ticks_ms(void);
|
||||
#endif
|
||||
|
||||
#ifndef mp_hal_ticks_us
|
||||
mp_uint_t mp_hal_ticks_us(void);
|
||||
#endif
|
||||
|
||||
#ifndef mp_hal_ticks_cpu
|
||||
mp_uint_t mp_hal_ticks_cpu(void);
|
||||
#endif
|
||||
|
||||
#ifndef mp_hal_time_ns
|
||||
// Nanoseconds since the Epoch.
|
||||
uint64_t mp_hal_time_ns(void);
|
||||
#endif
|
||||
|
||||
// If port HAL didn't define its own pin API, use generic
|
||||
// "virtual pin" API from the core.
|
||||
#ifndef mp_hal_pin_obj_t
|
||||
#define mp_hal_pin_obj_t mp_obj_t
|
||||
#define mp_hal_get_pin_obj(pin) (pin)
|
||||
#define mp_hal_pin_read(pin) mp_virtual_pin_read(pin)
|
||||
#define mp_hal_pin_write(pin, v) mp_virtual_pin_write(pin, v)
|
||||
#include "extmod/virtpin.h"
|
||||
#endif
|
||||
|
||||
#endif // MICROPY_INCLUDED_PY_MPHAL_H
|
||||
@@ -0,0 +1,577 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013-2015 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "py/mphal.h"
|
||||
#include "py/mpprint.h"
|
||||
#include "py/obj.h"
|
||||
#include "py/objint.h"
|
||||
#include "py/runtime.h"
|
||||
|
||||
#if MICROPY_PY_BUILTINS_FLOAT
|
||||
#include "py/formatfloat.h"
|
||||
#endif
|
||||
|
||||
static const char pad_spaces[] = " ";
|
||||
static const char pad_zeroes[] = "0000000000000000";
|
||||
|
||||
STATIC void plat_print_strn(void *env, const char *str, size_t len) {
|
||||
(void)env;
|
||||
MP_PLAT_PRINT_STRN(str, len);
|
||||
}
|
||||
|
||||
const mp_print_t mp_plat_print = {NULL, plat_print_strn};
|
||||
|
||||
int mp_print_str(const mp_print_t *print, const char *str) {
|
||||
size_t len = strlen(str);
|
||||
if (len) {
|
||||
print->print_strn(print->data, str, len);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
int mp_print_strn(const mp_print_t *print, const char *str, size_t len, int flags, char fill, int width) {
|
||||
int left_pad = 0;
|
||||
int right_pad = 0;
|
||||
int pad = width - len;
|
||||
int pad_size;
|
||||
int total_chars_printed = 0;
|
||||
const char *pad_chars;
|
||||
|
||||
if (!fill || fill == ' ') {
|
||||
pad_chars = pad_spaces;
|
||||
pad_size = sizeof(pad_spaces) - 1;
|
||||
} else if (fill == '0') {
|
||||
pad_chars = pad_zeroes;
|
||||
pad_size = sizeof(pad_zeroes) - 1;
|
||||
} else {
|
||||
// Other pad characters are fairly unusual, so we'll take the hit
|
||||
// and output them 1 at a time.
|
||||
pad_chars = &fill;
|
||||
pad_size = 1;
|
||||
}
|
||||
|
||||
if (flags & PF_FLAG_CENTER_ADJUST) {
|
||||
left_pad = pad / 2;
|
||||
right_pad = pad - left_pad;
|
||||
} else if (flags & PF_FLAG_LEFT_ADJUST) {
|
||||
right_pad = pad;
|
||||
} else {
|
||||
left_pad = pad;
|
||||
}
|
||||
|
||||
if (left_pad > 0) {
|
||||
total_chars_printed += left_pad;
|
||||
while (left_pad > 0) {
|
||||
int p = left_pad;
|
||||
if (p > pad_size) {
|
||||
p = pad_size;
|
||||
}
|
||||
print->print_strn(print->data, pad_chars, p);
|
||||
left_pad -= p;
|
||||
}
|
||||
}
|
||||
if (len) {
|
||||
print->print_strn(print->data, str, len);
|
||||
total_chars_printed += len;
|
||||
}
|
||||
if (right_pad > 0) {
|
||||
total_chars_printed += right_pad;
|
||||
while (right_pad > 0) {
|
||||
int p = right_pad;
|
||||
if (p > pad_size) {
|
||||
p = pad_size;
|
||||
}
|
||||
print->print_strn(print->data, pad_chars, p);
|
||||
right_pad -= p;
|
||||
}
|
||||
}
|
||||
return total_chars_printed;
|
||||
}
|
||||
|
||||
// 32-bits is 10 digits, add 3 for commas, 1 for sign, 1 for terminating null
|
||||
// We can use 16 characters for 32-bit and 32 characters for 64-bit
|
||||
#define INT_BUF_SIZE (sizeof(mp_int_t) * 4)
|
||||
|
||||
// Our mp_vprintf function below does not support the '#' format modifier to
|
||||
// print the prefix of a non-base-10 number, so we don't need code for this.
|
||||
#define SUPPORT_INT_BASE_PREFIX (0)
|
||||
|
||||
// This function is used exclusively by mp_vprintf to format ints.
|
||||
// It needs to be a separate function to mp_print_mp_int, since converting to a mp_int looses the MSB.
|
||||
STATIC int mp_print_int(const mp_print_t *print, mp_uint_t x, int sgn, int base, int base_char, int flags, char fill, int width) {
|
||||
char sign = 0;
|
||||
if (sgn) {
|
||||
if ((mp_int_t)x < 0) {
|
||||
sign = '-';
|
||||
x = -x;
|
||||
} else if (flags & PF_FLAG_SHOW_SIGN) {
|
||||
sign = '+';
|
||||
} else if (flags & PF_FLAG_SPACE_SIGN) {
|
||||
sign = ' ';
|
||||
}
|
||||
}
|
||||
|
||||
char buf[INT_BUF_SIZE];
|
||||
char *b = buf + INT_BUF_SIZE;
|
||||
|
||||
if (x == 0) {
|
||||
*(--b) = '0';
|
||||
} else {
|
||||
do {
|
||||
int c = x % base;
|
||||
x /= base;
|
||||
if (c >= 10) {
|
||||
c += base_char - 10;
|
||||
} else {
|
||||
c += '0';
|
||||
}
|
||||
*(--b) = c;
|
||||
} while (b > buf && x != 0);
|
||||
}
|
||||
|
||||
#if SUPPORT_INT_BASE_PREFIX
|
||||
char prefix_char = '\0';
|
||||
|
||||
if (flags & PF_FLAG_SHOW_PREFIX) {
|
||||
if (base == 2) {
|
||||
prefix_char = base_char + 'b' - 'a';
|
||||
} else if (base == 8) {
|
||||
prefix_char = base_char + 'o' - 'a';
|
||||
} else if (base == 16) {
|
||||
prefix_char = base_char + 'x' - 'a';
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
int len = 0;
|
||||
if (flags & PF_FLAG_PAD_AFTER_SIGN) {
|
||||
if (sign) {
|
||||
len += mp_print_strn(print, &sign, 1, flags, fill, 1);
|
||||
width--;
|
||||
}
|
||||
#if SUPPORT_INT_BASE_PREFIX
|
||||
if (prefix_char) {
|
||||
len += mp_print_strn(print, "0", 1, flags, fill, 1);
|
||||
len += mp_print_strn(print, &prefix_char, 1, flags, fill, 1);
|
||||
width -= 2;
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
#if SUPPORT_INT_BASE_PREFIX
|
||||
if (prefix_char && b > &buf[1]) {
|
||||
*(--b) = prefix_char;
|
||||
*(--b) = '0';
|
||||
}
|
||||
#endif
|
||||
if (sign && b > buf) {
|
||||
*(--b) = sign;
|
||||
}
|
||||
}
|
||||
|
||||
len += mp_print_strn(print, b, buf + INT_BUF_SIZE - b, flags, fill, width);
|
||||
return len;
|
||||
}
|
||||
|
||||
int mp_print_mp_int(const mp_print_t *print, mp_obj_t x, int base, int base_char, int flags, char fill, int width, int prec) {
|
||||
// These are the only values for "base" that are required to be supported by this
|
||||
// function, since Python only allows the user to format integers in these bases.
|
||||
// If needed this function could be generalised to handle other values.
|
||||
assert(base == 2 || base == 8 || base == 10 || base == 16);
|
||||
|
||||
if (!mp_obj_is_int(x)) {
|
||||
// This will convert booleans to int, or raise an error for
|
||||
// non-integer types.
|
||||
x = MP_OBJ_NEW_SMALL_INT(mp_obj_get_int(x));
|
||||
}
|
||||
|
||||
if ((flags & (PF_FLAG_LEFT_ADJUST | PF_FLAG_CENTER_ADJUST)) == 0 && fill == '0') {
|
||||
if (prec > width) {
|
||||
width = prec;
|
||||
}
|
||||
prec = 0;
|
||||
}
|
||||
char prefix_buf[4];
|
||||
char *prefix = prefix_buf;
|
||||
|
||||
if (mp_obj_int_sign(x) >= 0) {
|
||||
if (flags & PF_FLAG_SHOW_SIGN) {
|
||||
*prefix++ = '+';
|
||||
} else if (flags & PF_FLAG_SPACE_SIGN) {
|
||||
*prefix++ = ' ';
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & PF_FLAG_SHOW_PREFIX) {
|
||||
if (base == 2) {
|
||||
*prefix++ = '0';
|
||||
*prefix++ = base_char + 'b' - 'a';
|
||||
} else if (base == 8) {
|
||||
*prefix++ = '0';
|
||||
if (flags & PF_FLAG_SHOW_OCTAL_LETTER) {
|
||||
*prefix++ = base_char + 'o' - 'a';
|
||||
}
|
||||
} else if (base == 16) {
|
||||
*prefix++ = '0';
|
||||
*prefix++ = base_char + 'x' - 'a';
|
||||
}
|
||||
}
|
||||
*prefix = '\0';
|
||||
int prefix_len = prefix - prefix_buf;
|
||||
prefix = prefix_buf;
|
||||
|
||||
char comma = '\0';
|
||||
if (flags & PF_FLAG_SHOW_COMMA) {
|
||||
comma = ',';
|
||||
}
|
||||
|
||||
// The size of this buffer is rather arbitrary. If it's not large
|
||||
// enough, a dynamic one will be allocated.
|
||||
char stack_buf[sizeof(mp_int_t) * 4];
|
||||
char *buf = stack_buf;
|
||||
size_t buf_size = sizeof(stack_buf);
|
||||
size_t fmt_size = 0;
|
||||
char *str;
|
||||
|
||||
if (prec > 1) {
|
||||
flags |= PF_FLAG_PAD_AFTER_SIGN;
|
||||
}
|
||||
char sign = '\0';
|
||||
if (flags & PF_FLAG_PAD_AFTER_SIGN) {
|
||||
// We add the pad in this function, so since the pad goes after
|
||||
// the sign & prefix, we format without a prefix
|
||||
str = mp_obj_int_formatted(&buf, &buf_size, &fmt_size,
|
||||
x, base, NULL, base_char, comma);
|
||||
if (*str == '-') {
|
||||
sign = *str++;
|
||||
fmt_size--;
|
||||
}
|
||||
} else {
|
||||
str = mp_obj_int_formatted(&buf, &buf_size, &fmt_size,
|
||||
x, base, prefix, base_char, comma);
|
||||
}
|
||||
|
||||
int spaces_before = 0;
|
||||
int spaces_after = 0;
|
||||
|
||||
if (prec > 1) {
|
||||
// If prec was specified, then prec specifies the width to zero-pad the
|
||||
// the number to. This zero-padded number then gets left or right
|
||||
// aligned in width characters.
|
||||
|
||||
int prec_width = fmt_size; // The digits
|
||||
if (prec_width < prec) {
|
||||
prec_width = prec;
|
||||
}
|
||||
if (flags & PF_FLAG_PAD_AFTER_SIGN) {
|
||||
if (sign) {
|
||||
prec_width++;
|
||||
}
|
||||
prec_width += prefix_len;
|
||||
}
|
||||
if (prec_width < width) {
|
||||
if (flags & PF_FLAG_LEFT_ADJUST) {
|
||||
spaces_after = width - prec_width;
|
||||
} else {
|
||||
spaces_before = width - prec_width;
|
||||
}
|
||||
}
|
||||
fill = '0';
|
||||
flags &= ~PF_FLAG_LEFT_ADJUST;
|
||||
}
|
||||
|
||||
int len = 0;
|
||||
if (spaces_before) {
|
||||
len += mp_print_strn(print, "", 0, 0, ' ', spaces_before);
|
||||
}
|
||||
if (flags & PF_FLAG_PAD_AFTER_SIGN) {
|
||||
// pad after sign implies pad after prefix as well.
|
||||
if (sign) {
|
||||
len += mp_print_strn(print, &sign, 1, 0, 0, 1);
|
||||
width--;
|
||||
}
|
||||
if (prefix_len) {
|
||||
len += mp_print_strn(print, prefix, prefix_len, 0, 0, 1);
|
||||
width -= prefix_len;
|
||||
}
|
||||
}
|
||||
if (prec > 1) {
|
||||
width = prec;
|
||||
}
|
||||
|
||||
len += mp_print_strn(print, str, fmt_size, flags, fill, width);
|
||||
|
||||
if (spaces_after) {
|
||||
len += mp_print_strn(print, "", 0, 0, ' ', spaces_after);
|
||||
}
|
||||
|
||||
if (buf != stack_buf) {
|
||||
m_del(char, buf, buf_size);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
#if MICROPY_PY_BUILTINS_FLOAT
|
||||
int mp_print_float(const mp_print_t *print, mp_float_t f, char fmt, int flags, char fill, int width, int prec) {
|
||||
char buf[32];
|
||||
char sign = '\0';
|
||||
int chrs = 0;
|
||||
|
||||
if (flags & PF_FLAG_SHOW_SIGN) {
|
||||
sign = '+';
|
||||
} else
|
||||
if (flags & PF_FLAG_SPACE_SIGN) {
|
||||
sign = ' ';
|
||||
}
|
||||
|
||||
int len = mp_format_float(f, buf, sizeof(buf), fmt, prec, sign);
|
||||
|
||||
char *s = buf;
|
||||
|
||||
if ((flags & PF_FLAG_ADD_PERCENT) && (size_t)(len + 1) < sizeof(buf)) {
|
||||
buf[len++] = '%';
|
||||
buf[len] = '\0';
|
||||
}
|
||||
|
||||
// buf[0] < '0' returns true if the first character is space, + or -
|
||||
if ((flags & PF_FLAG_PAD_AFTER_SIGN) && buf[0] < '0') {
|
||||
// We have a sign character
|
||||
s++;
|
||||
chrs += mp_print_strn(print, &buf[0], 1, 0, 0, 1);
|
||||
width--;
|
||||
len--;
|
||||
}
|
||||
|
||||
chrs += mp_print_strn(print, s, len, flags, fill, width);
|
||||
|
||||
return chrs;
|
||||
}
|
||||
#endif
|
||||
|
||||
int mp_printf(const mp_print_t *print, const char *fmt, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
int ret = mp_vprintf(print, fmt, ap);
|
||||
va_end(ap);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int mp_vprintf(const mp_print_t *print, const char *fmt, va_list args) {
|
||||
int chrs = 0;
|
||||
for (;;) {
|
||||
{
|
||||
const char *f = fmt;
|
||||
while (*f != '\0' && *f != '%') {
|
||||
++f; // XXX UTF8 advance char
|
||||
}
|
||||
if (f > fmt) {
|
||||
print->print_strn(print->data, fmt, f - fmt);
|
||||
chrs += f - fmt;
|
||||
fmt = f;
|
||||
}
|
||||
}
|
||||
|
||||
if (*fmt == '\0') {
|
||||
break;
|
||||
}
|
||||
|
||||
// move past % character
|
||||
++fmt;
|
||||
|
||||
// parse flags, if they exist
|
||||
int flags = 0;
|
||||
char fill = ' ';
|
||||
while (*fmt != '\0') {
|
||||
if (*fmt == '-') {
|
||||
flags |= PF_FLAG_LEFT_ADJUST;
|
||||
} else if (*fmt == '+') {
|
||||
flags |= PF_FLAG_SHOW_SIGN;
|
||||
} else if (*fmt == ' ') {
|
||||
flags |= PF_FLAG_SPACE_SIGN;
|
||||
} else if (*fmt == '!') {
|
||||
flags |= PF_FLAG_NO_TRAILZ;
|
||||
} else if (*fmt == '0') {
|
||||
flags |= PF_FLAG_PAD_AFTER_SIGN;
|
||||
fill = '0';
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
++fmt;
|
||||
}
|
||||
|
||||
// parse width, if it exists
|
||||
int width = 0;
|
||||
for (; '0' <= *fmt && *fmt <= '9'; ++fmt) {
|
||||
width = width * 10 + *fmt - '0';
|
||||
}
|
||||
|
||||
// parse precision, if it exists
|
||||
int prec = -1;
|
||||
if (*fmt == '.') {
|
||||
++fmt;
|
||||
if (*fmt == '*') {
|
||||
++fmt;
|
||||
prec = va_arg(args, int);
|
||||
} else {
|
||||
prec = 0;
|
||||
for (; '0' <= *fmt && *fmt <= '9'; ++fmt) {
|
||||
prec = prec * 10 + *fmt - '0';
|
||||
}
|
||||
}
|
||||
if (prec < 0) {
|
||||
prec = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// parse long specifiers (only for LP64 model where they make a difference)
|
||||
#ifndef __LP64__
|
||||
const
|
||||
#endif
|
||||
bool long_arg = false;
|
||||
if (*fmt == 'l') {
|
||||
++fmt;
|
||||
#ifdef __LP64__
|
||||
long_arg = true;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (*fmt == '\0') {
|
||||
break;
|
||||
}
|
||||
|
||||
switch (*fmt) {
|
||||
case 'b':
|
||||
if (va_arg(args, int)) {
|
||||
chrs += mp_print_strn(print, "true", 4, flags, fill, width);
|
||||
} else {
|
||||
chrs += mp_print_strn(print, "false", 5, flags, fill, width);
|
||||
}
|
||||
break;
|
||||
case 'c': {
|
||||
char str = va_arg(args, int);
|
||||
chrs += mp_print_strn(print, &str, 1, flags, fill, width);
|
||||
break;
|
||||
}
|
||||
case 'q': {
|
||||
qstr qst = va_arg(args, qstr);
|
||||
size_t len;
|
||||
const char *str = (const char *)qstr_data(qst, &len);
|
||||
if (prec < 0) {
|
||||
prec = len;
|
||||
}
|
||||
chrs += mp_print_strn(print, str, prec, flags, fill, width);
|
||||
break;
|
||||
}
|
||||
case 's': {
|
||||
const char *str = va_arg(args, const char *);
|
||||
#ifndef NDEBUG
|
||||
// With debugging enabled, catch printing of null string pointers
|
||||
if (prec != 0 && str == NULL) {
|
||||
chrs += mp_print_strn(print, "(null)", 6, flags, fill, width);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
if (prec < 0) {
|
||||
prec = strlen(str);
|
||||
}
|
||||
chrs += mp_print_strn(print, str, prec, flags, fill, width);
|
||||
break;
|
||||
}
|
||||
case 'd': {
|
||||
mp_int_t val;
|
||||
if (long_arg) {
|
||||
val = va_arg(args, long int);
|
||||
} else {
|
||||
val = va_arg(args, int);
|
||||
}
|
||||
chrs += mp_print_int(print, val, 1, 10, 'a', flags, fill, width);
|
||||
break;
|
||||
}
|
||||
case 'u':
|
||||
case 'x':
|
||||
case 'X': {
|
||||
int base = 16 - ((*fmt + 1) & 6); // maps char u/x/X to base 10/16/16
|
||||
char fmt_c = (*fmt & 0xf0) - 'P' + 'A'; // maps char u/x/X to char a/a/A
|
||||
mp_uint_t val;
|
||||
if (long_arg) {
|
||||
val = va_arg(args, unsigned long int);
|
||||
} else {
|
||||
val = va_arg(args, unsigned int);
|
||||
}
|
||||
chrs += mp_print_int(print, val, 0, base, fmt_c, flags, fill, width);
|
||||
break;
|
||||
}
|
||||
case 'p':
|
||||
case 'P': // don't bother to handle upcase for 'P'
|
||||
// Use unsigned long int to work on both ILP32 and LP64 systems
|
||||
chrs += mp_print_int(print, va_arg(args, unsigned long int), 0, 16, 'a', flags, fill, width);
|
||||
break;
|
||||
#if MICROPY_PY_BUILTINS_FLOAT
|
||||
case 'e':
|
||||
case 'E':
|
||||
case 'f':
|
||||
case 'F':
|
||||
case 'g':
|
||||
case 'G': {
|
||||
#if ((MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT) || (MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE))
|
||||
mp_float_t f = (mp_float_t)va_arg(args, double);
|
||||
chrs += mp_print_float(print, f, *fmt, flags, fill, width, prec);
|
||||
#else
|
||||
#error Unknown MICROPY FLOAT IMPL
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
// Because 'l' is eaten above, another 'l' means %ll. We need to support
|
||||
// this length specifier for OBJ_REPR_D (64-bit NaN boxing).
|
||||
// TODO Either enable this unconditionally, or provide a specific config var.
|
||||
#if (MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D) || defined(_WIN64)
|
||||
case 'l': {
|
||||
unsigned long long int arg_value = va_arg(args, unsigned long long int);
|
||||
++fmt;
|
||||
if (*fmt == 'u' || *fmt == 'd') {
|
||||
chrs += mp_print_int(print, arg_value, *fmt == 'd', 10, 'a', flags, fill, width);
|
||||
break;
|
||||
}
|
||||
assert(!"unsupported fmt char");
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
// if it's not %% then it's an unsupported format character
|
||||
assert(*fmt == '%' || !"unsupported fmt char");
|
||||
print->print_strn(print->data, fmt, 1);
|
||||
chrs += 1;
|
||||
break;
|
||||
}
|
||||
++fmt;
|
||||
}
|
||||
return chrs;
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013, 2014 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef MICROPY_INCLUDED_PY_MPPRINT_H
|
||||
#define MICROPY_INCLUDED_PY_MPPRINT_H
|
||||
|
||||
#include "py/mpconfig.h"
|
||||
|
||||
#define PF_FLAG_LEFT_ADJUST (0x001)
|
||||
#define PF_FLAG_SHOW_SIGN (0x002)
|
||||
#define PF_FLAG_SPACE_SIGN (0x004)
|
||||
#define PF_FLAG_NO_TRAILZ (0x008)
|
||||
#define PF_FLAG_SHOW_PREFIX (0x010)
|
||||
#define PF_FLAG_SHOW_COMMA (0x020)
|
||||
#define PF_FLAG_PAD_AFTER_SIGN (0x040)
|
||||
#define PF_FLAG_CENTER_ADJUST (0x080)
|
||||
#define PF_FLAG_ADD_PERCENT (0x100)
|
||||
#define PF_FLAG_SHOW_OCTAL_LETTER (0x200)
|
||||
|
||||
#if MICROPY_PY_IO && MICROPY_PY_SYS_STDFILES
|
||||
#define MP_PYTHON_PRINTER &mp_sys_stdout_print
|
||||
#else
|
||||
#define MP_PYTHON_PRINTER &mp_plat_print
|
||||
#endif
|
||||
|
||||
typedef void (*mp_print_strn_t)(void *data, const char *str, size_t len);
|
||||
|
||||
typedef struct _mp_print_t {
|
||||
void *data;
|
||||
mp_print_strn_t print_strn;
|
||||
} mp_print_t;
|
||||
|
||||
// All (non-debug) prints go through one of the two interfaces below.
|
||||
// 1) Wrapper for platform print function, which wraps MP_PLAT_PRINT_STRN.
|
||||
extern const mp_print_t mp_plat_print;
|
||||
#if MICROPY_PY_IO && MICROPY_PY_SYS_STDFILES
|
||||
// 2) Wrapper for printing to sys.stdout.
|
||||
extern const mp_print_t mp_sys_stdout_print;
|
||||
#endif
|
||||
|
||||
int mp_print_str(const mp_print_t *print, const char *str);
|
||||
int mp_print_strn(const mp_print_t *print, const char *str, size_t len, int flags, char fill, int width);
|
||||
#if MICROPY_PY_BUILTINS_FLOAT
|
||||
int mp_print_float(const mp_print_t *print, mp_float_t f, char fmt, int flags, char fill, int width, int prec);
|
||||
#endif
|
||||
|
||||
int mp_printf(const mp_print_t *print, const char *fmt, ...);
|
||||
#ifdef va_start
|
||||
int mp_vprintf(const mp_print_t *print, const char *fmt, va_list args);
|
||||
#endif
|
||||
|
||||
#endif // MICROPY_INCLUDED_PY_MPPRINT_H
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "py/mpstate.h"
|
||||
|
||||
#if MICROPY_DYNAMIC_COMPILER
|
||||
mp_dynamic_compiler_t mp_dynamic_compiler = {0};
|
||||
#endif
|
||||
|
||||
mp_state_ctx_t mp_state_ctx;
|
||||
@@ -0,0 +1,294 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef MICROPY_INCLUDED_PY_MPSTATE_H
|
||||
#define MICROPY_INCLUDED_PY_MPSTATE_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "py/mpconfig.h"
|
||||
#include "py/mpthread.h"
|
||||
#include "py/misc.h"
|
||||
#include "py/nlr.h"
|
||||
#include "py/obj.h"
|
||||
#include "py/objlist.h"
|
||||
#include "py/objexcept.h"
|
||||
|
||||
// This file contains structures defining the state of the MicroPython
|
||||
// memory system, runtime and virtual machine. The state is a global
|
||||
// variable, but in the future it is hoped that the state can become local.
|
||||
|
||||
// This structure contains dynamic configuration for the compiler.
|
||||
#if MICROPY_DYNAMIC_COMPILER
|
||||
typedef struct mp_dynamic_compiler_t {
|
||||
uint8_t small_int_bits; // must be <= host small_int_bits
|
||||
bool opt_cache_map_lookup_in_bytecode;
|
||||
bool py_builtins_str_unicode;
|
||||
uint8_t native_arch;
|
||||
uint8_t nlr_buf_num_regs;
|
||||
} mp_dynamic_compiler_t;
|
||||
extern mp_dynamic_compiler_t mp_dynamic_compiler;
|
||||
#endif
|
||||
|
||||
// These are the values for sched_state
|
||||
#define MP_SCHED_IDLE (1)
|
||||
#define MP_SCHED_LOCKED (-1)
|
||||
#define MP_SCHED_PENDING (0) // 0 so it's a quick check in the VM
|
||||
|
||||
typedef struct _mp_sched_item_t {
|
||||
mp_obj_t func;
|
||||
mp_obj_t arg;
|
||||
} mp_sched_item_t;
|
||||
|
||||
// This structure hold information about the memory allocation system.
|
||||
typedef struct _mp_state_mem_t {
|
||||
#if MICROPY_MEM_STATS
|
||||
size_t total_bytes_allocated;
|
||||
size_t current_bytes_allocated;
|
||||
size_t peak_bytes_allocated;
|
||||
#endif
|
||||
|
||||
byte *gc_alloc_table_start;
|
||||
size_t gc_alloc_table_byte_len;
|
||||
#if MICROPY_ENABLE_FINALISER
|
||||
byte *gc_finaliser_table_start;
|
||||
#endif
|
||||
byte *gc_pool_start;
|
||||
byte *gc_pool_end;
|
||||
|
||||
int gc_stack_overflow;
|
||||
MICROPY_GC_STACK_ENTRY_TYPE gc_stack[MICROPY_ALLOC_GC_STACK_SIZE];
|
||||
uint16_t gc_lock_depth;
|
||||
|
||||
// This variable controls auto garbage collection. If set to 0 then the
|
||||
// GC won't automatically run when gc_alloc can't find enough blocks. But
|
||||
// you can still allocate/free memory and also explicitly call gc_collect.
|
||||
uint16_t gc_auto_collect_enabled;
|
||||
|
||||
#if MICROPY_GC_ALLOC_THRESHOLD
|
||||
size_t gc_alloc_amount;
|
||||
size_t gc_alloc_threshold;
|
||||
#endif
|
||||
|
||||
size_t gc_last_free_atb_index;
|
||||
|
||||
#if MICROPY_PY_GC_COLLECT_RETVAL
|
||||
size_t gc_collected;
|
||||
#endif
|
||||
|
||||
#if MICROPY_PY_THREAD && !MICROPY_PY_THREAD_GIL
|
||||
// This is a global mutex used to make the GC thread-safe.
|
||||
mp_thread_mutex_t gc_mutex;
|
||||
#endif
|
||||
} mp_state_mem_t;
|
||||
|
||||
// This structure hold runtime and VM information. It includes a section
|
||||
// which contains root pointers that must be scanned by the GC.
|
||||
typedef struct _mp_state_vm_t {
|
||||
//
|
||||
// CONTINUE ROOT POINTER SECTION
|
||||
// This must start at the start of this structure and follows
|
||||
// the state in the mp_state_thread_t structure, continuing
|
||||
// the root pointer section from there.
|
||||
//
|
||||
|
||||
qstr_pool_t *last_pool;
|
||||
|
||||
// non-heap memory for creating an exception if we can't allocate RAM
|
||||
mp_obj_exception_t mp_emergency_exception_obj;
|
||||
|
||||
// memory for exception arguments if we can't allocate RAM
|
||||
#if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF
|
||||
#if MICROPY_EMERGENCY_EXCEPTION_BUF_SIZE > 0
|
||||
// statically allocated buf (needs to be aligned to mp_obj_t)
|
||||
mp_obj_t mp_emergency_exception_buf[MICROPY_EMERGENCY_EXCEPTION_BUF_SIZE / sizeof(mp_obj_t)];
|
||||
#else
|
||||
// dynamically allocated buf
|
||||
byte *mp_emergency_exception_buf;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if MICROPY_KBD_EXCEPTION
|
||||
// exception object of type KeyboardInterrupt
|
||||
mp_obj_exception_t mp_kbd_exception;
|
||||
#endif
|
||||
|
||||
// dictionary with loaded modules (may be exposed as sys.modules)
|
||||
mp_obj_dict_t mp_loaded_modules_dict;
|
||||
|
||||
// pending exception object (MP_OBJ_NULL if not pending)
|
||||
volatile mp_obj_t mp_pending_exception;
|
||||
|
||||
#if MICROPY_ENABLE_SCHEDULER
|
||||
mp_sched_item_t sched_queue[MICROPY_SCHEDULER_DEPTH];
|
||||
#endif
|
||||
|
||||
// current exception being handled, for sys.exc_info()
|
||||
#if MICROPY_PY_SYS_EXC_INFO
|
||||
mp_obj_base_t *cur_exception;
|
||||
#endif
|
||||
|
||||
#if MICROPY_PY_SYS_ATEXIT
|
||||
// exposed through sys.atexit function
|
||||
mp_obj_t sys_exitfunc;
|
||||
#endif
|
||||
|
||||
// dictionary for the __main__ module
|
||||
mp_obj_dict_t dict_main;
|
||||
|
||||
// these two lists must be initialised per port, after the call to mp_init
|
||||
mp_obj_list_t mp_sys_path_obj;
|
||||
mp_obj_list_t mp_sys_argv_obj;
|
||||
|
||||
// dictionary for overridden builtins
|
||||
#if MICROPY_CAN_OVERRIDE_BUILTINS
|
||||
mp_obj_dict_t *mp_module_builtins_override_dict;
|
||||
#endif
|
||||
|
||||
#if MICROPY_PERSISTENT_CODE_TRACK_RELOC_CODE
|
||||
// An mp_obj_list_t that tracks relocated native code to prevent the GC from reclaiming them.
|
||||
mp_obj_t track_reloc_code_list;
|
||||
#endif
|
||||
|
||||
// include any root pointers defined by a port
|
||||
MICROPY_PORT_ROOT_POINTERS
|
||||
|
||||
// root pointers for extmod
|
||||
|
||||
#if MICROPY_REPL_EVENT_DRIVEN
|
||||
vstr_t *repl_line;
|
||||
#endif
|
||||
|
||||
#if MICROPY_PY_OS_DUPTERM
|
||||
mp_obj_t dupterm_objs[MICROPY_PY_OS_DUPTERM];
|
||||
#endif
|
||||
|
||||
#if MICROPY_PY_LWIP_SLIP
|
||||
mp_obj_t lwip_slip_stream;
|
||||
#endif
|
||||
|
||||
#if MICROPY_VFS
|
||||
struct _mp_vfs_mount_t *vfs_cur;
|
||||
struct _mp_vfs_mount_t *vfs_mount_table;
|
||||
#endif
|
||||
|
||||
#if MICROPY_PY_BLUETOOTH
|
||||
mp_obj_t bluetooth;
|
||||
#endif
|
||||
|
||||
//
|
||||
// END ROOT POINTER SECTION
|
||||
////////////////////////////////////////////////////////////
|
||||
|
||||
// pointer and sizes to store interned string data
|
||||
// (qstr_last_chunk can be root pointer but is also stored in qstr pool)
|
||||
byte *qstr_last_chunk;
|
||||
size_t qstr_last_alloc;
|
||||
size_t qstr_last_used;
|
||||
|
||||
#if MICROPY_PY_THREAD && !MICROPY_PY_THREAD_GIL
|
||||
// This is a global mutex used to make qstr interning thread-safe.
|
||||
mp_thread_mutex_t qstr_mutex;
|
||||
#endif
|
||||
|
||||
#if MICROPY_ENABLE_COMPILER
|
||||
mp_uint_t mp_optimise_value;
|
||||
#if MICROPY_EMIT_NATIVE
|
||||
uint8_t default_emit_opt; // one of MP_EMIT_OPT_xxx
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// size of the emergency exception buf, if it's dynamically allocated
|
||||
#if MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF && MICROPY_EMERGENCY_EXCEPTION_BUF_SIZE == 0
|
||||
mp_int_t mp_emergency_exception_buf_size;
|
||||
#endif
|
||||
|
||||
#if MICROPY_ENABLE_SCHEDULER
|
||||
volatile int16_t sched_state;
|
||||
uint8_t sched_len;
|
||||
uint8_t sched_idx;
|
||||
#endif
|
||||
|
||||
#if MICROPY_PY_THREAD_GIL
|
||||
// This is a global mutex used to make the VM/runtime thread-safe.
|
||||
mp_thread_mutex_t gil_mutex;
|
||||
#endif
|
||||
} mp_state_vm_t;
|
||||
|
||||
// This structure holds state that is specific to a given thread.
|
||||
// Everything in this structure is scanned for root pointers.
|
||||
typedef struct _mp_state_thread_t {
|
||||
// Stack top at the start of program
|
||||
char *stack_top;
|
||||
|
||||
#if MICROPY_STACK_CHECK
|
||||
size_t stack_limit;
|
||||
#endif
|
||||
|
||||
#if MICROPY_ENABLE_PYSTACK
|
||||
uint8_t *pystack_start;
|
||||
uint8_t *pystack_end;
|
||||
uint8_t *pystack_cur;
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// START ROOT POINTER SECTION
|
||||
// Everything that needs GC scanning must start here, and
|
||||
// is followed by state in the mp_state_vm_t structure.
|
||||
//
|
||||
|
||||
mp_obj_dict_t *dict_locals;
|
||||
mp_obj_dict_t *dict_globals;
|
||||
|
||||
nlr_buf_t *nlr_top;
|
||||
|
||||
#if MICROPY_PY_SYS_SETTRACE
|
||||
mp_obj_t prof_trace_callback;
|
||||
bool prof_callback_is_executing;
|
||||
struct _mp_code_state_t *current_code_state;
|
||||
#endif
|
||||
} mp_state_thread_t;
|
||||
|
||||
// This structure combines the above 3 structures.
|
||||
// The order of the entries are important for root pointer scanning in the GC to work.
|
||||
typedef struct _mp_state_ctx_t {
|
||||
mp_state_thread_t thread;
|
||||
mp_state_vm_t vm;
|
||||
mp_state_mem_t mem;
|
||||
} mp_state_ctx_t;
|
||||
|
||||
extern mp_state_ctx_t mp_state_ctx;
|
||||
|
||||
#define MP_STATE_VM(x) (mp_state_ctx.vm.x)
|
||||
#define MP_STATE_MEM(x) (mp_state_ctx.mem.x)
|
||||
|
||||
#if MICROPY_PY_THREAD
|
||||
extern mp_state_thread_t *mp_thread_get_state(void);
|
||||
#define MP_STATE_THREAD(x) (mp_thread_get_state()->x)
|
||||
#else
|
||||
#define MP_STATE_THREAD(x) (mp_state_ctx.thread.x)
|
||||
#endif
|
||||
|
||||
#endif // MICROPY_INCLUDED_PY_MPSTATE_H
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef MICROPY_INCLUDED_PY_MPTHREAD_H
|
||||
#define MICROPY_INCLUDED_PY_MPTHREAD_H
|
||||
|
||||
#include "py/mpconfig.h"
|
||||
|
||||
#if MICROPY_PY_THREAD
|
||||
|
||||
struct _mp_state_thread_t;
|
||||
|
||||
#ifdef MICROPY_MPTHREADPORT_H
|
||||
#include MICROPY_MPTHREADPORT_H
|
||||
#else
|
||||
#include <mpthreadport.h>
|
||||
#endif
|
||||
|
||||
struct _mp_state_thread_t *mp_thread_get_state(void);
|
||||
void mp_thread_set_state(struct _mp_state_thread_t *state);
|
||||
void mp_thread_create(void *(*entry)(void *), void *arg, size_t *stack_size);
|
||||
void mp_thread_start(void);
|
||||
void mp_thread_finish(void);
|
||||
void mp_thread_mutex_init(mp_thread_mutex_t *mutex);
|
||||
int mp_thread_mutex_lock(mp_thread_mutex_t *mutex, int wait);
|
||||
void mp_thread_mutex_unlock(mp_thread_mutex_t *mutex);
|
||||
|
||||
#endif // MICROPY_PY_THREAD
|
||||
|
||||
#if MICROPY_PY_THREAD && MICROPY_PY_THREAD_GIL
|
||||
#include "py/mpstate.h"
|
||||
#define MP_THREAD_GIL_ENTER() mp_thread_mutex_lock(&MP_STATE_VM(gil_mutex), 1)
|
||||
#define MP_THREAD_GIL_EXIT() mp_thread_mutex_unlock(&MP_STATE_VM(gil_mutex))
|
||||
#else
|
||||
#define MP_THREAD_GIL_ENTER()
|
||||
#define MP_THREAD_GIL_EXIT()
|
||||
#endif
|
||||
|
||||
#endif // MICROPY_INCLUDED_PY_MPTHREAD_H
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013, 2014 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef MICROPY_INCLUDED_PY_MPZ_H
|
||||
#define MICROPY_INCLUDED_PY_MPZ_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "py/mpconfig.h"
|
||||
#include "py/misc.h"
|
||||
|
||||
// This mpz module implements arbitrary precision integers.
|
||||
//
|
||||
// The storage for each digit is defined by mpz_dig_t. The actual number of
|
||||
// bits in mpz_dig_t that are used is defined by MPZ_DIG_SIZE. The machine must
|
||||
// also provide a type that is twice as wide as mpz_dig_t, in both signed and
|
||||
// unsigned versions.
|
||||
//
|
||||
// MPZ_DIG_SIZE can be between 4 and 8*sizeof(mpz_dig_t), but it makes most
|
||||
// sense to have it as large as possible. If MPZ_DIG_SIZE is not already
|
||||
// defined then it is auto-detected below, depending on the machine. The types
|
||||
// are then set based on the value of MPZ_DIG_SIZE (although they can be freely
|
||||
// changed so long as the constraints mentioned above are met).
|
||||
|
||||
#ifndef MPZ_DIG_SIZE
|
||||
#if defined(__x86_64__) || defined(_WIN64)
|
||||
// 64-bit machine, using 32-bit storage for digits
|
||||
#define MPZ_DIG_SIZE (32)
|
||||
#else
|
||||
// default: 32-bit machine, using 16-bit storage for digits
|
||||
#define MPZ_DIG_SIZE (16)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if MPZ_DIG_SIZE > 16
|
||||
#define MPZ_DBL_DIG_SIZE (64)
|
||||
typedef uint32_t mpz_dig_t;
|
||||
typedef uint64_t mpz_dbl_dig_t;
|
||||
typedef int64_t mpz_dbl_dig_signed_t;
|
||||
#elif MPZ_DIG_SIZE > 8
|
||||
#define MPZ_DBL_DIG_SIZE (32)
|
||||
typedef uint16_t mpz_dig_t;
|
||||
typedef uint32_t mpz_dbl_dig_t;
|
||||
typedef int32_t mpz_dbl_dig_signed_t;
|
||||
#elif MPZ_DIG_SIZE > 4
|
||||
#define MPZ_DBL_DIG_SIZE (16)
|
||||
typedef uint8_t mpz_dig_t;
|
||||
typedef uint16_t mpz_dbl_dig_t;
|
||||
typedef int16_t mpz_dbl_dig_signed_t;
|
||||
#else
|
||||
#define MPZ_DBL_DIG_SIZE (8)
|
||||
typedef uint8_t mpz_dig_t;
|
||||
typedef uint8_t mpz_dbl_dig_t;
|
||||
typedef int8_t mpz_dbl_dig_signed_t;
|
||||
#endif
|
||||
|
||||
#ifdef _WIN64
|
||||
#ifdef __MINGW32__
|
||||
#define MPZ_LONG_1 1LL
|
||||
#else
|
||||
#define MPZ_LONG_1 1i64
|
||||
#endif
|
||||
#else
|
||||
#define MPZ_LONG_1 1L
|
||||
#endif
|
||||
|
||||
// these define the maximum storage needed to hold an int or long long
|
||||
#define MPZ_NUM_DIG_FOR_INT ((sizeof(mp_int_t) * 8 + MPZ_DIG_SIZE - 1) / MPZ_DIG_SIZE)
|
||||
#define MPZ_NUM_DIG_FOR_LL ((sizeof(long long) * 8 + MPZ_DIG_SIZE - 1) / MPZ_DIG_SIZE)
|
||||
|
||||
typedef struct _mpz_t {
|
||||
size_t neg : 1;
|
||||
size_t fixed_dig : 1;
|
||||
size_t alloc : (8 * sizeof(size_t) - 2);
|
||||
size_t len;
|
||||
mpz_dig_t *dig;
|
||||
} mpz_t;
|
||||
|
||||
// convenience macro to declare an mpz with a digit array from the stack, initialised by an integer
|
||||
#define MPZ_CONST_INT(z, val) mpz_t z; mpz_dig_t z##_digits[MPZ_NUM_DIG_FOR_INT]; mpz_init_fixed_from_int(&z, z_digits, MPZ_NUM_DIG_FOR_INT, val);
|
||||
|
||||
void mpz_init_zero(mpz_t *z);
|
||||
void mpz_init_from_int(mpz_t *z, mp_int_t val);
|
||||
void mpz_init_fixed_from_int(mpz_t *z, mpz_dig_t *dig, size_t dig_alloc, mp_int_t val);
|
||||
void mpz_deinit(mpz_t *z);
|
||||
|
||||
void mpz_set(mpz_t *dest, const mpz_t *src);
|
||||
void mpz_set_from_int(mpz_t *z, mp_int_t src);
|
||||
void mpz_set_from_ll(mpz_t *z, long long i, bool is_signed);
|
||||
#if MICROPY_PY_BUILTINS_FLOAT
|
||||
void mpz_set_from_float(mpz_t *z, mp_float_t src);
|
||||
#endif
|
||||
size_t mpz_set_from_str(mpz_t *z, const char *str, size_t len, bool neg, unsigned int base);
|
||||
void mpz_set_from_bytes(mpz_t *z, bool big_endian, size_t len, const byte *buf);
|
||||
|
||||
static inline bool mpz_is_zero(const mpz_t *z) {
|
||||
return z->len == 0;
|
||||
}
|
||||
static inline bool mpz_is_neg(const mpz_t *z) {
|
||||
return z->len != 0 && z->neg != 0;
|
||||
}
|
||||
int mpz_cmp(const mpz_t *lhs, const mpz_t *rhs);
|
||||
|
||||
void mpz_abs_inpl(mpz_t *dest, const mpz_t *z);
|
||||
void mpz_neg_inpl(mpz_t *dest, const mpz_t *z);
|
||||
void mpz_not_inpl(mpz_t *dest, const mpz_t *z);
|
||||
void mpz_shl_inpl(mpz_t *dest, const mpz_t *lhs, mp_uint_t rhs);
|
||||
void mpz_shr_inpl(mpz_t *dest, const mpz_t *lhs, mp_uint_t rhs);
|
||||
void mpz_add_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs);
|
||||
void mpz_sub_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs);
|
||||
void mpz_mul_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs);
|
||||
void mpz_pow_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs);
|
||||
void mpz_pow3_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs, const mpz_t *mod);
|
||||
void mpz_and_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs);
|
||||
void mpz_or_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs);
|
||||
void mpz_xor_inpl(mpz_t *dest, const mpz_t *lhs, const mpz_t *rhs);
|
||||
void mpz_divmod_inpl(mpz_t *dest_quo, mpz_t *dest_rem, const mpz_t *lhs, const mpz_t *rhs);
|
||||
|
||||
static inline size_t mpz_max_num_bits(const mpz_t *z) {
|
||||
return z->len * MPZ_DIG_SIZE;
|
||||
}
|
||||
mp_int_t mpz_hash(const mpz_t *z);
|
||||
bool mpz_as_int_checked(const mpz_t *z, mp_int_t *value);
|
||||
bool mpz_as_uint_checked(const mpz_t *z, mp_uint_t *value);
|
||||
void mpz_as_bytes(const mpz_t *z, bool big_endian, size_t len, byte *buf);
|
||||
#if MICROPY_PY_BUILTINS_FLOAT
|
||||
mp_float_t mpz_as_float(const mpz_t *z);
|
||||
#endif
|
||||
size_t mpz_as_str_inpl(const mpz_t *z, unsigned int base, const char *prefix, char base_char, char comma, char *str);
|
||||
|
||||
#endif // MICROPY_INCLUDED_PY_MPZ_H
|
||||
@@ -0,0 +1,347 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "py/runtime.h"
|
||||
#include "py/smallint.h"
|
||||
#include "py/nativeglue.h"
|
||||
#include "py/gc.h"
|
||||
|
||||
#if MICROPY_DEBUG_VERBOSE // print debugging info
|
||||
#define DEBUG_printf DEBUG_printf
|
||||
#else // don't print debugging info
|
||||
#define DEBUG_printf(...) (void)0
|
||||
#endif
|
||||
|
||||
#if MICROPY_EMIT_NATIVE
|
||||
|
||||
int mp_native_type_from_qstr(qstr qst) {
|
||||
switch (qst) {
|
||||
case MP_QSTR_object:
|
||||
return MP_NATIVE_TYPE_OBJ;
|
||||
case MP_QSTR_bool:
|
||||
return MP_NATIVE_TYPE_BOOL;
|
||||
case MP_QSTR_int:
|
||||
return MP_NATIVE_TYPE_INT;
|
||||
case MP_QSTR_uint:
|
||||
return MP_NATIVE_TYPE_UINT;
|
||||
case MP_QSTR_ptr:
|
||||
return MP_NATIVE_TYPE_PTR;
|
||||
case MP_QSTR_ptr8:
|
||||
return MP_NATIVE_TYPE_PTR8;
|
||||
case MP_QSTR_ptr16:
|
||||
return MP_NATIVE_TYPE_PTR16;
|
||||
case MP_QSTR_ptr32:
|
||||
return MP_NATIVE_TYPE_PTR32;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
// convert a MicroPython object to a valid native value based on type
|
||||
mp_uint_t mp_native_from_obj(mp_obj_t obj, mp_uint_t type) {
|
||||
DEBUG_printf("mp_native_from_obj(%p, " UINT_FMT ")\n", obj, type);
|
||||
switch (type & 0xf) {
|
||||
case MP_NATIVE_TYPE_OBJ:
|
||||
return (mp_uint_t)obj;
|
||||
case MP_NATIVE_TYPE_BOOL:
|
||||
return mp_obj_is_true(obj);
|
||||
case MP_NATIVE_TYPE_INT:
|
||||
case MP_NATIVE_TYPE_UINT:
|
||||
return mp_obj_get_int_truncated(obj);
|
||||
default: { // cast obj to a pointer
|
||||
mp_buffer_info_t bufinfo;
|
||||
if (mp_get_buffer(obj, &bufinfo, MP_BUFFER_READ)) {
|
||||
return (mp_uint_t)bufinfo.buf;
|
||||
} else {
|
||||
// assume obj is an integer that represents an address
|
||||
return mp_obj_get_int_truncated(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if MICROPY_EMIT_MACHINE_CODE
|
||||
|
||||
// convert a native value to a MicroPython object based on type
|
||||
mp_obj_t mp_native_to_obj(mp_uint_t val, mp_uint_t type) {
|
||||
DEBUG_printf("mp_native_to_obj(" UINT_FMT ", " UINT_FMT ")\n", val, type);
|
||||
switch (type & 0xf) {
|
||||
case MP_NATIVE_TYPE_OBJ:
|
||||
return (mp_obj_t)val;
|
||||
case MP_NATIVE_TYPE_BOOL:
|
||||
return mp_obj_new_bool(val);
|
||||
case MP_NATIVE_TYPE_INT:
|
||||
return mp_obj_new_int(val);
|
||||
case MP_NATIVE_TYPE_UINT:
|
||||
return mp_obj_new_int_from_uint(val);
|
||||
default: // a pointer
|
||||
// we return just the value of the pointer as an integer
|
||||
return mp_obj_new_int_from_uint(val);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if MICROPY_EMIT_NATIVE && !MICROPY_DYNAMIC_COMPILER
|
||||
|
||||
#if !MICROPY_PY_BUILTINS_SET
|
||||
mp_obj_t mp_obj_new_set(size_t n_args, mp_obj_t *items) {
|
||||
(void)n_args;
|
||||
(void)items;
|
||||
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("set unsupported"));
|
||||
}
|
||||
|
||||
void mp_obj_set_store(mp_obj_t self_in, mp_obj_t item) {
|
||||
(void)self_in;
|
||||
(void)item;
|
||||
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("set unsupported"));
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !MICROPY_PY_BUILTINS_SLICE
|
||||
mp_obj_t mp_obj_new_slice(mp_obj_t ostart, mp_obj_t ostop, mp_obj_t ostep) {
|
||||
(void)ostart;
|
||||
(void)ostop;
|
||||
(void)ostep;
|
||||
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("slice unsupported"));
|
||||
}
|
||||
#endif
|
||||
|
||||
STATIC mp_obj_dict_t *mp_native_swap_globals(mp_obj_dict_t *new_globals) {
|
||||
if (new_globals == NULL) {
|
||||
// Globals were the originally the same so don't restore them
|
||||
return NULL;
|
||||
}
|
||||
mp_obj_dict_t *old_globals = mp_globals_get();
|
||||
if (old_globals == new_globals) {
|
||||
// Don't set globals if they are the same, and return NULL to indicate this
|
||||
return NULL;
|
||||
}
|
||||
mp_globals_set(new_globals);
|
||||
return old_globals;
|
||||
}
|
||||
|
||||
// wrapper that accepts n_args and n_kw in one argument
|
||||
// (native emitter can only pass at most 3 arguments to a function)
|
||||
STATIC mp_obj_t mp_native_call_function_n_kw(mp_obj_t fun_in, size_t n_args_kw, const mp_obj_t *args) {
|
||||
return mp_call_function_n_kw(fun_in, n_args_kw & 0xff, (n_args_kw >> 8) & 0xff, args);
|
||||
}
|
||||
|
||||
// wrapper that makes raise obj and raises it
|
||||
// END_FINALLY opcode requires that we don't raise if o==None
|
||||
STATIC void mp_native_raise(mp_obj_t o) {
|
||||
if (o != MP_OBJ_NULL && o != mp_const_none) {
|
||||
nlr_raise(mp_make_raise_obj(o));
|
||||
}
|
||||
}
|
||||
|
||||
// wrapper that handles iterator buffer
|
||||
STATIC mp_obj_t mp_native_getiter(mp_obj_t obj, mp_obj_iter_buf_t *iter) {
|
||||
if (iter == NULL) {
|
||||
return mp_getiter(obj, NULL);
|
||||
} else {
|
||||
obj = mp_getiter(obj, iter);
|
||||
if (obj != MP_OBJ_FROM_PTR(iter)) {
|
||||
// Iterator didn't use the stack so indicate that with MP_OBJ_NULL.
|
||||
iter->base.type = MP_OBJ_NULL;
|
||||
iter->buf[0] = obj;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// wrapper that handles iterator buffer
|
||||
STATIC mp_obj_t mp_native_iternext(mp_obj_iter_buf_t *iter) {
|
||||
mp_obj_t obj;
|
||||
if (iter->base.type == MP_OBJ_NULL) {
|
||||
obj = iter->buf[0];
|
||||
} else {
|
||||
obj = MP_OBJ_FROM_PTR(iter);
|
||||
}
|
||||
return mp_iternext(obj);
|
||||
}
|
||||
|
||||
STATIC bool mp_native_yield_from(mp_obj_t gen, mp_obj_t send_value, mp_obj_t *ret_value) {
|
||||
mp_vm_return_kind_t ret_kind;
|
||||
nlr_buf_t nlr_buf;
|
||||
mp_obj_t throw_value = *ret_value;
|
||||
if (nlr_push(&nlr_buf) == 0) {
|
||||
if (throw_value != MP_OBJ_NULL) {
|
||||
send_value = MP_OBJ_NULL;
|
||||
}
|
||||
ret_kind = mp_resume(gen, send_value, throw_value, ret_value);
|
||||
nlr_pop();
|
||||
} else {
|
||||
ret_kind = MP_VM_RETURN_EXCEPTION;
|
||||
*ret_value = nlr_buf.ret_val;
|
||||
}
|
||||
|
||||
if (ret_kind == MP_VM_RETURN_YIELD) {
|
||||
return true;
|
||||
} else if (ret_kind == MP_VM_RETURN_NORMAL) {
|
||||
if (*ret_value == MP_OBJ_STOP_ITERATION) {
|
||||
*ret_value = mp_const_none;
|
||||
}
|
||||
} else {
|
||||
assert(ret_kind == MP_VM_RETURN_EXCEPTION);
|
||||
if (!mp_obj_exception_match(*ret_value, MP_OBJ_FROM_PTR(&mp_type_StopIteration))) {
|
||||
nlr_raise(*ret_value);
|
||||
}
|
||||
*ret_value = mp_obj_exception_get_value(*ret_value);
|
||||
}
|
||||
|
||||
if (throw_value != MP_OBJ_NULL && mp_obj_exception_match(throw_value, MP_OBJ_FROM_PTR(&mp_type_GeneratorExit))) {
|
||||
nlr_raise(mp_make_raise_obj(throw_value));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#if !MICROPY_PY_BUILTINS_FLOAT
|
||||
|
||||
STATIC mp_obj_t mp_obj_new_float_from_f(float f) {
|
||||
(void)f;
|
||||
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("float unsupported"));
|
||||
}
|
||||
|
||||
STATIC mp_obj_t mp_obj_new_float_from_d(double d) {
|
||||
(void)d;
|
||||
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("float unsupported"));
|
||||
}
|
||||
|
||||
STATIC float mp_obj_get_float_to_f(mp_obj_t o) {
|
||||
(void)o;
|
||||
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("float unsupported"));
|
||||
}
|
||||
|
||||
STATIC double mp_obj_get_float_to_d(mp_obj_t o) {
|
||||
(void)o;
|
||||
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("float unsupported"));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// these must correspond to the respective enum in nativeglue.h
|
||||
const mp_fun_table_t mp_fun_table = {
|
||||
mp_const_none,
|
||||
mp_const_false,
|
||||
mp_const_true,
|
||||
mp_native_from_obj,
|
||||
mp_native_to_obj,
|
||||
mp_native_swap_globals,
|
||||
mp_load_name,
|
||||
mp_load_global,
|
||||
mp_load_build_class,
|
||||
mp_load_attr,
|
||||
mp_load_method,
|
||||
mp_load_super_method,
|
||||
mp_store_name,
|
||||
mp_store_global,
|
||||
mp_store_attr,
|
||||
mp_obj_subscr,
|
||||
mp_obj_is_true,
|
||||
mp_unary_op,
|
||||
mp_binary_op,
|
||||
mp_obj_new_tuple,
|
||||
mp_obj_new_list,
|
||||
mp_obj_new_dict,
|
||||
mp_obj_new_set,
|
||||
mp_obj_set_store,
|
||||
mp_obj_list_append,
|
||||
mp_obj_dict_store,
|
||||
mp_make_function_from_raw_code,
|
||||
mp_native_call_function_n_kw,
|
||||
mp_call_method_n_kw,
|
||||
mp_call_method_n_kw_var,
|
||||
mp_native_getiter,
|
||||
mp_native_iternext,
|
||||
#if MICROPY_NLR_SETJMP
|
||||
nlr_push_tail,
|
||||
#else
|
||||
nlr_push,
|
||||
#endif
|
||||
nlr_pop,
|
||||
mp_native_raise,
|
||||
mp_import_name,
|
||||
mp_import_from,
|
||||
mp_import_all,
|
||||
mp_obj_new_slice,
|
||||
mp_unpack_sequence,
|
||||
mp_unpack_ex,
|
||||
mp_delete_name,
|
||||
mp_delete_global,
|
||||
mp_make_closure_from_raw_code,
|
||||
mp_arg_check_num_sig,
|
||||
mp_setup_code_state,
|
||||
mp_small_int_floor_divide,
|
||||
mp_small_int_modulo,
|
||||
mp_native_yield_from,
|
||||
#if MICROPY_NLR_SETJMP
|
||||
setjmp,
|
||||
#else
|
||||
NULL,
|
||||
#endif
|
||||
// Additional entries for dynamic runtime, starts at index 50
|
||||
memset,
|
||||
memmove,
|
||||
gc_realloc,
|
||||
mp_printf,
|
||||
mp_vprintf,
|
||||
mp_raise_msg,
|
||||
mp_obj_get_type,
|
||||
mp_obj_new_str,
|
||||
mp_obj_new_bytes,
|
||||
mp_obj_new_bytearray_by_ref,
|
||||
mp_obj_new_float_from_f,
|
||||
mp_obj_new_float_from_d,
|
||||
mp_obj_get_float_to_f,
|
||||
mp_obj_get_float_to_d,
|
||||
mp_get_buffer_raise,
|
||||
mp_get_stream_raise,
|
||||
&mp_plat_print,
|
||||
&mp_type_type,
|
||||
&mp_type_str,
|
||||
&mp_type_list,
|
||||
&mp_type_dict,
|
||||
&mp_type_fun_builtin_0,
|
||||
&mp_type_fun_builtin_1,
|
||||
&mp_type_fun_builtin_2,
|
||||
&mp_type_fun_builtin_3,
|
||||
&mp_type_fun_builtin_var,
|
||||
&mp_stream_read_obj,
|
||||
&mp_stream_readinto_obj,
|
||||
&mp_stream_unbuffered_readline_obj,
|
||||
&mp_stream_write_obj,
|
||||
};
|
||||
|
||||
#endif // MICROPY_EMIT_NATIVE
|
||||
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013-2019 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef MICROPY_INCLUDED_PY_NATIVEGLUE_H
|
||||
#define MICROPY_INCLUDED_PY_NATIVEGLUE_H
|
||||
|
||||
#include <stdarg.h>
|
||||
#include "py/obj.h"
|
||||
#include "py/persistentcode.h"
|
||||
#include "py/stream.h"
|
||||
|
||||
typedef enum {
|
||||
MP_F_CONST_NONE_OBJ = 0,
|
||||
MP_F_CONST_FALSE_OBJ,
|
||||
MP_F_CONST_TRUE_OBJ,
|
||||
MP_F_CONVERT_OBJ_TO_NATIVE,
|
||||
MP_F_CONVERT_NATIVE_TO_OBJ,
|
||||
MP_F_NATIVE_SWAP_GLOBALS,
|
||||
MP_F_LOAD_NAME,
|
||||
MP_F_LOAD_GLOBAL,
|
||||
MP_F_LOAD_BUILD_CLASS,
|
||||
MP_F_LOAD_ATTR,
|
||||
MP_F_LOAD_METHOD,
|
||||
MP_F_LOAD_SUPER_METHOD,
|
||||
MP_F_STORE_NAME,
|
||||
MP_F_STORE_GLOBAL,
|
||||
MP_F_STORE_ATTR,
|
||||
MP_F_OBJ_SUBSCR,
|
||||
MP_F_OBJ_IS_TRUE,
|
||||
MP_F_UNARY_OP,
|
||||
MP_F_BINARY_OP,
|
||||
MP_F_BUILD_TUPLE,
|
||||
MP_F_BUILD_LIST,
|
||||
MP_F_BUILD_MAP,
|
||||
MP_F_BUILD_SET,
|
||||
MP_F_STORE_SET,
|
||||
MP_F_LIST_APPEND,
|
||||
MP_F_STORE_MAP,
|
||||
MP_F_MAKE_FUNCTION_FROM_RAW_CODE,
|
||||
MP_F_NATIVE_CALL_FUNCTION_N_KW,
|
||||
MP_F_CALL_METHOD_N_KW,
|
||||
MP_F_CALL_METHOD_N_KW_VAR,
|
||||
MP_F_NATIVE_GETITER,
|
||||
MP_F_NATIVE_ITERNEXT,
|
||||
MP_F_NLR_PUSH,
|
||||
MP_F_NLR_POP,
|
||||
MP_F_NATIVE_RAISE,
|
||||
MP_F_IMPORT_NAME,
|
||||
MP_F_IMPORT_FROM,
|
||||
MP_F_IMPORT_ALL,
|
||||
MP_F_NEW_SLICE,
|
||||
MP_F_UNPACK_SEQUENCE,
|
||||
MP_F_UNPACK_EX,
|
||||
MP_F_DELETE_NAME,
|
||||
MP_F_DELETE_GLOBAL,
|
||||
MP_F_MAKE_CLOSURE_FROM_RAW_CODE,
|
||||
MP_F_ARG_CHECK_NUM_SIG,
|
||||
MP_F_SETUP_CODE_STATE,
|
||||
MP_F_SMALL_INT_FLOOR_DIVIDE,
|
||||
MP_F_SMALL_INT_MODULO,
|
||||
MP_F_NATIVE_YIELD_FROM,
|
||||
MP_F_SETJMP,
|
||||
MP_F_NUMBER_OF,
|
||||
} mp_fun_kind_t;
|
||||
|
||||
typedef struct _mp_fun_table_t {
|
||||
mp_const_obj_t const_none;
|
||||
mp_const_obj_t const_false;
|
||||
mp_const_obj_t const_true;
|
||||
mp_uint_t (*native_from_obj)(mp_obj_t obj, mp_uint_t type);
|
||||
mp_obj_t (*native_to_obj)(mp_uint_t val, mp_uint_t type);
|
||||
mp_obj_dict_t *(*swap_globals)(mp_obj_dict_t * new_globals);
|
||||
mp_obj_t (*load_name)(qstr qst);
|
||||
mp_obj_t (*load_global)(qstr qst);
|
||||
mp_obj_t (*load_build_class)(void);
|
||||
mp_obj_t (*load_attr)(mp_obj_t base, qstr attr);
|
||||
void (*load_method)(mp_obj_t base, qstr attr, mp_obj_t *dest);
|
||||
void (*load_super_method)(qstr attr, mp_obj_t *dest);
|
||||
void (*store_name)(qstr qst, mp_obj_t obj);
|
||||
void (*store_global)(qstr qst, mp_obj_t obj);
|
||||
void (*store_attr)(mp_obj_t base, qstr attr, mp_obj_t val);
|
||||
mp_obj_t (*obj_subscr)(mp_obj_t base, mp_obj_t index, mp_obj_t val);
|
||||
bool (*obj_is_true)(mp_obj_t arg);
|
||||
mp_obj_t (*unary_op)(mp_unary_op_t op, mp_obj_t arg);
|
||||
mp_obj_t (*binary_op)(mp_binary_op_t op, mp_obj_t lhs, mp_obj_t rhs);
|
||||
mp_obj_t (*new_tuple)(size_t n, const mp_obj_t *items);
|
||||
mp_obj_t (*new_list)(size_t n, mp_obj_t *items);
|
||||
mp_obj_t (*new_dict)(size_t n_args);
|
||||
mp_obj_t (*new_set)(size_t n_args, mp_obj_t *items);
|
||||
void (*set_store)(mp_obj_t self_in, mp_obj_t item);
|
||||
mp_obj_t (*list_append)(mp_obj_t self_in, mp_obj_t arg);
|
||||
mp_obj_t (*dict_store)(mp_obj_t self_in, mp_obj_t key, mp_obj_t value);
|
||||
mp_obj_t (*make_function_from_raw_code)(const mp_raw_code_t *rc, mp_obj_t def_args, mp_obj_t def_kw_args);
|
||||
mp_obj_t (*call_function_n_kw)(mp_obj_t fun_in, size_t n_args_kw, const mp_obj_t *args);
|
||||
mp_obj_t (*call_method_n_kw)(size_t n_args, size_t n_kw, const mp_obj_t *args);
|
||||
mp_obj_t (*call_method_n_kw_var)(bool have_self, size_t n_args_n_kw, const mp_obj_t *args);
|
||||
mp_obj_t (*getiter)(mp_obj_t obj, mp_obj_iter_buf_t *iter);
|
||||
mp_obj_t (*iternext)(mp_obj_iter_buf_t *iter);
|
||||
unsigned int (*nlr_push)(nlr_buf_t *);
|
||||
void (*nlr_pop)(void);
|
||||
void (*raise)(mp_obj_t o);
|
||||
mp_obj_t (*import_name)(qstr name, mp_obj_t fromlist, mp_obj_t level);
|
||||
mp_obj_t (*import_from)(mp_obj_t module, qstr name);
|
||||
void (*import_all)(mp_obj_t module);
|
||||
mp_obj_t (*new_slice)(mp_obj_t start, mp_obj_t stop, mp_obj_t step);
|
||||
void (*unpack_sequence)(mp_obj_t seq, size_t num, mp_obj_t *items);
|
||||
void (*unpack_ex)(mp_obj_t seq, size_t num, mp_obj_t *items);
|
||||
void (*delete_name)(qstr qst);
|
||||
void (*delete_global)(qstr qst);
|
||||
mp_obj_t (*make_closure_from_raw_code)(const mp_raw_code_t *rc, mp_uint_t n_closed_over, const mp_obj_t *args);
|
||||
void (*arg_check_num_sig)(size_t n_args, size_t n_kw, uint32_t sig);
|
||||
void (*setup_code_state)(mp_code_state_t *code_state, size_t n_args, size_t n_kw, const mp_obj_t *args);
|
||||
mp_int_t (*small_int_floor_divide)(mp_int_t num, mp_int_t denom);
|
||||
mp_int_t (*small_int_modulo)(mp_int_t dividend, mp_int_t divisor);
|
||||
bool (*yield_from)(mp_obj_t gen, mp_obj_t send_value, mp_obj_t *ret_value);
|
||||
void *setjmp_;
|
||||
// Additional entries for dynamic runtime, starts at index 50
|
||||
void *(*memset_)(void *s, int c, size_t n);
|
||||
void *(*memmove_)(void *dest, const void *src, size_t n);
|
||||
void *(*realloc_)(void *ptr, size_t n_bytes, bool allow_move);
|
||||
int (*printf_)(const mp_print_t *print, const char *fmt, ...);
|
||||
int (*vprintf_)(const mp_print_t *print, const char *fmt, va_list args);
|
||||
#if defined(__GNUC__)
|
||||
NORETURN // Only certain compilers support no-return attributes in function pointer declarations
|
||||
#endif
|
||||
void (*raise_msg)(const mp_obj_type_t *exc_type, mp_rom_error_text_t msg);
|
||||
const mp_obj_type_t *(*obj_get_type)(mp_const_obj_t o_in);
|
||||
mp_obj_t (*obj_new_str)(const char *data, size_t len);
|
||||
mp_obj_t (*obj_new_bytes)(const byte *data, size_t len);
|
||||
mp_obj_t (*obj_new_bytearray_by_ref)(size_t n, void *items);
|
||||
mp_obj_t (*obj_new_float_from_f)(float f);
|
||||
mp_obj_t (*obj_new_float_from_d)(double d);
|
||||
float (*obj_get_float_to_f)(mp_obj_t o);
|
||||
double (*obj_get_float_to_d)(mp_obj_t o);
|
||||
void (*get_buffer_raise)(mp_obj_t obj, mp_buffer_info_t *bufinfo, mp_uint_t flags);
|
||||
const mp_stream_p_t *(*get_stream_raise)(mp_obj_t self_in, int flags);
|
||||
const mp_print_t *plat_print;
|
||||
const mp_obj_type_t *type_type;
|
||||
const mp_obj_type_t *type_str;
|
||||
const mp_obj_type_t *type_list;
|
||||
const mp_obj_type_t *type_dict;
|
||||
const mp_obj_type_t *type_fun_builtin_0;
|
||||
const mp_obj_type_t *type_fun_builtin_1;
|
||||
const mp_obj_type_t *type_fun_builtin_2;
|
||||
const mp_obj_type_t *type_fun_builtin_3;
|
||||
const mp_obj_type_t *type_fun_builtin_var;
|
||||
const mp_obj_fun_builtin_var_t *stream_read_obj;
|
||||
const mp_obj_fun_builtin_var_t *stream_readinto_obj;
|
||||
const mp_obj_fun_builtin_var_t *stream_unbuffered_readline_obj;
|
||||
const mp_obj_fun_builtin_var_t *stream_write_obj;
|
||||
} mp_fun_table_t;
|
||||
|
||||
extern const mp_fun_table_t mp_fun_table;
|
||||
|
||||
#endif // MICROPY_INCLUDED_PY_NATIVEGLUE_H
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013-2017 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "py/mpstate.h"
|
||||
|
||||
#if !MICROPY_NLR_SETJMP
|
||||
// When not using setjmp, nlr_push_tail is called from inline asm so needs special care
|
||||
#if MICROPY_NLR_X86 && MICROPY_NLR_OS_WINDOWS
|
||||
// On these 32-bit platforms make sure nlr_push_tail doesn't have a leading underscore
|
||||
unsigned int nlr_push_tail(nlr_buf_t *nlr) asm ("nlr_push_tail");
|
||||
#else
|
||||
// LTO can't see inside inline asm functions so explicitly mark nlr_push_tail as used
|
||||
__attribute__((used)) unsigned int nlr_push_tail(nlr_buf_t *nlr);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
unsigned int nlr_push_tail(nlr_buf_t *nlr) {
|
||||
nlr_buf_t **top = &MP_STATE_THREAD(nlr_top);
|
||||
nlr->prev = *top;
|
||||
MP_NLR_SAVE_PYSTACK(nlr);
|
||||
*top = nlr;
|
||||
return 0; // normal return
|
||||
}
|
||||
|
||||
void nlr_pop(void) {
|
||||
nlr_buf_t **top = &MP_STATE_THREAD(nlr_top);
|
||||
*top = (*top)->prev;
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013, 2014 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef MICROPY_INCLUDED_PY_NLR_H
|
||||
#define MICROPY_INCLUDED_PY_NLR_H
|
||||
|
||||
// non-local return
|
||||
// exception handling, basically a stack of setjmp/longjmp buffers
|
||||
|
||||
#include <limits.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "py/mpconfig.h"
|
||||
|
||||
#define MICROPY_NLR_NUM_REGS_X86 (6)
|
||||
#define MICROPY_NLR_NUM_REGS_X64 (8)
|
||||
#define MICROPY_NLR_NUM_REGS_X64_WIN (10)
|
||||
#define MICROPY_NLR_NUM_REGS_ARM_THUMB (10)
|
||||
#define MICROPY_NLR_NUM_REGS_ARM_THUMB_FP (10 + 6)
|
||||
#define MICROPY_NLR_NUM_REGS_XTENSA (10)
|
||||
#define MICROPY_NLR_NUM_REGS_XTENSAWIN (17)
|
||||
|
||||
// *FORMAT-OFF*
|
||||
|
||||
// If MICROPY_NLR_SETJMP is not enabled then auto-detect the machine arch
|
||||
#if !MICROPY_NLR_SETJMP
|
||||
// A lot of nlr-related things need different treatment on Windows
|
||||
#if defined(_WIN32) || defined(__CYGWIN__)
|
||||
#define MICROPY_NLR_OS_WINDOWS 1
|
||||
#else
|
||||
#define MICROPY_NLR_OS_WINDOWS 0
|
||||
#endif
|
||||
#if defined(__i386__)
|
||||
#define MICROPY_NLR_X86 (1)
|
||||
#define MICROPY_NLR_NUM_REGS (MICROPY_NLR_NUM_REGS_X86)
|
||||
#elif defined(__x86_64__)
|
||||
#define MICROPY_NLR_X64 (1)
|
||||
#if MICROPY_NLR_OS_WINDOWS
|
||||
#define MICROPY_NLR_NUM_REGS (MICROPY_NLR_NUM_REGS_X64_WIN)
|
||||
#else
|
||||
#define MICROPY_NLR_NUM_REGS (MICROPY_NLR_NUM_REGS_X64)
|
||||
#endif
|
||||
#elif defined(__thumb2__) || defined(__thumb__) || defined(__arm__)
|
||||
#define MICROPY_NLR_THUMB (1)
|
||||
#if defined(__SOFTFP__)
|
||||
#define MICROPY_NLR_NUM_REGS (MICROPY_NLR_NUM_REGS_ARM_THUMB)
|
||||
#else
|
||||
// With hardware FP registers s16-s31 are callee save so in principle
|
||||
// should be saved and restored by the NLR code. gcc only uses s16-s21
|
||||
// so only save/restore those as an optimisation.
|
||||
#define MICROPY_NLR_NUM_REGS (MICROPY_NLR_NUM_REGS_ARM_THUMB_FP)
|
||||
#endif
|
||||
#elif defined(__xtensa__)
|
||||
#define MICROPY_NLR_XTENSA (1)
|
||||
#define MICROPY_NLR_NUM_REGS (MICROPY_NLR_NUM_REGS_XTENSA)
|
||||
#elif defined(__powerpc__)
|
||||
#define MICROPY_NLR_POWERPC (1)
|
||||
// this could be less but using 128 for safety
|
||||
#define MICROPY_NLR_NUM_REGS (128)
|
||||
#else
|
||||
#define MICROPY_NLR_SETJMP (1)
|
||||
//#warning "No native NLR support for this arch, using setjmp implementation"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// *FORMAT-ON*
|
||||
|
||||
#if MICROPY_NLR_SETJMP
|
||||
#include <setjmp.h>
|
||||
#endif
|
||||
|
||||
typedef struct _nlr_buf_t nlr_buf_t;
|
||||
struct _nlr_buf_t {
|
||||
// the entries here must all be machine word size
|
||||
nlr_buf_t *prev;
|
||||
void *ret_val; // always a concrete object (an exception instance)
|
||||
|
||||
#if MICROPY_NLR_SETJMP
|
||||
jmp_buf jmpbuf;
|
||||
#else
|
||||
void *regs[MICROPY_NLR_NUM_REGS];
|
||||
#endif
|
||||
|
||||
#if MICROPY_ENABLE_PYSTACK
|
||||
void *pystack;
|
||||
#endif
|
||||
};
|
||||
|
||||
// Helper macros to save/restore the pystack state
|
||||
#if MICROPY_ENABLE_PYSTACK
|
||||
#define MP_NLR_SAVE_PYSTACK(nlr_buf) (nlr_buf)->pystack = MP_STATE_THREAD(pystack_cur)
|
||||
#define MP_NLR_RESTORE_PYSTACK(nlr_buf) MP_STATE_THREAD(pystack_cur) = (nlr_buf)->pystack
|
||||
#else
|
||||
#define MP_NLR_SAVE_PYSTACK(nlr_buf) (void)nlr_buf
|
||||
#define MP_NLR_RESTORE_PYSTACK(nlr_buf) (void)nlr_buf
|
||||
#endif
|
||||
|
||||
// Helper macro to use at the start of a specific nlr_jump implementation
|
||||
#define MP_NLR_JUMP_HEAD(val, top) \
|
||||
nlr_buf_t **_top_ptr = &MP_STATE_THREAD(nlr_top); \
|
||||
nlr_buf_t *top = *_top_ptr; \
|
||||
if (top == NULL) { \
|
||||
nlr_jump_fail(val); \
|
||||
} \
|
||||
top->ret_val = val; \
|
||||
MP_NLR_RESTORE_PYSTACK(top); \
|
||||
*_top_ptr = top->prev; \
|
||||
|
||||
#if MICROPY_NLR_SETJMP
|
||||
// nlr_push() must be defined as a macro, because "The stack context will be
|
||||
// invalidated if the function which called setjmp() returns."
|
||||
// For this case it is safe to call nlr_push_tail() first.
|
||||
#define nlr_push(buf) (nlr_push_tail(buf), setjmp((buf)->jmpbuf))
|
||||
#else
|
||||
unsigned int nlr_push(nlr_buf_t *);
|
||||
#endif
|
||||
|
||||
unsigned int nlr_push_tail(nlr_buf_t *top);
|
||||
void nlr_pop(void);
|
||||
NORETURN void nlr_jump(void *val);
|
||||
|
||||
// This must be implemented by a port. It's called by nlr_jump
|
||||
// if no nlr buf has been pushed. It must not return, but rather
|
||||
// should bail out with a fatal error.
|
||||
NORETURN void nlr_jump_fail(void *val);
|
||||
|
||||
// use nlr_raise instead of nlr_jump so that debugging is easier
|
||||
#ifndef MICROPY_DEBUG_NLR
|
||||
#define nlr_raise(val) nlr_jump(MP_OBJ_TO_PTR(val))
|
||||
#else
|
||||
#include "mpstate.h"
|
||||
#define nlr_raise(val) \
|
||||
do { \
|
||||
/*printf("nlr_raise: nlr_top=%p\n", MP_STATE_THREAD(nlr_top)); \
|
||||
fflush(stdout);*/ \
|
||||
void *_val = MP_OBJ_TO_PTR(val); \
|
||||
assert(_val != NULL); \
|
||||
assert(mp_obj_is_exception_instance(val)); \
|
||||
nlr_jump(_val); \
|
||||
} while (0)
|
||||
|
||||
#if !MICROPY_NLR_SETJMP
|
||||
#define nlr_push(val) \
|
||||
assert(MP_STATE_THREAD(nlr_top) != val),nlr_push(val)
|
||||
|
||||
/*
|
||||
#define nlr_push(val) \
|
||||
printf("nlr_push: before: nlr_top=%p, val=%p\n", MP_STATE_THREAD(nlr_top), val),assert(MP_STATE_THREAD(nlr_top) != val),nlr_push(val)
|
||||
*/
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#endif // MICROPY_INCLUDED_PY_NLR_H
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2019, Michael Neuling, IBM Corporation.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "py/mpstate.h"
|
||||
|
||||
#if MICROPY_NLR_POWERPC
|
||||
|
||||
#undef nlr_push
|
||||
|
||||
// Saving all ABI non-vol registers here
|
||||
|
||||
unsigned int nlr_push(nlr_buf_t *nlr) {
|
||||
|
||||
__asm__ volatile (
|
||||
"li 4, 0x4eed ; " // Store canary
|
||||
"std 4, 0x00(%0) ;"
|
||||
"std 0, 0x08(%0) ;"
|
||||
"std 1, 0x10(%0) ;"
|
||||
"std 2, 0x18(%0) ;"
|
||||
"std 14, 0x20(%0) ;"
|
||||
"std 15, 0x28(%0) ;"
|
||||
"std 16, 0x30(%0) ;"
|
||||
"std 17, 0x38(%0) ;"
|
||||
"std 18, 0x40(%0) ;"
|
||||
"std 19, 0x48(%0) ;"
|
||||
"std 20, 0x50(%0) ;"
|
||||
"std 21, 0x58(%0) ;"
|
||||
"std 22, 0x60(%0) ;"
|
||||
"std 23, 0x68(%0) ;"
|
||||
"std 24, 0x70(%0) ;"
|
||||
"std 25, 0x78(%0) ;"
|
||||
"std 26, 0x80(%0) ;"
|
||||
"std 27, 0x88(%0) ;"
|
||||
"std 28, 0x90(%0) ;"
|
||||
"std 29, 0x98(%0) ;"
|
||||
"std 30, 0xA0(%0) ;"
|
||||
"std 31, 0xA8(%0) ;"
|
||||
|
||||
"mfcr 4 ; "
|
||||
"std 4, 0xB0(%0) ;"
|
||||
"mflr 4 ;"
|
||||
"std 4, 0xB8(%0) ;"
|
||||
"li 4, nlr_push_tail@l ;"
|
||||
"oris 4, 4, nlr_push_tail@h ;"
|
||||
"mtctr 4 ;"
|
||||
"mr 3, %1 ; "
|
||||
"bctr ;"
|
||||
:
|
||||
: "r" (&nlr->regs), "r" (nlr)
|
||||
:
|
||||
);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
NORETURN void nlr_jump(void *val) {
|
||||
MP_NLR_JUMP_HEAD(val, top)
|
||||
|
||||
__asm__ volatile (
|
||||
"ld 3, 0x0(%0) ;"
|
||||
"cmpdi 3, 0x4eed ; " // Check canary
|
||||
"bne . ; "
|
||||
"ld 0, 0x08(%0) ;"
|
||||
"ld 1, 0x10(%0) ;"
|
||||
"ld 2, 0x18(%0) ;"
|
||||
"ld 14, 0x20(%0) ;"
|
||||
"ld 15, 0x28(%0) ;"
|
||||
"ld 16, 0x30(%0) ;"
|
||||
"ld 17, 0x38(%0) ;"
|
||||
"ld 18, 0x40(%0) ;"
|
||||
"ld 19, 0x48(%0) ;"
|
||||
"ld 20, 0x50(%0) ;"
|
||||
"ld 21, 0x58(%0) ;"
|
||||
"ld 22, 0x60(%0) ;"
|
||||
"ld 23, 0x68(%0) ;"
|
||||
"ld 24, 0x70(%0) ;"
|
||||
"ld 25, 0x78(%0) ;"
|
||||
"ld 26, 0x80(%0) ;"
|
||||
"ld 27, 0x88(%0) ;"
|
||||
"ld 28, 0x90(%0) ;"
|
||||
"ld 29, 0x98(%0) ;"
|
||||
"ld 30, 0xA0(%0) ;"
|
||||
"ld 31, 0xA8(%0) ;"
|
||||
"ld 3, 0xB0(%0) ;"
|
||||
"mtcr 3 ;"
|
||||
"ld 3, 0xB8(%0) ;"
|
||||
"mtlr 3 ; "
|
||||
"li 3, 1;"
|
||||
"blr ;"
|
||||
:
|
||||
: "r" (&top->regs)
|
||||
:
|
||||
);
|
||||
|
||||
MP_UNREACHABLE;
|
||||
}
|
||||
|
||||
#endif // MICROPY_NLR_POWERPC
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013-2017 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "py/mpstate.h"
|
||||
|
||||
#if MICROPY_NLR_SETJMP
|
||||
|
||||
void nlr_jump(void *val) {
|
||||
nlr_buf_t **top_ptr = &MP_STATE_THREAD(nlr_top);
|
||||
nlr_buf_t *top = *top_ptr;
|
||||
if (top == NULL) {
|
||||
nlr_jump_fail(val);
|
||||
}
|
||||
top->ret_val = val;
|
||||
MP_NLR_RESTORE_PYSTACK(top);
|
||||
*top_ptr = top->prev;
|
||||
longjmp(top->jmpbuf, 1);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013-2017 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "py/mpstate.h"
|
||||
|
||||
#if MICROPY_NLR_THUMB
|
||||
|
||||
#undef nlr_push
|
||||
|
||||
// We only need the functions here if we are on arm/thumb, and we are not
|
||||
// using setjmp/longjmp.
|
||||
//
|
||||
// For reference, arm/thumb callee save regs are:
|
||||
// r4-r11, r13=sp
|
||||
|
||||
__attribute__((naked)) unsigned int nlr_push(nlr_buf_t *nlr) {
|
||||
|
||||
__asm volatile (
|
||||
"str r4, [r0, #12] \n" // store r4 into nlr_buf
|
||||
"str r5, [r0, #16] \n" // store r5 into nlr_buf
|
||||
"str r6, [r0, #20] \n" // store r6 into nlr_buf
|
||||
"str r7, [r0, #24] \n" // store r7 into nlr_buf
|
||||
|
||||
#if !defined(__thumb2__)
|
||||
"mov r1, r8 \n"
|
||||
"str r1, [r0, #28] \n" // store r8 into nlr_buf
|
||||
"mov r1, r9 \n"
|
||||
"str r1, [r0, #32] \n" // store r9 into nlr_buf
|
||||
"mov r1, r10 \n"
|
||||
"str r1, [r0, #36] \n" // store r10 into nlr_buf
|
||||
"mov r1, r11 \n"
|
||||
"str r1, [r0, #40] \n" // store r11 into nlr_buf
|
||||
"mov r1, r13 \n"
|
||||
"str r1, [r0, #44] \n" // store r13=sp into nlr_buf
|
||||
"mov r1, lr \n"
|
||||
"str r1, [r0, #8] \n" // store lr into nlr_buf
|
||||
#else
|
||||
"str r8, [r0, #28] \n" // store r8 into nlr_buf
|
||||
"str r9, [r0, #32] \n" // store r9 into nlr_buf
|
||||
"str r10, [r0, #36] \n" // store r10 into nlr_buf
|
||||
"str r11, [r0, #40] \n" // store r11 into nlr_buf
|
||||
"str r13, [r0, #44] \n" // store r13=sp into nlr_buf
|
||||
#if MICROPY_NLR_NUM_REGS == 16
|
||||
"vstr d8, [r0, #48] \n" // store s16-s17 into nlr_buf
|
||||
"vstr d9, [r0, #56] \n" // store s18-s19 into nlr_buf
|
||||
"vstr d10, [r0, #64] \n" // store s20-s21 into nlr_buf
|
||||
#endif
|
||||
"str lr, [r0, #8] \n" // store lr into nlr_buf
|
||||
#endif
|
||||
|
||||
#if !defined(__thumb2__)
|
||||
"ldr r1, nlr_push_tail_var \n"
|
||||
"bx r1 \n" // do the rest in C
|
||||
".align 2 \n"
|
||||
"nlr_push_tail_var: .word nlr_push_tail \n"
|
||||
#else
|
||||
#if defined(__APPLE__) || defined(__MACH__)
|
||||
"b _nlr_push_tail \n" // do the rest in C
|
||||
#else
|
||||
"b nlr_push_tail \n" // do the rest in C
|
||||
#endif
|
||||
#endif
|
||||
);
|
||||
|
||||
#if !defined(__clang__) && defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 8))
|
||||
// Older versions of gcc give an error when naked functions don't return a value
|
||||
// Additionally exclude Clang as it also defines __GNUC__ but doesn't need this statement
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
NORETURN void nlr_jump(void *val) {
|
||||
MP_NLR_JUMP_HEAD(val, top)
|
||||
|
||||
__asm volatile (
|
||||
"mov r0, %0 \n" // r0 points to nlr_buf
|
||||
"ldr r4, [r0, #12] \n" // load r4 from nlr_buf
|
||||
"ldr r5, [r0, #16] \n" // load r5 from nlr_buf
|
||||
"ldr r6, [r0, #20] \n" // load r6 from nlr_buf
|
||||
"ldr r7, [r0, #24] \n" // load r7 from nlr_buf
|
||||
|
||||
#if !defined(__thumb2__)
|
||||
"ldr r1, [r0, #28] \n" // load r8 from nlr_buf
|
||||
"mov r8, r1 \n"
|
||||
"ldr r1, [r0, #32] \n" // load r9 from nlr_buf
|
||||
"mov r9, r1 \n"
|
||||
"ldr r1, [r0, #36] \n" // load r10 from nlr_buf
|
||||
"mov r10, r1 \n"
|
||||
"ldr r1, [r0, #40] \n" // load r11 from nlr_buf
|
||||
"mov r11, r1 \n"
|
||||
"ldr r1, [r0, #44] \n" // load r13=sp from nlr_buf
|
||||
"mov r13, r1 \n"
|
||||
"ldr r1, [r0, #8] \n" // load lr from nlr_buf
|
||||
"mov lr, r1 \n"
|
||||
#else
|
||||
"ldr r8, [r0, #28] \n" // load r8 from nlr_buf
|
||||
"ldr r9, [r0, #32] \n" // load r9 from nlr_buf
|
||||
"ldr r10, [r0, #36] \n" // load r10 from nlr_buf
|
||||
"ldr r11, [r0, #40] \n" // load r11 from nlr_buf
|
||||
"ldr r13, [r0, #44] \n" // load r13=sp from nlr_buf
|
||||
#if MICROPY_NLR_NUM_REGS == 16
|
||||
"vldr d8, [r0, #48] \n" // load s16-s17 from nlr_buf
|
||||
"vldr d9, [r0, #56] \n" // load s18-s19 from nlr_buf
|
||||
"vldr d10, [r0, #64] \n" // load s20-s21 from nlr_buf
|
||||
#endif
|
||||
"ldr lr, [r0, #8] \n" // load lr from nlr_buf
|
||||
#endif
|
||||
"movs r0, #1 \n" // return 1, non-local return
|
||||
"bx lr \n" // return
|
||||
: // output operands
|
||||
: "r" (top) // input operands
|
||||
: // clobbered registers
|
||||
);
|
||||
|
||||
MP_UNREACHABLE
|
||||
}
|
||||
|
||||
#endif // MICROPY_NLR_THUMB
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013-2017 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "py/mpstate.h"
|
||||
|
||||
#if MICROPY_NLR_X64
|
||||
|
||||
#undef nlr_push
|
||||
|
||||
// x86-64 callee-save registers are:
|
||||
// rbx, rbp, rsp, r12, r13, r14, r15
|
||||
|
||||
__attribute__((used)) unsigned int nlr_push_tail(nlr_buf_t *nlr);
|
||||
|
||||
unsigned int nlr_push(nlr_buf_t *nlr) {
|
||||
(void)nlr;
|
||||
|
||||
#if MICROPY_NLR_OS_WINDOWS
|
||||
|
||||
__asm volatile (
|
||||
"movq (%rsp), %rax \n" // load return %rip
|
||||
"movq %rax, 16(%rcx) \n" // store %rip into nlr_buf
|
||||
"movq %rbp, 24(%rcx) \n" // store %rbp into nlr_buf
|
||||
"movq %rsp, 32(%rcx) \n" // store %rsp into nlr_buf
|
||||
"movq %rbx, 40(%rcx) \n" // store %rbx into nlr_buf
|
||||
"movq %r12, 48(%rcx) \n" // store %r12 into nlr_buf
|
||||
"movq %r13, 56(%rcx) \n" // store %r13 into nlr_buf
|
||||
"movq %r14, 64(%rcx) \n" // store %r14 into nlr_buf
|
||||
"movq %r15, 72(%rcx) \n" // store %r15 into nlr_buf
|
||||
"movq %rdi, 80(%rcx) \n" // store %rdr into nlr_buf
|
||||
"movq %rsi, 88(%rcx) \n" // store %rsi into nlr_buf
|
||||
"jmp nlr_push_tail \n" // do the rest in C
|
||||
);
|
||||
|
||||
#else
|
||||
|
||||
__asm volatile (
|
||||
#if defined(__APPLE__) || defined(__MACH__)
|
||||
"pop %rbp \n" // undo function's prelude
|
||||
#endif
|
||||
"movq (%rsp), %rax \n" // load return %rip
|
||||
"movq %rax, 16(%rdi) \n" // store %rip into nlr_buf
|
||||
"movq %rbp, 24(%rdi) \n" // store %rbp into nlr_buf
|
||||
"movq %rsp, 32(%rdi) \n" // store %rsp into nlr_buf
|
||||
"movq %rbx, 40(%rdi) \n" // store %rbx into nlr_buf
|
||||
"movq %r12, 48(%rdi) \n" // store %r12 into nlr_buf
|
||||
"movq %r13, 56(%rdi) \n" // store %r13 into nlr_buf
|
||||
"movq %r14, 64(%rdi) \n" // store %r14 into nlr_buf
|
||||
"movq %r15, 72(%rdi) \n" // store %r15 into nlr_buf
|
||||
#if defined(__APPLE__) || defined(__MACH__)
|
||||
"jmp _nlr_push_tail \n" // do the rest in C
|
||||
#else
|
||||
"jmp nlr_push_tail \n" // do the rest in C
|
||||
#endif
|
||||
);
|
||||
|
||||
#endif
|
||||
|
||||
return 0; // needed to silence compiler warning
|
||||
}
|
||||
|
||||
NORETURN void nlr_jump(void *val) {
|
||||
MP_NLR_JUMP_HEAD(val, top)
|
||||
|
||||
__asm volatile (
|
||||
"movq %0, %%rcx \n" // %rcx points to nlr_buf
|
||||
#if MICROPY_NLR_OS_WINDOWS
|
||||
"movq 88(%%rcx), %%rsi \n" // load saved %rsi
|
||||
"movq 80(%%rcx), %%rdi \n" // load saved %rdr
|
||||
#endif
|
||||
"movq 72(%%rcx), %%r15 \n" // load saved %r15
|
||||
"movq 64(%%rcx), %%r14 \n" // load saved %r14
|
||||
"movq 56(%%rcx), %%r13 \n" // load saved %r13
|
||||
"movq 48(%%rcx), %%r12 \n" // load saved %r12
|
||||
"movq 40(%%rcx), %%rbx \n" // load saved %rbx
|
||||
"movq 32(%%rcx), %%rsp \n" // load saved %rsp
|
||||
"movq 24(%%rcx), %%rbp \n" // load saved %rbp
|
||||
"movq 16(%%rcx), %%rax \n" // load saved %rip
|
||||
"movq %%rax, (%%rsp) \n" // store saved %rip to stack
|
||||
"xorq %%rax, %%rax \n" // clear return register
|
||||
"inc %%al \n" // increase to make 1, non-local return
|
||||
"ret \n" // return
|
||||
: // output operands
|
||||
: "r" (top) // input operands
|
||||
: // clobbered registers
|
||||
);
|
||||
|
||||
MP_UNREACHABLE
|
||||
}
|
||||
|
||||
#endif // MICROPY_NLR_X64
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013-2017 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "py/mpstate.h"
|
||||
|
||||
#if MICROPY_NLR_X86
|
||||
|
||||
#undef nlr_push
|
||||
|
||||
// For reference, x86 callee save regs are:
|
||||
// ebx, esi, edi, ebp, esp, eip
|
||||
|
||||
#if MICROPY_NLR_OS_WINDOWS
|
||||
unsigned int nlr_push_tail(nlr_buf_t *nlr) asm ("nlr_push_tail");
|
||||
#else
|
||||
__attribute__((used)) unsigned int nlr_push_tail(nlr_buf_t *nlr);
|
||||
#endif
|
||||
|
||||
#if !defined(__clang__) && defined(__GNUC__) && __GNUC__ >= 8
|
||||
// Since gcc 8.0 the naked attribute is supported
|
||||
#define USE_NAKED (1)
|
||||
#define UNDO_PRELUDE (0)
|
||||
#elif defined(__ZEPHYR__) || defined(__ANDROID__)
|
||||
// Zephyr and Android use a different calling convention by default
|
||||
#define USE_NAKED (0)
|
||||
#define UNDO_PRELUDE (0)
|
||||
#else
|
||||
#define USE_NAKED (0)
|
||||
#define UNDO_PRELUDE (1)
|
||||
#endif
|
||||
|
||||
#if USE_NAKED
|
||||
__attribute__((naked))
|
||||
#endif
|
||||
unsigned int nlr_push(nlr_buf_t *nlr) {
|
||||
(void)nlr;
|
||||
|
||||
__asm volatile (
|
||||
#if UNDO_PRELUDE
|
||||
"pop %ebp \n" // undo function's prelude
|
||||
#endif
|
||||
"mov 4(%esp), %edx \n" // load nlr_buf
|
||||
"mov (%esp), %eax \n" // load return %eip
|
||||
"mov %eax, 8(%edx) \n" // store %eip into nlr_buf
|
||||
"mov %ebp, 12(%edx) \n" // store %ebp into nlr_buf
|
||||
"mov %esp, 16(%edx) \n" // store %esp into nlr_buf
|
||||
"mov %ebx, 20(%edx) \n" // store %ebx into nlr_buf
|
||||
"mov %edi, 24(%edx) \n" // store %edi into nlr_buf
|
||||
"mov %esi, 28(%edx) \n" // store %esi into nlr_buf
|
||||
"jmp nlr_push_tail \n" // do the rest in C
|
||||
);
|
||||
|
||||
#if !USE_NAKED
|
||||
return 0; // needed to silence compiler warning
|
||||
#endif
|
||||
}
|
||||
|
||||
NORETURN void nlr_jump(void *val) {
|
||||
MP_NLR_JUMP_HEAD(val, top)
|
||||
|
||||
__asm volatile (
|
||||
"mov %0, %%edx \n" // %edx points to nlr_buf
|
||||
"mov 28(%%edx), %%esi \n" // load saved %esi
|
||||
"mov 24(%%edx), %%edi \n" // load saved %edi
|
||||
"mov 20(%%edx), %%ebx \n" // load saved %ebx
|
||||
"mov 16(%%edx), %%esp \n" // load saved %esp
|
||||
"mov 12(%%edx), %%ebp \n" // load saved %ebp
|
||||
"mov 8(%%edx), %%eax \n" // load saved %eip
|
||||
"mov %%eax, (%%esp) \n" // store saved %eip to stack
|
||||
"xor %%eax, %%eax \n" // clear return register
|
||||
"inc %%al \n" // increase to make 1, non-local return
|
||||
"ret \n" // return
|
||||
: // output operands
|
||||
: "r" (top) // input operands
|
||||
: // clobbered registers
|
||||
);
|
||||
|
||||
MP_UNREACHABLE
|
||||
}
|
||||
|
||||
#endif // MICROPY_NLR_X86
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2014-2017 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "py/mpstate.h"
|
||||
|
||||
#if MICROPY_NLR_XTENSA
|
||||
|
||||
#undef nlr_push
|
||||
|
||||
// Xtensa calling conventions:
|
||||
// a0 = return address
|
||||
// a1 = stack pointer
|
||||
// a2 = first arg, return value
|
||||
// a3-a7 = rest of args
|
||||
|
||||
unsigned int nlr_push(nlr_buf_t *nlr) {
|
||||
|
||||
__asm volatile (
|
||||
"s32i.n a0, a2, 8 \n" // save regs...
|
||||
"s32i.n a1, a2, 12 \n"
|
||||
"s32i.n a8, a2, 16 \n"
|
||||
"s32i.n a9, a2, 20 \n"
|
||||
"s32i.n a10, a2, 24 \n"
|
||||
"s32i.n a11, a2, 28 \n"
|
||||
"s32i.n a12, a2, 32 \n"
|
||||
"s32i.n a13, a2, 36 \n"
|
||||
"s32i.n a14, a2, 40 \n"
|
||||
"s32i.n a15, a2, 44 \n"
|
||||
"j nlr_push_tail \n" // do the rest in C
|
||||
);
|
||||
|
||||
return 0; // needed to silence compiler warning
|
||||
}
|
||||
|
||||
NORETURN void nlr_jump(void *val) {
|
||||
MP_NLR_JUMP_HEAD(val, top)
|
||||
|
||||
__asm volatile (
|
||||
"mov.n a2, %0 \n" // a2 points to nlr_buf
|
||||
"l32i.n a0, a2, 8 \n" // restore regs...
|
||||
"l32i.n a1, a2, 12 \n"
|
||||
"l32i.n a8, a2, 16 \n"
|
||||
"l32i.n a9, a2, 20 \n"
|
||||
"l32i.n a10, a2, 24 \n"
|
||||
"l32i.n a11, a2, 28 \n"
|
||||
"l32i.n a12, a2, 32 \n"
|
||||
"l32i.n a13, a2, 36 \n"
|
||||
"l32i.n a14, a2, 40 \n"
|
||||
"l32i.n a15, a2, 44 \n"
|
||||
"movi.n a2, 1 \n" // return 1, non-local return
|
||||
"ret.n \n" // return
|
||||
: // output operands
|
||||
: "r" (top) // input operands
|
||||
: // clobbered registers
|
||||
);
|
||||
|
||||
MP_UNREACHABLE
|
||||
}
|
||||
|
||||
#endif // MICROPY_NLR_XTENSA
|
||||
@@ -0,0 +1,604 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013, 2014 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "py/obj.h"
|
||||
#include "py/objtype.h"
|
||||
#include "py/objint.h"
|
||||
#include "py/objstr.h"
|
||||
#include "py/runtime.h"
|
||||
#include "py/stackctrl.h"
|
||||
#include "py/stream.h" // for mp_obj_print
|
||||
|
||||
const mp_obj_type_t *mp_obj_get_type(mp_const_obj_t o_in) {
|
||||
#if MICROPY_OBJ_IMMEDIATE_OBJS && MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_A
|
||||
|
||||
if (mp_obj_is_obj(o_in)) {
|
||||
const mp_obj_base_t *o = MP_OBJ_TO_PTR(o_in);
|
||||
return o->type;
|
||||
} else {
|
||||
static const mp_obj_type_t *const types[] = {
|
||||
NULL, &mp_type_int, &mp_type_str, &mp_type_int,
|
||||
NULL, &mp_type_int, &mp_type_NoneType, &mp_type_int,
|
||||
NULL, &mp_type_int, &mp_type_str, &mp_type_int,
|
||||
NULL, &mp_type_int, &mp_type_bool, &mp_type_int,
|
||||
};
|
||||
return types[(uintptr_t)o_in & 0xf];
|
||||
}
|
||||
|
||||
#elif MICROPY_OBJ_IMMEDIATE_OBJS && MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_C
|
||||
|
||||
if (mp_obj_is_small_int(o_in)) {
|
||||
return &mp_type_int;
|
||||
} else if (mp_obj_is_obj(o_in)) {
|
||||
const mp_obj_base_t *o = MP_OBJ_TO_PTR(o_in);
|
||||
return o->type;
|
||||
#if MICROPY_PY_BUILTINS_FLOAT
|
||||
} else if ((((mp_uint_t)(o_in)) & 0xff800007) != 0x00000006) {
|
||||
return &mp_type_float;
|
||||
#endif
|
||||
} else {
|
||||
static const mp_obj_type_t *const types[] = {
|
||||
&mp_type_str, &mp_type_NoneType, &mp_type_str, &mp_type_bool,
|
||||
};
|
||||
return types[((uintptr_t)o_in >> 3) & 3];
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
if (mp_obj_is_small_int(o_in)) {
|
||||
return &mp_type_int;
|
||||
} else if (mp_obj_is_qstr(o_in)) {
|
||||
return &mp_type_str;
|
||||
#if MICROPY_PY_BUILTINS_FLOAT && ( \
|
||||
MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_C || MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D)
|
||||
} else if (mp_obj_is_float(o_in)) {
|
||||
return &mp_type_float;
|
||||
#endif
|
||||
#if MICROPY_OBJ_IMMEDIATE_OBJS
|
||||
} else if (mp_obj_is_immediate_obj(o_in)) {
|
||||
static const mp_obj_type_t *const types[2] = {&mp_type_NoneType, &mp_type_bool};
|
||||
return types[MP_OBJ_IMMEDIATE_OBJ_VALUE(o_in) & 1];
|
||||
#endif
|
||||
} else {
|
||||
const mp_obj_base_t *o = MP_OBJ_TO_PTR(o_in);
|
||||
return o->type;
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
const char *mp_obj_get_type_str(mp_const_obj_t o_in) {
|
||||
return qstr_str(mp_obj_get_type(o_in)->name);
|
||||
}
|
||||
|
||||
void mp_obj_print_helper(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) {
|
||||
// There can be data structures nested too deep, or just recursive
|
||||
MP_STACK_CHECK();
|
||||
#ifndef NDEBUG
|
||||
if (o_in == MP_OBJ_NULL) {
|
||||
mp_print_str(print, "(nil)");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
const mp_obj_type_t *type = mp_obj_get_type(o_in);
|
||||
if (type->print != NULL) {
|
||||
type->print((mp_print_t *)print, o_in, kind);
|
||||
} else {
|
||||
mp_printf(print, "<%q>", type->name);
|
||||
}
|
||||
}
|
||||
|
||||
void mp_obj_print(mp_obj_t o_in, mp_print_kind_t kind) {
|
||||
mp_obj_print_helper(MP_PYTHON_PRINTER, o_in, kind);
|
||||
}
|
||||
|
||||
// helper function to print an exception with traceback
|
||||
void mp_obj_print_exception(const mp_print_t *print, mp_obj_t exc) {
|
||||
if (mp_obj_is_exception_instance(exc)) {
|
||||
size_t n, *values;
|
||||
mp_obj_exception_get_traceback(exc, &n, &values);
|
||||
if (n > 0) {
|
||||
assert(n % 3 == 0);
|
||||
mp_print_str(print, "Traceback (most recent call last):\n");
|
||||
for (int i = n - 3; i >= 0; i -= 3) {
|
||||
#if MICROPY_ENABLE_SOURCE_LINE
|
||||
mp_printf(print, " File \"%q\", line %d", values[i], (int)values[i + 1]);
|
||||
#else
|
||||
mp_printf(print, " File \"%q\"", values[i]);
|
||||
#endif
|
||||
// the block name can be NULL if it's unknown
|
||||
qstr block = values[i + 2];
|
||||
if (block == MP_QSTRnull) {
|
||||
mp_print_str(print, "\n");
|
||||
} else {
|
||||
mp_printf(print, ", in %q\n", block);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
mp_obj_print_helper(print, exc, PRINT_EXC);
|
||||
mp_print_str(print, "\n");
|
||||
}
|
||||
|
||||
bool mp_obj_is_true(mp_obj_t arg) {
|
||||
if (arg == mp_const_false) {
|
||||
return 0;
|
||||
} else if (arg == mp_const_true) {
|
||||
return 1;
|
||||
} else if (arg == mp_const_none) {
|
||||
return 0;
|
||||
} else if (mp_obj_is_small_int(arg)) {
|
||||
if (arg == MP_OBJ_NEW_SMALL_INT(0)) {
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
const mp_obj_type_t *type = mp_obj_get_type(arg);
|
||||
if (type->unary_op != NULL) {
|
||||
mp_obj_t result = type->unary_op(MP_UNARY_OP_BOOL, arg);
|
||||
if (result != MP_OBJ_NULL) {
|
||||
return result == mp_const_true;
|
||||
}
|
||||
}
|
||||
|
||||
mp_obj_t len = mp_obj_len_maybe(arg);
|
||||
if (len != MP_OBJ_NULL) {
|
||||
// obj has a length, truth determined if len != 0
|
||||
return len != MP_OBJ_NEW_SMALL_INT(0);
|
||||
} else {
|
||||
// any other obj is true per Python semantics
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool mp_obj_is_callable(mp_obj_t o_in) {
|
||||
const mp_call_fun_t call = mp_obj_get_type(o_in)->call;
|
||||
if (call != mp_obj_instance_call) {
|
||||
return call != NULL;
|
||||
}
|
||||
return mp_obj_instance_is_callable(o_in);
|
||||
}
|
||||
|
||||
// This function implements the '==' and '!=' operators.
|
||||
//
|
||||
// From the Python language reference:
|
||||
// (https://docs.python.org/3/reference/expressions.html#not-in)
|
||||
// "The objects need not have the same type. If both are numbers, they are converted
|
||||
// to a common type. Otherwise, the == and != operators always consider objects of
|
||||
// different types to be unequal."
|
||||
//
|
||||
// This means that False==0 and True==1 are true expressions.
|
||||
//
|
||||
// Furthermore, from the v3.4.2 code for object.c: "Practical amendments: If rich
|
||||
// comparison returns NotImplemented, == and != are decided by comparing the object
|
||||
// pointer."
|
||||
mp_obj_t mp_obj_equal_not_equal(mp_binary_op_t op, mp_obj_t o1, mp_obj_t o2) {
|
||||
mp_obj_t local_true = (op == MP_BINARY_OP_NOT_EQUAL) ? mp_const_false : mp_const_true;
|
||||
mp_obj_t local_false = (op == MP_BINARY_OP_NOT_EQUAL) ? mp_const_true : mp_const_false;
|
||||
int pass_number = 0;
|
||||
|
||||
// Shortcut for very common cases
|
||||
if (o1 == o2 &&
|
||||
(mp_obj_is_small_int(o1) || !(mp_obj_get_type(o1)->flags & MP_TYPE_FLAG_EQ_NOT_REFLEXIVE))) {
|
||||
return local_true;
|
||||
}
|
||||
|
||||
// fast path for strings
|
||||
if (mp_obj_is_str(o1)) {
|
||||
if (mp_obj_is_str(o2)) {
|
||||
// both strings, use special function
|
||||
return mp_obj_str_equal(o1, o2) ? local_true : local_false;
|
||||
#if MICROPY_PY_STR_BYTES_CMP_WARN
|
||||
} else if (mp_obj_is_type(o2, &mp_type_bytes)) {
|
||||
str_bytes_cmp:
|
||||
mp_warning(MP_WARN_CAT(BytesWarning), "Comparison between bytes and str");
|
||||
return local_false;
|
||||
#endif
|
||||
} else {
|
||||
goto skip_one_pass;
|
||||
}
|
||||
#if MICROPY_PY_STR_BYTES_CMP_WARN
|
||||
} else if (mp_obj_is_str(o2) && mp_obj_is_type(o1, &mp_type_bytes)) {
|
||||
// o1 is not a string (else caught above), so the objects are not equal
|
||||
goto str_bytes_cmp;
|
||||
#endif
|
||||
}
|
||||
|
||||
// fast path for small ints
|
||||
if (mp_obj_is_small_int(o1)) {
|
||||
if (mp_obj_is_small_int(o2)) {
|
||||
// both SMALL_INT, and not equal if we get here
|
||||
return local_false;
|
||||
} else {
|
||||
goto skip_one_pass;
|
||||
}
|
||||
}
|
||||
|
||||
// generic type, call binary_op(MP_BINARY_OP_EQUAL)
|
||||
while (pass_number < 2) {
|
||||
const mp_obj_type_t *type = mp_obj_get_type(o1);
|
||||
// If a full equality test is not needed and the other object is a different
|
||||
// type then we don't need to bother trying the comparison.
|
||||
if (type->binary_op != NULL &&
|
||||
((type->flags & MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE) || mp_obj_get_type(o2) == type)) {
|
||||
// CPython is asymmetric: it will try __eq__ if there's no __ne__ but not the
|
||||
// other way around. If the class doesn't need a full test we can skip __ne__.
|
||||
if (op == MP_BINARY_OP_NOT_EQUAL && (type->flags & MP_TYPE_FLAG_EQ_HAS_NEQ_TEST)) {
|
||||
mp_obj_t r = type->binary_op(MP_BINARY_OP_NOT_EQUAL, o1, o2);
|
||||
if (r != MP_OBJ_NULL) {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
// Try calling __eq__.
|
||||
mp_obj_t r = type->binary_op(MP_BINARY_OP_EQUAL, o1, o2);
|
||||
if (r != MP_OBJ_NULL) {
|
||||
if (op == MP_BINARY_OP_EQUAL) {
|
||||
return r;
|
||||
} else {
|
||||
return mp_obj_is_true(r) ? local_true : local_false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
skip_one_pass:
|
||||
// Try the other way around if none of the above worked
|
||||
++pass_number;
|
||||
mp_obj_t temp = o1;
|
||||
o1 = o2;
|
||||
o2 = temp;
|
||||
}
|
||||
|
||||
// equality not implemented, so fall back to pointer conparison
|
||||
return (o1 == o2) ? local_true : local_false;
|
||||
}
|
||||
|
||||
bool mp_obj_equal(mp_obj_t o1, mp_obj_t o2) {
|
||||
return mp_obj_is_true(mp_obj_equal_not_equal(MP_BINARY_OP_EQUAL, o1, o2));
|
||||
}
|
||||
|
||||
mp_int_t mp_obj_get_int(mp_const_obj_t arg) {
|
||||
// This function essentially performs implicit type conversion to int
|
||||
// Note that Python does NOT provide implicit type conversion from
|
||||
// float to int in the core expression language, try some_list[1.0].
|
||||
if (arg == mp_const_false) {
|
||||
return 0;
|
||||
} else if (arg == mp_const_true) {
|
||||
return 1;
|
||||
} else if (mp_obj_is_small_int(arg)) {
|
||||
return MP_OBJ_SMALL_INT_VALUE(arg);
|
||||
} else if (mp_obj_is_type(arg, &mp_type_int)) {
|
||||
return mp_obj_int_get_checked(arg);
|
||||
} else {
|
||||
mp_obj_t res = mp_unary_op(MP_UNARY_OP_INT, (mp_obj_t)arg);
|
||||
return mp_obj_int_get_checked(res);
|
||||
}
|
||||
}
|
||||
|
||||
mp_int_t mp_obj_get_int_truncated(mp_const_obj_t arg) {
|
||||
if (mp_obj_is_int(arg)) {
|
||||
return mp_obj_int_get_truncated(arg);
|
||||
} else {
|
||||
return mp_obj_get_int(arg);
|
||||
}
|
||||
}
|
||||
|
||||
// returns false if arg is not of integral type
|
||||
// returns true and sets *value if it is of integral type
|
||||
// can throw OverflowError if arg is of integral type, but doesn't fit in a mp_int_t
|
||||
bool mp_obj_get_int_maybe(mp_const_obj_t arg, mp_int_t *value) {
|
||||
if (arg == mp_const_false) {
|
||||
*value = 0;
|
||||
} else if (arg == mp_const_true) {
|
||||
*value = 1;
|
||||
} else if (mp_obj_is_small_int(arg)) {
|
||||
*value = MP_OBJ_SMALL_INT_VALUE(arg);
|
||||
} else if (mp_obj_is_type(arg, &mp_type_int)) {
|
||||
*value = mp_obj_int_get_checked(arg);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#if MICROPY_PY_BUILTINS_FLOAT
|
||||
bool mp_obj_get_float_maybe(mp_obj_t arg, mp_float_t *value) {
|
||||
mp_float_t val;
|
||||
|
||||
if (arg == mp_const_false) {
|
||||
val = 0;
|
||||
} else if (arg == mp_const_true) {
|
||||
val = 1;
|
||||
} else if (mp_obj_is_small_int(arg)) {
|
||||
val = (mp_float_t)MP_OBJ_SMALL_INT_VALUE(arg);
|
||||
#if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE
|
||||
} else if (mp_obj_is_type(arg, &mp_type_int)) {
|
||||
val = mp_obj_int_as_float_impl(arg);
|
||||
#endif
|
||||
} else if (mp_obj_is_float(arg)) {
|
||||
val = mp_obj_float_get(arg);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
*value = val;
|
||||
return true;
|
||||
}
|
||||
|
||||
mp_float_t mp_obj_get_float(mp_obj_t arg) {
|
||||
mp_float_t val;
|
||||
|
||||
if (!mp_obj_get_float_maybe(arg, &val)) {
|
||||
#if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("can't convert to float"));
|
||||
#else
|
||||
mp_raise_msg_varg(&mp_type_TypeError,
|
||||
MP_ERROR_TEXT("can't convert %s to float"), mp_obj_get_type_str(arg));
|
||||
#endif
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
#if MICROPY_PY_BUILTINS_COMPLEX
|
||||
bool mp_obj_get_complex_maybe(mp_obj_t arg, mp_float_t *real, mp_float_t *imag) {
|
||||
if (arg == mp_const_false) {
|
||||
*real = 0;
|
||||
*imag = 0;
|
||||
} else if (arg == mp_const_true) {
|
||||
*real = 1;
|
||||
*imag = 0;
|
||||
} else if (mp_obj_is_small_int(arg)) {
|
||||
*real = (mp_float_t)MP_OBJ_SMALL_INT_VALUE(arg);
|
||||
*imag = 0;
|
||||
#if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE
|
||||
} else if (mp_obj_is_type(arg, &mp_type_int)) {
|
||||
*real = mp_obj_int_as_float_impl(arg);
|
||||
*imag = 0;
|
||||
#endif
|
||||
} else if (mp_obj_is_float(arg)) {
|
||||
*real = mp_obj_float_get(arg);
|
||||
*imag = 0;
|
||||
} else if (mp_obj_is_type(arg, &mp_type_complex)) {
|
||||
mp_obj_complex_get(arg, real, imag);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void mp_obj_get_complex(mp_obj_t arg, mp_float_t *real, mp_float_t *imag) {
|
||||
if (!mp_obj_get_complex_maybe(arg, real, imag)) {
|
||||
#if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("can't convert to complex"));
|
||||
#else
|
||||
mp_raise_msg_varg(&mp_type_TypeError,
|
||||
MP_ERROR_TEXT("can't convert %s to complex"), mp_obj_get_type_str(arg));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// note: returned value in *items may point to the interior of a GC block
|
||||
void mp_obj_get_array(mp_obj_t o, size_t *len, mp_obj_t **items) {
|
||||
if (mp_obj_is_type(o, &mp_type_tuple)) {
|
||||
mp_obj_tuple_get(o, len, items);
|
||||
} else if (mp_obj_is_type(o, &mp_type_list)) {
|
||||
mp_obj_list_get(o, len, items);
|
||||
} else {
|
||||
#if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("expected tuple/list"));
|
||||
#else
|
||||
mp_raise_msg_varg(&mp_type_TypeError,
|
||||
MP_ERROR_TEXT("object '%s' isn't a tuple or list"), mp_obj_get_type_str(o));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// note: returned value in *items may point to the interior of a GC block
|
||||
void mp_obj_get_array_fixed_n(mp_obj_t o, size_t len, mp_obj_t **items) {
|
||||
size_t seq_len;
|
||||
mp_obj_get_array(o, &seq_len, items);
|
||||
if (seq_len != len) {
|
||||
#if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("tuple/list has wrong length"));
|
||||
#else
|
||||
mp_raise_msg_varg(&mp_type_ValueError,
|
||||
MP_ERROR_TEXT("requested length %d but object has length %d"), (int)len, (int)seq_len);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// is_slice determines whether the index is a slice index
|
||||
size_t mp_get_index(const mp_obj_type_t *type, size_t len, mp_obj_t index, bool is_slice) {
|
||||
mp_int_t i;
|
||||
if (mp_obj_is_small_int(index)) {
|
||||
i = MP_OBJ_SMALL_INT_VALUE(index);
|
||||
} else if (!mp_obj_get_int_maybe(index, &i)) {
|
||||
#if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("indices must be integers"));
|
||||
#else
|
||||
mp_raise_msg_varg(&mp_type_TypeError,
|
||||
MP_ERROR_TEXT("%q indices must be integers, not %s"),
|
||||
type->name, mp_obj_get_type_str(index));
|
||||
#endif
|
||||
}
|
||||
|
||||
if (i < 0) {
|
||||
i += len;
|
||||
}
|
||||
if (is_slice) {
|
||||
if (i < 0) {
|
||||
i = 0;
|
||||
} else if ((mp_uint_t)i > len) {
|
||||
i = len;
|
||||
}
|
||||
} else {
|
||||
if (i < 0 || (mp_uint_t)i >= len) {
|
||||
#if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE
|
||||
mp_raise_msg(&mp_type_IndexError, MP_ERROR_TEXT("index out of range"));
|
||||
#else
|
||||
mp_raise_msg_varg(&mp_type_IndexError, MP_ERROR_TEXT("%q index out of range"), type->name);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// By this point 0 <= i <= len and so fits in a size_t
|
||||
return (size_t)i;
|
||||
}
|
||||
|
||||
mp_obj_t mp_obj_id(mp_obj_t o_in) {
|
||||
mp_int_t id = (mp_int_t)o_in;
|
||||
if (!mp_obj_is_obj(o_in)) {
|
||||
return mp_obj_new_int(id);
|
||||
} else if (id >= 0) {
|
||||
// Many OSes and CPUs have affinity for putting "user" memories
|
||||
// into low half of address space, and "system" into upper half.
|
||||
// We're going to take advantage of that and return small int
|
||||
// (signed) for such "user" addresses.
|
||||
return MP_OBJ_NEW_SMALL_INT(id);
|
||||
} else {
|
||||
// If that didn't work, well, let's return long int, just as
|
||||
// a (big) positive value, so it will never clash with the range
|
||||
// of small int returned in previous case.
|
||||
return mp_obj_new_int_from_uint((mp_uint_t)id);
|
||||
}
|
||||
}
|
||||
|
||||
// will raise a TypeError if object has no length
|
||||
mp_obj_t mp_obj_len(mp_obj_t o_in) {
|
||||
mp_obj_t len = mp_obj_len_maybe(o_in);
|
||||
if (len == MP_OBJ_NULL) {
|
||||
#if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("object has no len"));
|
||||
#else
|
||||
mp_raise_msg_varg(&mp_type_TypeError,
|
||||
MP_ERROR_TEXT("object of type '%s' has no len()"), mp_obj_get_type_str(o_in));
|
||||
#endif
|
||||
} else {
|
||||
return len;
|
||||
}
|
||||
}
|
||||
|
||||
// may return MP_OBJ_NULL
|
||||
mp_obj_t mp_obj_len_maybe(mp_obj_t o_in) {
|
||||
if (
|
||||
#if !MICROPY_PY_BUILTINS_STR_UNICODE
|
||||
// It's simple - unicode is slow, non-unicode is fast
|
||||
mp_obj_is_str(o_in) ||
|
||||
#endif
|
||||
mp_obj_is_type(o_in, &mp_type_bytes)) {
|
||||
GET_STR_LEN(o_in, l);
|
||||
return MP_OBJ_NEW_SMALL_INT(l);
|
||||
} else {
|
||||
const mp_obj_type_t *type = mp_obj_get_type(o_in);
|
||||
if (type->unary_op != NULL) {
|
||||
return type->unary_op(MP_UNARY_OP_LEN, o_in);
|
||||
} else {
|
||||
return MP_OBJ_NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mp_obj_t mp_obj_subscr(mp_obj_t base, mp_obj_t index, mp_obj_t value) {
|
||||
const mp_obj_type_t *type = mp_obj_get_type(base);
|
||||
if (type->subscr != NULL) {
|
||||
mp_obj_t ret = type->subscr(base, index, value);
|
||||
if (ret != MP_OBJ_NULL) {
|
||||
return ret;
|
||||
}
|
||||
// TODO: call base classes here?
|
||||
}
|
||||
if (value == MP_OBJ_NULL) {
|
||||
#if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("object doesn't support item deletion"));
|
||||
#else
|
||||
mp_raise_msg_varg(&mp_type_TypeError,
|
||||
MP_ERROR_TEXT("'%s' object doesn't support item deletion"), mp_obj_get_type_str(base));
|
||||
#endif
|
||||
} else if (value == MP_OBJ_SENTINEL) {
|
||||
#if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("object isn't subscriptable"));
|
||||
#else
|
||||
mp_raise_msg_varg(&mp_type_TypeError,
|
||||
MP_ERROR_TEXT("'%s' object isn't subscriptable"), mp_obj_get_type_str(base));
|
||||
#endif
|
||||
} else {
|
||||
#if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("object doesn't support item assignment"));
|
||||
#else
|
||||
mp_raise_msg_varg(&mp_type_TypeError,
|
||||
MP_ERROR_TEXT("'%s' object doesn't support item assignment"), mp_obj_get_type_str(base));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// Return input argument. Useful as .getiter for objects which are
|
||||
// their own iterators, etc.
|
||||
mp_obj_t mp_identity(mp_obj_t self) {
|
||||
return self;
|
||||
}
|
||||
MP_DEFINE_CONST_FUN_OBJ_1(mp_identity_obj, mp_identity);
|
||||
|
||||
mp_obj_t mp_identity_getiter(mp_obj_t self, mp_obj_iter_buf_t *iter_buf) {
|
||||
(void)iter_buf;
|
||||
return self;
|
||||
}
|
||||
|
||||
bool mp_get_buffer(mp_obj_t obj, mp_buffer_info_t *bufinfo, mp_uint_t flags) {
|
||||
const mp_obj_type_t *type = mp_obj_get_type(obj);
|
||||
if (type->buffer_p.get_buffer == NULL) {
|
||||
return false;
|
||||
}
|
||||
int ret = type->buffer_p.get_buffer(obj, bufinfo, flags);
|
||||
if (ret != 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void mp_get_buffer_raise(mp_obj_t obj, mp_buffer_info_t *bufinfo, mp_uint_t flags) {
|
||||
if (!mp_get_buffer(obj, bufinfo, flags)) {
|
||||
mp_raise_TypeError(MP_ERROR_TEXT("object with buffer protocol required"));
|
||||
}
|
||||
}
|
||||
|
||||
mp_obj_t mp_generic_unary_op(mp_unary_op_t op, mp_obj_t o_in) {
|
||||
switch (op) {
|
||||
case MP_UNARY_OP_HASH:
|
||||
return MP_OBJ_NEW_SMALL_INT((mp_uint_t)o_in);
|
||||
default:
|
||||
return MP_OBJ_NULL; // op not supported
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,660 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013, 2014 Damien P. George
|
||||
* Copyright (c) 2014 Paul Sokolovsky
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "py/runtime.h"
|
||||
#include "py/binary.h"
|
||||
#include "py/objstr.h"
|
||||
#include "py/objarray.h"
|
||||
|
||||
#if MICROPY_PY_ARRAY || MICROPY_PY_BUILTINS_BYTEARRAY || MICROPY_PY_BUILTINS_MEMORYVIEW
|
||||
|
||||
// About memoryview object: We want to reuse as much code as possible from
|
||||
// array, and keep the memoryview object 4 words in size so it fits in 1 GC
|
||||
// block. Also, memoryview must keep a pointer to the base of the buffer so
|
||||
// that the buffer is not GC'd if the original parent object is no longer
|
||||
// around (we are assuming that all memoryview'able objects return a pointer
|
||||
// which points to the start of a GC chunk). Given the above constraints we
|
||||
// do the following:
|
||||
// - typecode high bit is set if the buffer is read-write (else read-only)
|
||||
// - free is the offset in elements to the first item in the memoryview
|
||||
// - len is the length in elements
|
||||
// - items points to the start of the original buffer
|
||||
// Note that we don't handle the case where the original buffer might change
|
||||
// size due to a resize of the original parent object.
|
||||
|
||||
#if MICROPY_PY_BUILTINS_MEMORYVIEW
|
||||
#define TYPECODE_MASK (0x7f)
|
||||
#define memview_offset free
|
||||
#else
|
||||
// make (& TYPECODE_MASK) a null operation if memorview not enabled
|
||||
#define TYPECODE_MASK (~(size_t)0)
|
||||
// memview_offset should not be accessed if memoryview is not enabled,
|
||||
// so not defined to catch errors
|
||||
#endif
|
||||
|
||||
STATIC mp_obj_t array_iterator_new(mp_obj_t array_in, mp_obj_iter_buf_t *iter_buf);
|
||||
STATIC mp_obj_t array_append(mp_obj_t self_in, mp_obj_t arg);
|
||||
STATIC mp_obj_t array_extend(mp_obj_t self_in, mp_obj_t arg_in);
|
||||
STATIC mp_int_t array_get_buffer(mp_obj_t o_in, mp_buffer_info_t *bufinfo, mp_uint_t flags);
|
||||
|
||||
/******************************************************************************/
|
||||
// array
|
||||
|
||||
#if MICROPY_PY_BUILTINS_BYTEARRAY || MICROPY_PY_ARRAY
|
||||
STATIC void array_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) {
|
||||
(void)kind;
|
||||
mp_obj_array_t *o = MP_OBJ_TO_PTR(o_in);
|
||||
if (o->typecode == BYTEARRAY_TYPECODE) {
|
||||
mp_print_str(print, "bytearray(b");
|
||||
mp_str_print_quoted(print, o->items, o->len, true);
|
||||
} else {
|
||||
mp_printf(print, "array('%c'", o->typecode);
|
||||
if (o->len > 0) {
|
||||
mp_print_str(print, ", [");
|
||||
for (size_t i = 0; i < o->len; i++) {
|
||||
if (i > 0) {
|
||||
mp_print_str(print, ", ");
|
||||
}
|
||||
mp_obj_print_helper(print, mp_binary_get_val_array(o->typecode, o->items, i), PRINT_REPR);
|
||||
}
|
||||
mp_print_str(print, "]");
|
||||
}
|
||||
}
|
||||
mp_print_str(print, ")");
|
||||
}
|
||||
#endif
|
||||
|
||||
#if MICROPY_PY_BUILTINS_BYTEARRAY || MICROPY_PY_ARRAY
|
||||
STATIC mp_obj_array_t *array_new(char typecode, size_t n) {
|
||||
int typecode_size = mp_binary_get_size('@', typecode, NULL);
|
||||
mp_obj_array_t *o = m_new_obj(mp_obj_array_t);
|
||||
#if MICROPY_PY_BUILTINS_BYTEARRAY && MICROPY_PY_ARRAY
|
||||
o->base.type = (typecode == BYTEARRAY_TYPECODE) ? &mp_type_bytearray : &mp_type_array;
|
||||
#elif MICROPY_PY_BUILTINS_BYTEARRAY
|
||||
o->base.type = &mp_type_bytearray;
|
||||
#else
|
||||
o->base.type = &mp_type_array;
|
||||
#endif
|
||||
o->typecode = typecode;
|
||||
o->free = 0;
|
||||
o->len = n;
|
||||
o->items = m_new(byte, typecode_size * o->len);
|
||||
return o;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if MICROPY_PY_BUILTINS_BYTEARRAY || MICROPY_PY_ARRAY
|
||||
STATIC mp_obj_t array_construct(char typecode, mp_obj_t initializer) {
|
||||
// bytearrays can be raw-initialised from anything with the buffer protocol
|
||||
// other arrays can only be raw-initialised from bytes and bytearray objects
|
||||
mp_buffer_info_t bufinfo;
|
||||
if (((MICROPY_PY_BUILTINS_BYTEARRAY
|
||||
&& typecode == BYTEARRAY_TYPECODE)
|
||||
|| (MICROPY_PY_ARRAY
|
||||
&& (mp_obj_is_type(initializer, &mp_type_bytes)
|
||||
|| (MICROPY_PY_BUILTINS_BYTEARRAY && mp_obj_is_type(initializer, &mp_type_bytearray)))))
|
||||
&& mp_get_buffer(initializer, &bufinfo, MP_BUFFER_READ)) {
|
||||
// construct array from raw bytes
|
||||
// we round-down the len to make it a multiple of sz (CPython raises error)
|
||||
size_t sz = mp_binary_get_size('@', typecode, NULL);
|
||||
size_t len = bufinfo.len / sz;
|
||||
mp_obj_array_t *o = array_new(typecode, len);
|
||||
memcpy(o->items, bufinfo.buf, len * sz);
|
||||
return MP_OBJ_FROM_PTR(o);
|
||||
}
|
||||
|
||||
size_t len;
|
||||
// Try to create array of exact len if initializer len is known
|
||||
mp_obj_t len_in = mp_obj_len_maybe(initializer);
|
||||
if (len_in == MP_OBJ_NULL) {
|
||||
len = 0;
|
||||
} else {
|
||||
len = MP_OBJ_SMALL_INT_VALUE(len_in);
|
||||
}
|
||||
|
||||
mp_obj_array_t *array = array_new(typecode, len);
|
||||
|
||||
mp_obj_t iterable = mp_getiter(initializer, NULL);
|
||||
mp_obj_t item;
|
||||
size_t i = 0;
|
||||
while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) {
|
||||
if (len == 0) {
|
||||
array_append(MP_OBJ_FROM_PTR(array), item);
|
||||
} else {
|
||||
mp_binary_set_val_array(typecode, array->items, i++, item);
|
||||
}
|
||||
}
|
||||
|
||||
return MP_OBJ_FROM_PTR(array);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if MICROPY_PY_ARRAY
|
||||
STATIC mp_obj_t array_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args) {
|
||||
(void)type_in;
|
||||
mp_arg_check_num(n_args, n_kw, 1, 2, false);
|
||||
|
||||
// get typecode
|
||||
const char *typecode = mp_obj_str_get_str(args[0]);
|
||||
|
||||
if (n_args == 1) {
|
||||
// 1 arg: make an empty array
|
||||
return MP_OBJ_FROM_PTR(array_new(*typecode, 0));
|
||||
} else {
|
||||
// 2 args: construct the array from the given object
|
||||
return array_construct(*typecode, args[1]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if MICROPY_PY_BUILTINS_BYTEARRAY
|
||||
STATIC mp_obj_t bytearray_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args) {
|
||||
(void)type_in;
|
||||
// Can take 2nd/3rd arg if constructs from str
|
||||
mp_arg_check_num(n_args, n_kw, 0, 3, false);
|
||||
|
||||
if (n_args == 0) {
|
||||
// no args: construct an empty bytearray
|
||||
return MP_OBJ_FROM_PTR(array_new(BYTEARRAY_TYPECODE, 0));
|
||||
} else if (mp_obj_is_int(args[0])) {
|
||||
// 1 arg, an integer: construct a blank bytearray of that length
|
||||
mp_uint_t len = mp_obj_get_int(args[0]);
|
||||
mp_obj_array_t *o = array_new(BYTEARRAY_TYPECODE, len);
|
||||
memset(o->items, 0, len);
|
||||
return MP_OBJ_FROM_PTR(o);
|
||||
} else {
|
||||
// 1 arg: construct the bytearray from that
|
||||
return array_construct(BYTEARRAY_TYPECODE, args[0]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if MICROPY_PY_BUILTINS_MEMORYVIEW
|
||||
|
||||
mp_obj_t mp_obj_new_memoryview(byte typecode, size_t nitems, void *items) {
|
||||
mp_obj_array_t *self = m_new_obj(mp_obj_array_t);
|
||||
self->base.type = &mp_type_memoryview;
|
||||
self->typecode = typecode;
|
||||
self->memview_offset = 0;
|
||||
self->len = nitems;
|
||||
self->items = items;
|
||||
return MP_OBJ_FROM_PTR(self);
|
||||
}
|
||||
|
||||
STATIC mp_obj_t memoryview_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args) {
|
||||
(void)type_in;
|
||||
|
||||
// TODO possibly allow memoryview constructor to take start/stop so that one
|
||||
// can do memoryview(b, 4, 8) instead of memoryview(b)[4:8] (uses less RAM)
|
||||
|
||||
mp_arg_check_num(n_args, n_kw, 1, 1, false);
|
||||
|
||||
mp_buffer_info_t bufinfo;
|
||||
mp_get_buffer_raise(args[0], &bufinfo, MP_BUFFER_READ);
|
||||
|
||||
mp_obj_array_t *self = MP_OBJ_TO_PTR(mp_obj_new_memoryview(bufinfo.typecode,
|
||||
bufinfo.len / mp_binary_get_size('@', bufinfo.typecode, NULL),
|
||||
bufinfo.buf));
|
||||
|
||||
// test if the object can be written to
|
||||
if (mp_get_buffer(args[0], &bufinfo, MP_BUFFER_RW)) {
|
||||
self->typecode |= MP_OBJ_ARRAY_TYPECODE_FLAG_RW; // indicate writable buffer
|
||||
}
|
||||
|
||||
return MP_OBJ_FROM_PTR(self);
|
||||
}
|
||||
|
||||
#if MICROPY_PY_BUILTINS_MEMORYVIEW_ITEMSIZE
|
||||
STATIC void memoryview_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
|
||||
if (dest[0] != MP_OBJ_NULL) {
|
||||
return;
|
||||
}
|
||||
if (attr == MP_QSTR_itemsize) {
|
||||
mp_obj_array_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
dest[0] = MP_OBJ_NEW_SMALL_INT(mp_binary_get_size('@', self->typecode & TYPECODE_MASK, NULL));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
STATIC mp_obj_t array_unary_op(mp_unary_op_t op, mp_obj_t o_in) {
|
||||
mp_obj_array_t *o = MP_OBJ_TO_PTR(o_in);
|
||||
switch (op) {
|
||||
case MP_UNARY_OP_BOOL:
|
||||
return mp_obj_new_bool(o->len != 0);
|
||||
case MP_UNARY_OP_LEN:
|
||||
return MP_OBJ_NEW_SMALL_INT(o->len);
|
||||
default:
|
||||
return MP_OBJ_NULL; // op not supported
|
||||
}
|
||||
}
|
||||
|
||||
STATIC mp_obj_t array_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
|
||||
mp_obj_array_t *lhs = MP_OBJ_TO_PTR(lhs_in);
|
||||
switch (op) {
|
||||
case MP_BINARY_OP_ADD: {
|
||||
// allow to add anything that has the buffer protocol (extension to CPython)
|
||||
mp_buffer_info_t lhs_bufinfo;
|
||||
mp_buffer_info_t rhs_bufinfo;
|
||||
array_get_buffer(lhs_in, &lhs_bufinfo, MP_BUFFER_READ);
|
||||
mp_get_buffer_raise(rhs_in, &rhs_bufinfo, MP_BUFFER_READ);
|
||||
|
||||
size_t sz = mp_binary_get_size('@', lhs_bufinfo.typecode, NULL);
|
||||
|
||||
// convert byte count to element count (in case rhs is not multiple of sz)
|
||||
size_t rhs_len = rhs_bufinfo.len / sz;
|
||||
|
||||
// note: lhs->len is element count of lhs, lhs_bufinfo.len is byte count
|
||||
mp_obj_array_t *res = array_new(lhs_bufinfo.typecode, lhs->len + rhs_len);
|
||||
mp_seq_cat((byte *)res->items, lhs_bufinfo.buf, lhs_bufinfo.len, rhs_bufinfo.buf, rhs_len * sz, byte);
|
||||
return MP_OBJ_FROM_PTR(res);
|
||||
}
|
||||
|
||||
case MP_BINARY_OP_INPLACE_ADD: {
|
||||
#if MICROPY_PY_BUILTINS_MEMORYVIEW
|
||||
if (lhs->base.type == &mp_type_memoryview) {
|
||||
return MP_OBJ_NULL; // op not supported
|
||||
}
|
||||
#endif
|
||||
array_extend(lhs_in, rhs_in);
|
||||
return lhs_in;
|
||||
}
|
||||
|
||||
case MP_BINARY_OP_CONTAINS: {
|
||||
#if MICROPY_PY_BUILTINS_BYTEARRAY
|
||||
// Can search string only in bytearray
|
||||
mp_buffer_info_t lhs_bufinfo;
|
||||
mp_buffer_info_t rhs_bufinfo;
|
||||
if (mp_get_buffer(rhs_in, &rhs_bufinfo, MP_BUFFER_READ)) {
|
||||
if (!mp_obj_is_type(lhs_in, &mp_type_bytearray)) {
|
||||
return mp_const_false;
|
||||
}
|
||||
array_get_buffer(lhs_in, &lhs_bufinfo, MP_BUFFER_READ);
|
||||
return mp_obj_new_bool(
|
||||
find_subbytes(lhs_bufinfo.buf, lhs_bufinfo.len, rhs_bufinfo.buf, rhs_bufinfo.len, 1) != NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Otherwise, can only look for a scalar numeric value in an array
|
||||
if (mp_obj_is_int(rhs_in) || mp_obj_is_float(rhs_in)) {
|
||||
mp_raise_NotImplementedError(NULL);
|
||||
}
|
||||
|
||||
return mp_const_false;
|
||||
}
|
||||
|
||||
case MP_BINARY_OP_EQUAL: {
|
||||
mp_buffer_info_t lhs_bufinfo;
|
||||
mp_buffer_info_t rhs_bufinfo;
|
||||
array_get_buffer(lhs_in, &lhs_bufinfo, MP_BUFFER_READ);
|
||||
if (!mp_get_buffer(rhs_in, &rhs_bufinfo, MP_BUFFER_READ)) {
|
||||
return mp_const_false;
|
||||
}
|
||||
return mp_obj_new_bool(mp_seq_cmp_bytes(op, lhs_bufinfo.buf, lhs_bufinfo.len, rhs_bufinfo.buf, rhs_bufinfo.len));
|
||||
}
|
||||
|
||||
default:
|
||||
return MP_OBJ_NULL; // op not supported
|
||||
}
|
||||
}
|
||||
|
||||
#if MICROPY_PY_BUILTINS_BYTEARRAY || MICROPY_PY_ARRAY
|
||||
STATIC mp_obj_t array_append(mp_obj_t self_in, mp_obj_t arg) {
|
||||
// self is not a memoryview, so we don't need to use (& TYPECODE_MASK)
|
||||
assert((MICROPY_PY_BUILTINS_BYTEARRAY && mp_obj_is_type(self_in, &mp_type_bytearray))
|
||||
|| (MICROPY_PY_ARRAY && mp_obj_is_type(self_in, &mp_type_array)));
|
||||
mp_obj_array_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
|
||||
if (self->free == 0) {
|
||||
size_t item_sz = mp_binary_get_size('@', self->typecode, NULL);
|
||||
// TODO: alloc policy
|
||||
self->free = 8;
|
||||
self->items = m_renew(byte, self->items, item_sz * self->len, item_sz * (self->len + self->free));
|
||||
mp_seq_clear(self->items, self->len + 1, self->len + self->free, item_sz);
|
||||
}
|
||||
mp_binary_set_val_array(self->typecode, self->items, self->len, arg);
|
||||
// only update length/free if set succeeded
|
||||
self->len++;
|
||||
self->free--;
|
||||
return mp_const_none; // return None, as per CPython
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(array_append_obj, array_append);
|
||||
|
||||
STATIC mp_obj_t array_extend(mp_obj_t self_in, mp_obj_t arg_in) {
|
||||
// self is not a memoryview, so we don't need to use (& TYPECODE_MASK)
|
||||
assert((MICROPY_PY_BUILTINS_BYTEARRAY && mp_obj_is_type(self_in, &mp_type_bytearray))
|
||||
|| (MICROPY_PY_ARRAY && mp_obj_is_type(self_in, &mp_type_array)));
|
||||
mp_obj_array_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
|
||||
// allow to extend by anything that has the buffer protocol (extension to CPython)
|
||||
mp_buffer_info_t arg_bufinfo;
|
||||
mp_get_buffer_raise(arg_in, &arg_bufinfo, MP_BUFFER_READ);
|
||||
|
||||
size_t sz = mp_binary_get_size('@', self->typecode, NULL);
|
||||
|
||||
// convert byte count to element count
|
||||
size_t len = arg_bufinfo.len / sz;
|
||||
|
||||
// make sure we have enough room to extend
|
||||
// TODO: alloc policy; at the moment we go conservative
|
||||
if (self->free < len) {
|
||||
self->items = m_renew(byte, self->items, (self->len + self->free) * sz, (self->len + len) * sz);
|
||||
self->free = 0;
|
||||
} else {
|
||||
self->free -= len;
|
||||
}
|
||||
|
||||
// extend
|
||||
mp_seq_copy((byte *)self->items + self->len * sz, arg_bufinfo.buf, len * sz, byte);
|
||||
self->len += len;
|
||||
|
||||
return mp_const_none;
|
||||
}
|
||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(array_extend_obj, array_extend);
|
||||
#endif
|
||||
|
||||
STATIC mp_obj_t array_subscr(mp_obj_t self_in, mp_obj_t index_in, mp_obj_t value) {
|
||||
if (value == MP_OBJ_NULL) {
|
||||
// delete item
|
||||
// TODO implement
|
||||
// TODO: confirmed that both bytearray and array.array support
|
||||
// slice deletion
|
||||
return MP_OBJ_NULL; // op not supported
|
||||
} else {
|
||||
mp_obj_array_t *o = MP_OBJ_TO_PTR(self_in);
|
||||
#if MICROPY_PY_BUILTINS_SLICE
|
||||
if (mp_obj_is_type(index_in, &mp_type_slice)) {
|
||||
mp_bound_slice_t slice;
|
||||
if (!mp_seq_get_fast_slice_indexes(o->len, index_in, &slice)) {
|
||||
mp_raise_NotImplementedError(MP_ERROR_TEXT("only slices with step=1 (aka None) are supported"));
|
||||
}
|
||||
if (value != MP_OBJ_SENTINEL) {
|
||||
#if MICROPY_PY_ARRAY_SLICE_ASSIGN
|
||||
// Assign
|
||||
size_t src_len;
|
||||
void *src_items;
|
||||
size_t item_sz = mp_binary_get_size('@', o->typecode & TYPECODE_MASK, NULL);
|
||||
if (mp_obj_is_obj(value) && ((mp_obj_base_t *)MP_OBJ_TO_PTR(value))->type->subscr == array_subscr) {
|
||||
// value is array, bytearray or memoryview
|
||||
mp_obj_array_t *src_slice = MP_OBJ_TO_PTR(value);
|
||||
if (item_sz != mp_binary_get_size('@', src_slice->typecode & TYPECODE_MASK, NULL)) {
|
||||
compat_error:
|
||||
mp_raise_ValueError(MP_ERROR_TEXT("lhs and rhs should be compatible"));
|
||||
}
|
||||
src_len = src_slice->len;
|
||||
src_items = src_slice->items;
|
||||
#if MICROPY_PY_BUILTINS_MEMORYVIEW
|
||||
if (mp_obj_is_type(value, &mp_type_memoryview)) {
|
||||
src_items = (uint8_t *)src_items + (src_slice->memview_offset * item_sz);
|
||||
}
|
||||
#endif
|
||||
} else if (mp_obj_is_type(value, &mp_type_bytes)) {
|
||||
if (item_sz != 1) {
|
||||
goto compat_error;
|
||||
}
|
||||
mp_buffer_info_t bufinfo;
|
||||
mp_get_buffer_raise(value, &bufinfo, MP_BUFFER_READ);
|
||||
src_len = bufinfo.len;
|
||||
src_items = bufinfo.buf;
|
||||
} else {
|
||||
mp_raise_NotImplementedError(MP_ERROR_TEXT("array/bytes required on right side"));
|
||||
}
|
||||
|
||||
// TODO: check src/dst compat
|
||||
mp_int_t len_adj = src_len - (slice.stop - slice.start);
|
||||
uint8_t *dest_items = o->items;
|
||||
#if MICROPY_PY_BUILTINS_MEMORYVIEW
|
||||
if (o->base.type == &mp_type_memoryview) {
|
||||
if (!(o->typecode & MP_OBJ_ARRAY_TYPECODE_FLAG_RW)) {
|
||||
// store to read-only memoryview not allowed
|
||||
return MP_OBJ_NULL;
|
||||
}
|
||||
if (len_adj != 0) {
|
||||
goto compat_error;
|
||||
}
|
||||
dest_items += o->memview_offset * item_sz;
|
||||
}
|
||||
#endif
|
||||
if (len_adj > 0) {
|
||||
if ((size_t)len_adj > o->free) {
|
||||
// TODO: alloc policy; at the moment we go conservative
|
||||
o->items = m_renew(byte, o->items, (o->len + o->free) * item_sz, (o->len + len_adj) * item_sz);
|
||||
o->free = len_adj;
|
||||
dest_items = o->items;
|
||||
}
|
||||
mp_seq_replace_slice_grow_inplace(dest_items, o->len,
|
||||
slice.start, slice.stop, src_items, src_len, len_adj, item_sz);
|
||||
} else {
|
||||
mp_seq_replace_slice_no_grow(dest_items, o->len,
|
||||
slice.start, slice.stop, src_items, src_len, item_sz);
|
||||
// Clear "freed" elements at the end of list
|
||||
// TODO: This is actually only needed for typecode=='O'
|
||||
mp_seq_clear(dest_items, o->len + len_adj, o->len, item_sz);
|
||||
// TODO: alloc policy after shrinking
|
||||
}
|
||||
o->free -= len_adj;
|
||||
o->len += len_adj;
|
||||
return mp_const_none;
|
||||
#else
|
||||
return MP_OBJ_NULL; // op not supported
|
||||
#endif
|
||||
}
|
||||
|
||||
mp_obj_array_t *res;
|
||||
size_t sz = mp_binary_get_size('@', o->typecode & TYPECODE_MASK, NULL);
|
||||
assert(sz > 0);
|
||||
#if MICROPY_PY_BUILTINS_MEMORYVIEW
|
||||
if (o->base.type == &mp_type_memoryview) {
|
||||
res = m_new_obj(mp_obj_array_t);
|
||||
*res = *o;
|
||||
res->memview_offset += slice.start;
|
||||
res->len = slice.stop - slice.start;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
res = array_new(o->typecode, slice.stop - slice.start);
|
||||
memcpy(res->items, (uint8_t *)o->items + slice.start * sz, (slice.stop - slice.start) * sz);
|
||||
}
|
||||
return MP_OBJ_FROM_PTR(res);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
size_t index = mp_get_index(o->base.type, o->len, index_in, false);
|
||||
#if MICROPY_PY_BUILTINS_MEMORYVIEW
|
||||
if (o->base.type == &mp_type_memoryview) {
|
||||
index += o->memview_offset;
|
||||
if (value != MP_OBJ_SENTINEL && !(o->typecode & MP_OBJ_ARRAY_TYPECODE_FLAG_RW)) {
|
||||
// store to read-only memoryview
|
||||
return MP_OBJ_NULL;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (value == MP_OBJ_SENTINEL) {
|
||||
// load
|
||||
return mp_binary_get_val_array(o->typecode & TYPECODE_MASK, o->items, index);
|
||||
} else {
|
||||
// store
|
||||
mp_binary_set_val_array(o->typecode & TYPECODE_MASK, o->items, index, value);
|
||||
return mp_const_none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
STATIC mp_int_t array_get_buffer(mp_obj_t o_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) {
|
||||
mp_obj_array_t *o = MP_OBJ_TO_PTR(o_in);
|
||||
size_t sz = mp_binary_get_size('@', o->typecode & TYPECODE_MASK, NULL);
|
||||
bufinfo->buf = o->items;
|
||||
bufinfo->len = o->len * sz;
|
||||
bufinfo->typecode = o->typecode & TYPECODE_MASK;
|
||||
#if MICROPY_PY_BUILTINS_MEMORYVIEW
|
||||
if (o->base.type == &mp_type_memoryview) {
|
||||
if (!(o->typecode & MP_OBJ_ARRAY_TYPECODE_FLAG_RW) && (flags & MP_BUFFER_WRITE)) {
|
||||
// read-only memoryview
|
||||
return 1;
|
||||
}
|
||||
bufinfo->buf = (uint8_t *)bufinfo->buf + (size_t)o->memview_offset * sz;
|
||||
}
|
||||
#else
|
||||
(void)flags;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if MICROPY_PY_BUILTINS_BYTEARRAY || MICROPY_PY_ARRAY
|
||||
STATIC const mp_rom_map_elem_t array_locals_dict_table[] = {
|
||||
{ MP_ROM_QSTR(MP_QSTR_append), MP_ROM_PTR(&array_append_obj) },
|
||||
{ MP_ROM_QSTR(MP_QSTR_extend), MP_ROM_PTR(&array_extend_obj) },
|
||||
#if MICROPY_CPYTHON_COMPAT
|
||||
{ MP_ROM_QSTR(MP_QSTR_decode), MP_ROM_PTR(&bytes_decode_obj) },
|
||||
#endif
|
||||
};
|
||||
|
||||
STATIC MP_DEFINE_CONST_DICT(array_locals_dict, array_locals_dict_table);
|
||||
#endif
|
||||
|
||||
#if MICROPY_PY_ARRAY
|
||||
const mp_obj_type_t mp_type_array = {
|
||||
{ &mp_type_type },
|
||||
.name = MP_QSTR_array,
|
||||
.print = array_print,
|
||||
.make_new = array_make_new,
|
||||
.getiter = array_iterator_new,
|
||||
.unary_op = array_unary_op,
|
||||
.binary_op = array_binary_op,
|
||||
.subscr = array_subscr,
|
||||
.buffer_p = { .get_buffer = array_get_buffer },
|
||||
.locals_dict = (mp_obj_dict_t *)&array_locals_dict,
|
||||
};
|
||||
#endif
|
||||
|
||||
#if MICROPY_PY_BUILTINS_BYTEARRAY
|
||||
const mp_obj_type_t mp_type_bytearray = {
|
||||
{ &mp_type_type },
|
||||
.flags = MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE,
|
||||
.name = MP_QSTR_bytearray,
|
||||
.print = array_print,
|
||||
.make_new = bytearray_make_new,
|
||||
.getiter = array_iterator_new,
|
||||
.unary_op = array_unary_op,
|
||||
.binary_op = array_binary_op,
|
||||
.subscr = array_subscr,
|
||||
.buffer_p = { .get_buffer = array_get_buffer },
|
||||
.locals_dict = (mp_obj_dict_t *)&array_locals_dict,
|
||||
};
|
||||
#endif
|
||||
|
||||
#if MICROPY_PY_BUILTINS_MEMORYVIEW
|
||||
const mp_obj_type_t mp_type_memoryview = {
|
||||
{ &mp_type_type },
|
||||
.flags = MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE,
|
||||
.name = MP_QSTR_memoryview,
|
||||
.make_new = memoryview_make_new,
|
||||
.getiter = array_iterator_new,
|
||||
.unary_op = array_unary_op,
|
||||
.binary_op = array_binary_op,
|
||||
#if MICROPY_PY_BUILTINS_MEMORYVIEW_ITEMSIZE
|
||||
.attr = memoryview_attr,
|
||||
#endif
|
||||
.subscr = array_subscr,
|
||||
.buffer_p = { .get_buffer = array_get_buffer },
|
||||
};
|
||||
#endif
|
||||
|
||||
/* unused
|
||||
size_t mp_obj_array_len(mp_obj_t self_in) {
|
||||
return ((mp_obj_array_t *)self_in)->len;
|
||||
}
|
||||
*/
|
||||
|
||||
#if MICROPY_PY_BUILTINS_BYTEARRAY
|
||||
mp_obj_t mp_obj_new_bytearray(size_t n, void *items) {
|
||||
mp_obj_array_t *o = array_new(BYTEARRAY_TYPECODE, n);
|
||||
memcpy(o->items, items, n);
|
||||
return MP_OBJ_FROM_PTR(o);
|
||||
}
|
||||
|
||||
// Create bytearray which references specified memory area
|
||||
mp_obj_t mp_obj_new_bytearray_by_ref(size_t n, void *items) {
|
||||
mp_obj_array_t *o = m_new_obj(mp_obj_array_t);
|
||||
o->base.type = &mp_type_bytearray;
|
||||
o->typecode = BYTEARRAY_TYPECODE;
|
||||
o->free = 0;
|
||||
o->len = n;
|
||||
o->items = items;
|
||||
return MP_OBJ_FROM_PTR(o);
|
||||
}
|
||||
#endif
|
||||
|
||||
/******************************************************************************/
|
||||
// array iterator
|
||||
|
||||
typedef struct _mp_obj_array_it_t {
|
||||
mp_obj_base_t base;
|
||||
mp_obj_array_t *array;
|
||||
size_t offset;
|
||||
size_t cur;
|
||||
} mp_obj_array_it_t;
|
||||
|
||||
STATIC mp_obj_t array_it_iternext(mp_obj_t self_in) {
|
||||
mp_obj_array_it_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
if (self->cur < self->array->len) {
|
||||
return mp_binary_get_val_array(self->array->typecode & TYPECODE_MASK, self->array->items, self->offset + self->cur++);
|
||||
} else {
|
||||
return MP_OBJ_STOP_ITERATION;
|
||||
}
|
||||
}
|
||||
|
||||
STATIC const mp_obj_type_t array_it_type = {
|
||||
{ &mp_type_type },
|
||||
.name = MP_QSTR_iterator,
|
||||
.getiter = mp_identity_getiter,
|
||||
.iternext = array_it_iternext,
|
||||
};
|
||||
|
||||
STATIC mp_obj_t array_iterator_new(mp_obj_t array_in, mp_obj_iter_buf_t *iter_buf) {
|
||||
assert(sizeof(mp_obj_array_t) <= sizeof(mp_obj_iter_buf_t));
|
||||
mp_obj_array_t *array = MP_OBJ_TO_PTR(array_in);
|
||||
mp_obj_array_it_t *o = (mp_obj_array_it_t *)iter_buf;
|
||||
o->base.type = &array_it_type;
|
||||
o->array = array;
|
||||
o->offset = 0;
|
||||
o->cur = 0;
|
||||
#if MICROPY_PY_BUILTINS_MEMORYVIEW
|
||||
if (array->base.type == &mp_type_memoryview) {
|
||||
o->offset = array->memview_offset;
|
||||
}
|
||||
#endif
|
||||
return MP_OBJ_FROM_PTR(o);
|
||||
}
|
||||
|
||||
#endif // MICROPY_PY_ARRAY || MICROPY_PY_BUILTINS_BYTEARRAY || MICROPY_PY_BUILTINS_MEMORYVIEW
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013, 2014 Damien P. George
|
||||
* Copyright (c) 2014 Paul Sokolovsky
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
#ifndef MICROPY_INCLUDED_PY_OBJARRAY_H
|
||||
#define MICROPY_INCLUDED_PY_OBJARRAY_H
|
||||
|
||||
#include "py/obj.h"
|
||||
|
||||
// Used only for memoryview types, set in "typecode" to indicate a writable memoryview
|
||||
#define MP_OBJ_ARRAY_TYPECODE_FLAG_RW (0x80)
|
||||
|
||||
// This structure is used for all of bytearray, array.array, memoryview
|
||||
// objects. Note that memoryview has different meaning for some fields,
|
||||
// see comment at the beginning of objarray.c.
|
||||
typedef struct _mp_obj_array_t {
|
||||
mp_obj_base_t base;
|
||||
size_t typecode : 8;
|
||||
// free is number of unused elements after len used elements
|
||||
// alloc size = len + free
|
||||
// But for memoryview, 'free' is reused as offset (in elements) into the
|
||||
// parent object. (Union is not used to not go into a complication of
|
||||
// union-of-bitfields with different toolchains). See comments in
|
||||
// objarray.c.
|
||||
size_t free : (8 * sizeof(size_t) - 8);
|
||||
size_t len; // in elements
|
||||
void *items;
|
||||
} mp_obj_array_t;
|
||||
|
||||
#if MICROPY_PY_BUILTINS_MEMORYVIEW
|
||||
static inline void mp_obj_memoryview_init(mp_obj_array_t *self, size_t typecode, size_t offset, size_t len, void *items) {
|
||||
self->base.type = &mp_type_memoryview;
|
||||
self->typecode = typecode;
|
||||
self->free = offset;
|
||||
self->len = len;
|
||||
self->items = items;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // MICROPY_INCLUDED_PY_OBJARRAY_H
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2015 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "py/objtuple.h"
|
||||
|
||||
#if MICROPY_PY_ATTRTUPLE || MICROPY_PY_COLLECTIONS
|
||||
|
||||
// this helper function is used by collections.namedtuple
|
||||
#if !MICROPY_PY_COLLECTIONS
|
||||
STATIC
|
||||
#endif
|
||||
void mp_obj_attrtuple_print_helper(const mp_print_t *print, const qstr *fields, mp_obj_tuple_t *o) {
|
||||
mp_print_str(print, "(");
|
||||
for (size_t i = 0; i < o->len; i++) {
|
||||
if (i > 0) {
|
||||
mp_print_str(print, ", ");
|
||||
}
|
||||
mp_printf(print, "%q=", fields[i]);
|
||||
mp_obj_print_helper(print, o->items[i], PRINT_REPR);
|
||||
}
|
||||
mp_print_str(print, ")");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if MICROPY_PY_ATTRTUPLE
|
||||
|
||||
STATIC void mp_obj_attrtuple_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) {
|
||||
(void)kind;
|
||||
mp_obj_tuple_t *o = MP_OBJ_TO_PTR(o_in);
|
||||
const qstr *fields = (const qstr *)MP_OBJ_TO_PTR(o->items[o->len]);
|
||||
mp_obj_attrtuple_print_helper(print, fields, o);
|
||||
}
|
||||
|
||||
STATIC void mp_obj_attrtuple_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
|
||||
if (dest[0] == MP_OBJ_NULL) {
|
||||
// load attribute
|
||||
mp_obj_tuple_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
size_t len = self->len;
|
||||
const qstr *fields = (const qstr *)MP_OBJ_TO_PTR(self->items[len]);
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
if (fields[i] == attr) {
|
||||
dest[0] = self->items[i];
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mp_obj_t mp_obj_new_attrtuple(const qstr *fields, size_t n, const mp_obj_t *items) {
|
||||
mp_obj_tuple_t *o = m_new_obj_var(mp_obj_tuple_t, mp_obj_t, n + 1);
|
||||
o->base.type = &mp_type_attrtuple;
|
||||
o->len = n;
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
o->items[i] = items[i];
|
||||
}
|
||||
o->items[n] = MP_OBJ_FROM_PTR(fields);
|
||||
return MP_OBJ_FROM_PTR(o);
|
||||
}
|
||||
|
||||
const mp_obj_type_t mp_type_attrtuple = {
|
||||
{ &mp_type_type },
|
||||
.name = MP_QSTR_tuple, // reuse tuple to save on a qstr
|
||||
.print = mp_obj_attrtuple_print,
|
||||
.unary_op = mp_obj_tuple_unary_op,
|
||||
.binary_op = mp_obj_tuple_binary_op,
|
||||
.attr = mp_obj_attrtuple_attr,
|
||||
.subscr = mp_obj_tuple_subscr,
|
||||
.getiter = mp_obj_tuple_getiter,
|
||||
};
|
||||
|
||||
#endif // MICROPY_PY_ATTRTUPLE
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013, 2014 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "py/runtime.h"
|
||||
|
||||
#if MICROPY_OBJ_IMMEDIATE_OBJS
|
||||
|
||||
#define BOOL_VALUE(o) ((o) == mp_const_false ? 0 : 1)
|
||||
|
||||
#else
|
||||
|
||||
#define BOOL_VALUE(o) (((mp_obj_bool_t *)MP_OBJ_TO_PTR(o))->value)
|
||||
|
||||
typedef struct _mp_obj_bool_t {
|
||||
mp_obj_base_t base;
|
||||
bool value;
|
||||
} mp_obj_bool_t;
|
||||
|
||||
#endif
|
||||
|
||||
STATIC void bool_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
|
||||
bool value = BOOL_VALUE(self_in);
|
||||
if (MICROPY_PY_UJSON && kind == PRINT_JSON) {
|
||||
if (value) {
|
||||
mp_print_str(print, "true");
|
||||
} else {
|
||||
mp_print_str(print, "false");
|
||||
}
|
||||
} else {
|
||||
if (value) {
|
||||
mp_print_str(print, "True");
|
||||
} else {
|
||||
mp_print_str(print, "False");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
STATIC mp_obj_t bool_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args) {
|
||||
(void)type_in;
|
||||
mp_arg_check_num(n_args, n_kw, 0, 1, false);
|
||||
|
||||
if (n_args == 0) {
|
||||
return mp_const_false;
|
||||
} else {
|
||||
return mp_obj_new_bool(mp_obj_is_true(args[0]));
|
||||
}
|
||||
}
|
||||
|
||||
STATIC mp_obj_t bool_unary_op(mp_unary_op_t op, mp_obj_t o_in) {
|
||||
if (op == MP_UNARY_OP_LEN) {
|
||||
return MP_OBJ_NULL;
|
||||
}
|
||||
bool value = BOOL_VALUE(o_in);
|
||||
return mp_unary_op(op, MP_OBJ_NEW_SMALL_INT(value));
|
||||
}
|
||||
|
||||
STATIC mp_obj_t bool_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) {
|
||||
bool value = BOOL_VALUE(lhs_in);
|
||||
return mp_binary_op(op, MP_OBJ_NEW_SMALL_INT(value), rhs_in);
|
||||
}
|
||||
|
||||
const mp_obj_type_t mp_type_bool = {
|
||||
{ &mp_type_type },
|
||||
.flags = MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE, // can match all numeric types
|
||||
.name = MP_QSTR_bool,
|
||||
.print = bool_print,
|
||||
.make_new = bool_make_new,
|
||||
.unary_op = bool_unary_op,
|
||||
.binary_op = bool_binary_op,
|
||||
};
|
||||
|
||||
#if !MICROPY_OBJ_IMMEDIATE_OBJS
|
||||
const mp_obj_bool_t mp_const_false_obj = {{&mp_type_bool}, false};
|
||||
const mp_obj_bool_t mp_const_true_obj = {{&mp_type_bool}, true};
|
||||
#endif
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013, 2014 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "py/obj.h"
|
||||
#include "py/runtime.h"
|
||||
|
||||
typedef struct _mp_obj_bound_meth_t {
|
||||
mp_obj_base_t base;
|
||||
mp_obj_t meth;
|
||||
mp_obj_t self;
|
||||
} mp_obj_bound_meth_t;
|
||||
|
||||
#if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_DETAILED
|
||||
STATIC void bound_meth_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) {
|
||||
(void)kind;
|
||||
mp_obj_bound_meth_t *o = MP_OBJ_TO_PTR(o_in);
|
||||
mp_printf(print, "<bound_method %p ", o);
|
||||
mp_obj_print_helper(print, o->self, PRINT_REPR);
|
||||
mp_print_str(print, ".");
|
||||
mp_obj_print_helper(print, o->meth, PRINT_REPR);
|
||||
mp_print_str(print, ">");
|
||||
}
|
||||
#endif
|
||||
|
||||
mp_obj_t mp_call_method_self_n_kw(mp_obj_t meth, mp_obj_t self, size_t n_args, size_t n_kw, const mp_obj_t *args) {
|
||||
// need to insert self before all other args and then call meth
|
||||
size_t n_total = n_args + 2 * n_kw;
|
||||
mp_obj_t *args2 = NULL;
|
||||
#if MICROPY_ENABLE_PYSTACK
|
||||
args2 = mp_pystack_alloc(sizeof(mp_obj_t) * (1 + n_total));
|
||||
#else
|
||||
mp_obj_t *free_args2 = NULL;
|
||||
if (n_total > 4) {
|
||||
// try to use heap to allocate temporary args array
|
||||
args2 = m_new_maybe(mp_obj_t, 1 + n_total);
|
||||
free_args2 = args2;
|
||||
}
|
||||
if (args2 == NULL) {
|
||||
// (fallback to) use stack to allocate temporary args array
|
||||
args2 = alloca(sizeof(mp_obj_t) * (1 + n_total));
|
||||
}
|
||||
#endif
|
||||
args2[0] = self;
|
||||
memcpy(args2 + 1, args, n_total * sizeof(mp_obj_t));
|
||||
mp_obj_t res = mp_call_function_n_kw(meth, n_args + 1, n_kw, args2);
|
||||
#if MICROPY_ENABLE_PYSTACK
|
||||
mp_pystack_free(args2);
|
||||
#else
|
||||
if (free_args2 != NULL) {
|
||||
m_del(mp_obj_t, free_args2, 1 + n_total);
|
||||
}
|
||||
#endif
|
||||
return res;
|
||||
}
|
||||
|
||||
STATIC mp_obj_t bound_meth_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) {
|
||||
mp_obj_bound_meth_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
return mp_call_method_self_n_kw(self->meth, self->self, n_args, n_kw, args);
|
||||
}
|
||||
|
||||
#if MICROPY_PY_FUNCTION_ATTRS
|
||||
STATIC void bound_meth_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
|
||||
if (dest[0] != MP_OBJ_NULL) {
|
||||
// not load attribute
|
||||
return;
|
||||
}
|
||||
// Delegate the load to the method object
|
||||
mp_obj_bound_meth_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
mp_load_method_maybe(self->meth, attr, dest);
|
||||
}
|
||||
#endif
|
||||
|
||||
STATIC const mp_obj_type_t mp_type_bound_meth = {
|
||||
{ &mp_type_type },
|
||||
.name = MP_QSTR_bound_method,
|
||||
#if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_DETAILED
|
||||
.print = bound_meth_print,
|
||||
#endif
|
||||
.call = bound_meth_call,
|
||||
#if MICROPY_PY_FUNCTION_ATTRS
|
||||
.attr = bound_meth_attr,
|
||||
#endif
|
||||
};
|
||||
|
||||
mp_obj_t mp_obj_new_bound_meth(mp_obj_t meth, mp_obj_t self) {
|
||||
mp_obj_bound_meth_t *o = m_new_obj(mp_obj_bound_meth_t);
|
||||
o->base.type = &mp_type_bound_meth;
|
||||
o->meth = meth;
|
||||
o->self = self;
|
||||
return MP_OBJ_FROM_PTR(o);
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013, 2014 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "py/obj.h"
|
||||
|
||||
typedef struct _mp_obj_cell_t {
|
||||
mp_obj_base_t base;
|
||||
mp_obj_t obj;
|
||||
} mp_obj_cell_t;
|
||||
|
||||
mp_obj_t mp_obj_cell_get(mp_obj_t self_in) {
|
||||
mp_obj_cell_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
return self->obj;
|
||||
}
|
||||
|
||||
void mp_obj_cell_set(mp_obj_t self_in, mp_obj_t obj) {
|
||||
mp_obj_cell_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
self->obj = obj;
|
||||
}
|
||||
|
||||
#if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_DETAILED
|
||||
STATIC void cell_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) {
|
||||
(void)kind;
|
||||
mp_obj_cell_t *o = MP_OBJ_TO_PTR(o_in);
|
||||
mp_printf(print, "<cell %p ", o->obj);
|
||||
if (o->obj == MP_OBJ_NULL) {
|
||||
mp_print_str(print, "(nil)");
|
||||
} else {
|
||||
mp_obj_print_helper(print, o->obj, PRINT_REPR);
|
||||
}
|
||||
mp_print_str(print, ">");
|
||||
}
|
||||
#endif
|
||||
|
||||
STATIC const mp_obj_type_t mp_type_cell = {
|
||||
{ &mp_type_type },
|
||||
.name = MP_QSTR_, // cell representation is just value in < >
|
||||
#if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_DETAILED
|
||||
.print = cell_print,
|
||||
#endif
|
||||
};
|
||||
|
||||
mp_obj_t mp_obj_new_cell(mp_obj_t obj) {
|
||||
mp_obj_cell_t *o = m_new_obj(mp_obj_cell_t);
|
||||
o->base.type = &mp_type_cell;
|
||||
o->obj = obj;
|
||||
return MP_OBJ_FROM_PTR(o);
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* This file is part of the MicroPython project, http://micropython.org/
|
||||
*
|
||||
* The MIT License (MIT)
|
||||
*
|
||||
* Copyright (c) 2013, 2014 Damien P. George
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "py/obj.h"
|
||||
#include "py/runtime.h"
|
||||
|
||||
typedef struct _mp_obj_closure_t {
|
||||
mp_obj_base_t base;
|
||||
mp_obj_t fun;
|
||||
size_t n_closed;
|
||||
mp_obj_t closed[];
|
||||
} mp_obj_closure_t;
|
||||
|
||||
STATIC mp_obj_t closure_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) {
|
||||
mp_obj_closure_t *self = MP_OBJ_TO_PTR(self_in);
|
||||
|
||||
// need to concatenate closed-over-vars and args
|
||||
|
||||
size_t n_total = self->n_closed + n_args + 2 * n_kw;
|
||||
if (n_total <= 5) {
|
||||
// use stack to allocate temporary args array
|
||||
mp_obj_t args2[5];
|
||||
memcpy(args2, self->closed, self->n_closed * sizeof(mp_obj_t));
|
||||
memcpy(args2 + self->n_closed, args, (n_args + 2 * n_kw) * sizeof(mp_obj_t));
|
||||
return mp_call_function_n_kw(self->fun, self->n_closed + n_args, n_kw, args2);
|
||||
} else {
|
||||
// use heap to allocate temporary args array
|
||||
mp_obj_t *args2 = m_new(mp_obj_t, n_total);
|
||||
memcpy(args2, self->closed, self->n_closed * sizeof(mp_obj_t));
|
||||
memcpy(args2 + self->n_closed, args, (n_args + 2 * n_kw) * sizeof(mp_obj_t));
|
||||
mp_obj_t res = mp_call_function_n_kw(self->fun, self->n_closed + n_args, n_kw, args2);
|
||||
m_del(mp_obj_t, args2, n_total);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
#if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_DETAILED
|
||||
STATIC void closure_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) {
|
||||
(void)kind;
|
||||
mp_obj_closure_t *o = MP_OBJ_TO_PTR(o_in);
|
||||
mp_print_str(print, "<closure ");
|
||||
mp_obj_print_helper(print, o->fun, PRINT_REPR);
|
||||
mp_printf(print, " at %p, n_closed=%u ", o, (int)o->n_closed);
|
||||
for (size_t i = 0; i < o->n_closed; i++) {
|
||||
if (o->closed[i] == MP_OBJ_NULL) {
|
||||
mp_print_str(print, "(nil)");
|
||||
} else {
|
||||
mp_obj_print_helper(print, o->closed[i], PRINT_REPR);
|
||||
}
|
||||
mp_print_str(print, " ");
|
||||
}
|
||||
mp_print_str(print, ">");
|
||||
}
|
||||
#endif
|
||||
|
||||
const mp_obj_type_t closure_type = {
|
||||
{ &mp_type_type },
|
||||
.flags = MP_TYPE_FLAG_BINDS_SELF,
|
||||
.name = MP_QSTR_closure,
|
||||
#if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_DETAILED
|
||||
.print = closure_print,
|
||||
#endif
|
||||
.call = closure_call,
|
||||
};
|
||||
|
||||
mp_obj_t mp_obj_new_closure(mp_obj_t fun, size_t n_closed_over, const mp_obj_t *closed) {
|
||||
mp_obj_closure_t *o = m_new_obj_var(mp_obj_closure_t, mp_obj_t, n_closed_over);
|
||||
o->base.type = &closure_type;
|
||||
o->fun = fun;
|
||||
o->n_closed = n_closed_over;
|
||||
memcpy(o->closed, closed, n_closed_over * sizeof(mp_obj_t));
|
||||
return MP_OBJ_FROM_PTR(o);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user