micropython: add micropython component

This commit is contained in:
KY-zhang-X
2022-09-29 12:10:37 +08:00
parent 1514f1cb9b
commit dd76146324
2679 changed files with 354110 additions and 0 deletions

View File

@@ -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

View File

@@ -0,0 +1,377 @@
/*
* 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)
// 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_ldrh_reg_reg_offset(asm_arm_t *as, uint rd, uint rn, uint byte_offset) {
// ldrh rd, [rn, #off]
emit_al(as, 0x1f000b0 | (rn << 16) | (rd << 12) | ((byte_offset & 0xf0) << 4) | (byte_offset & 0xf));
}
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

View File

@@ -0,0 +1,218 @@
/*
* 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;
static inline void asm_arm_end_pass(asm_arm_t *as) {
(void)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_ldrh_reg_reg_offset(asm_arm_t *as, uint rd, uint rn, uint byte_offset);
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_LOAD16_REG_REG_OFFSET(as, reg_dest, reg_base, uint16_offset) asm_arm_ldrh_reg_reg_offset((as), (reg_dest), (reg_base), 2 * (uint16_offset))
#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

View File

@@ -0,0 +1,103 @@
/*
* 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(void *as_in, size_t num_bytes_to_write) {
mp_asm_base_t *as = as_in;
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

View File

@@ -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(void *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

View File

@@ -0,0 +1,592 @@
/*
* 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/asmthumb.h"
#ifdef _MSC_VER
#include <intrin.h>
static uint32_t mp_clz(uint32_t x) {
unsigned long lz = 0;
return _BitScanReverse(&lz, x) ? (sizeof(x) * 8 - 1) - lz : 0;
}
static uint32_t mp_ctz(uint32_t x) {
unsigned long tz = 0;
return _BitScanForward(&tz, x) ? tz : 0;
}
#else
#define mp_clz(x) __builtin_clz(x)
#define mp_ctz(x) __builtin_ctz(x)
#endif
#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))
#define OP_LDRH_W_HI(reg_base) (0xf8b0 | (reg_base))
#define OP_LDRH_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);
}
/*
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__) && !defined(__thumb__))
#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 (asm_thumb_allow_armv7m(as)) {
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));
}
} else {
int adj = stack_adjust;
// we don't expect the stack_adjust to be massive
while (!UNSIGNED_FIT7(adj)) {
asm_thumb_op16(as, OP_SUB_SP(127));
adj -= 127;
}
asm_thumb_op16(as, OP_SUB_SP(adj));
}
}
as->push_reglist = reglist;
as->stack_adjust = stack_adjust;
}
void asm_thumb_exit(asm_thumb_t *as) {
if (as->stack_adjust > 0) {
if (asm_thumb_allow_armv7m(as)) {
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));
}
} else {
int adj = as->stack_adjust;
// we don't expect the stack_adjust to be massive
while (!UNSIGNED_FIT7(adj)) {
asm_thumb_op16(as, OP_ADD_SP(127));
adj -= 127;
}
asm_thumb_op16(as, OP_ADD_SP(adj));
}
}
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!
void 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);
// 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));
}
static void asm_thumb_mov_rlo_i16(asm_thumb_t *as, uint rlo_dest, int i16_src) {
asm_thumb_mov_rlo_i8(as, rlo_dest, (i16_src >> 8) & 0xff);
asm_thumb_lsl_rlo_rlo_i5(as, rlo_dest, rlo_dest, 8);
asm_thumb_add_rlo_i8(as, rlo_dest, i16_src & 0xff);
}
#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 if (asm_thumb_allow_armv7m(as)) {
asm_thumb_op32(as, OP_BCC_W_HI(cond, rel), OP_BCC_W_LO(rel));
return true;
} else {
// this method should not be called for ARMV6M
return false;
}
}
#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);
if (asm_thumb_allow_armv7m(as)) {
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);
} else {
// should only be called with lo reg for ARMV6M
assert(reg_dest < ASM_THUMB_REG_R8);
// sanity check that generated code is aligned
assert(!as->base.code_base || !(3u & (uintptr_t)as->base.code_base));
// basically:
// (nop)
// ldr reg_dest, _data
// b 1f
// _data: .word i32
// 1:
if (as->base.code_offset & 2u) {
asm_thumb_op16(as, ASM_THUMB_OP_NOP);
}
asm_thumb_ldr_rlo_pcrel_i8(as, reg_dest, 0);
asm_thumb_op16(as, OP_B_N(2));
asm_thumb_op16(as, i32 & 0xffff);
asm_thumb_op16(as, 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 (asm_thumb_allow_armv7m(as)) {
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);
}
} else {
uint rlo_dest = reg_dest;
assert(rlo_dest < ASM_THUMB_REG_R8); // should never be called for ARMV6M
bool negate = i32 < 0 && ((i32 + i32) & 0xffffffffu); // don't negate 0x80000000
if (negate) {
i32 = -i32;
}
uint clz = mp_clz(i32);
uint ctz = i32 ? mp_ctz(i32) : 0;
assert(clz + ctz <= 32);
if (clz + ctz >= 24) {
asm_thumb_mov_rlo_i8(as, rlo_dest, (i32 >> ctz) & 0xff);
asm_thumb_lsl_rlo_rlo_i5(as, rlo_dest, rlo_dest, ctz);
} else if (UNSIGNED_FIT16(i32)) {
asm_thumb_mov_rlo_i16(as, rlo_dest, i32);
} else {
if (negate) {
// no point in negating if we're storing in 32 bit anyway
negate = false;
i32 = -i32;
}
asm_thumb_mov_reg_i32(as, rlo_dest, i32);
}
if (negate) {
asm_thumb_neg_rlo_rlo(as, rlo_dest, rlo_dest);
}
}
}
#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))
static void asm_thumb_mov_local_check(asm_thumb_t *as, int word_offset) {
if (as->base.pass >= MP_ASM_PASS_EMIT) {
assert(word_offset >= 0);
if (!UNSIGNED_FIT8(word_offset)) {
mp_raise_NotImplementedError(MP_ERROR_TEXT("too many locals for native method"));
}
}
}
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;
asm_thumb_mov_local_check(as, word_offset);
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;
asm_thumb_mov_local_check(as, word_offset);
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 |= 1; // to stay in Thumb state when jumping to this address
if (asm_thumb_allow_armv7m(as)) {
rel -= 6 + 4; // adjust for mov_reg_i16, sxth_rlo_rlo and then PC+4 prefetch of add_reg_reg
asm_thumb_mov_reg_i16(as, ASM_THUMB_OP_MOVW, rlo_dest, rel); // 4 bytes
asm_thumb_sxth_rlo_rlo(as, rlo_dest, rlo_dest); // 2 bytes
} else {
rel -= 8 + 4; // adjust for four instructions and then PC+4 prefetch of add_reg_reg
// 6 bytes
asm_thumb_mov_rlo_i16(as, rlo_dest, rel);
// 2 bytes - not always needed, but we want to keep the size the same
asm_thumb_sxth_rlo_rlo(as, rlo_dest, rlo_dest);
}
asm_thumb_add_reg_reg(as, rlo_dest, ASM_THUMB_REG_R15); // 2 bytes
}
// ARMv7-M only
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));
}
// emits code for: reg_dest = reg_base + offset << offset_shift
static void asm_thumb_add_reg_reg_offset(asm_thumb_t *as, uint reg_dest, uint reg_base, uint offset, uint offset_shift) {
if (reg_dest < ASM_THUMB_REG_R8 && reg_base < ASM_THUMB_REG_R8) {
if (offset << offset_shift < 256) {
if (reg_dest != reg_base) {
asm_thumb_mov_reg_reg(as, reg_dest, reg_base);
}
asm_thumb_add_rlo_i8(as, reg_dest, offset << offset_shift);
} else if (UNSIGNED_FIT8(offset) && reg_dest != reg_base) {
asm_thumb_mov_rlo_i8(as, reg_dest, offset);
asm_thumb_lsl_rlo_rlo_i5(as, reg_dest, reg_dest, offset_shift);
asm_thumb_add_rlo_rlo_rlo(as, reg_dest, reg_dest, reg_base);
} else if (reg_dest != reg_base) {
asm_thumb_mov_rlo_i16(as, reg_dest, offset << offset_shift);
asm_thumb_add_rlo_rlo_rlo(as, reg_dest, reg_dest, reg_dest);
} else {
uint reg_other = reg_dest ^ 7;
asm_thumb_op16(as, OP_PUSH_RLIST((1 << reg_other)));
asm_thumb_mov_rlo_i16(as, reg_other, offset << offset_shift);
asm_thumb_add_rlo_rlo_rlo(as, reg_dest, reg_dest, reg_other);
asm_thumb_op16(as, OP_POP_RLIST((1 << reg_other)));
}
} else {
assert(0); // should never be called for ARMV6M
}
}
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 if (asm_thumb_allow_armv7m(as)) {
asm_thumb_ldr_reg_reg_i12(as, reg_dest, reg_base, word_offset);
} else {
asm_thumb_add_reg_reg_offset(as, reg_dest, reg_base, word_offset - 31, 2);
asm_thumb_ldr_rlo_rlo_i5(as, reg_dest, reg_dest, 31);
}
}
// ARMv7-M only
static inline void asm_thumb_ldrh_reg_reg_i12(asm_thumb_t *as, uint reg_dest, uint reg_base, uint uint16_offset) {
asm_thumb_op32(as, OP_LDRH_W_HI(reg_base), OP_LDRH_W_LO(reg_dest, uint16_offset * 2));
}
void asm_thumb_ldrh_reg_reg_i12_optimised(asm_thumb_t *as, uint reg_dest, uint reg_base, uint uint16_offset) {
if (reg_dest < ASM_THUMB_REG_R8 && reg_base < ASM_THUMB_REG_R8 && UNSIGNED_FIT5(uint16_offset)) {
asm_thumb_ldrh_rlo_rlo_i5(as, reg_dest, reg_base, uint16_offset);
} else if (asm_thumb_allow_armv7m(as)) {
asm_thumb_ldrh_reg_reg_i12(as, reg_dest, reg_base, uint16_offset);
} else {
asm_thumb_add_reg_reg_offset(as, reg_dest, reg_base, uint16_offset - 31, 1);
asm_thumb_ldrh_rlo_rlo_i5(as, reg_dest, reg_dest, 31);
}
}
// 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));
return;
}
}
// is a large backwards jump, or a forwards jump (that must be assumed large)
if (asm_thumb_allow_armv7m(as)) {
asm_thumb_op32(as, OP_BW_HI(rel), OP_BW_LO(rel));
} else {
if (SIGNED_FIT12(rel)) {
// this code path has to be the same number of instructions irrespective of rel
asm_thumb_op16(as, OP_B_N(rel));
} else {
asm_thumb_op16(as, ASM_THUMB_OP_NOP);
if (dest != (mp_uint_t)-1) {
// we have an actual branch > 12 bits; this is not handled yet
mp_raise_NotImplementedError(MP_ERROR_TEXT("native method too big"));
}
}
}
}
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));
return;
}
}
// is a large backwards jump, or a forwards jump (that must be assumed large)
if (asm_thumb_allow_armv7m(as)) {
asm_thumb_op32(as, OP_BCC_W_HI(cond, rel), OP_BCC_W_LO(rel));
} else {
// reverse the sense of the branch to jump over a longer branch
asm_thumb_op16(as, OP_BCC_N(cond ^ 1, 0));
asm_thumb_b_label(as, label);
}
}
void asm_thumb_bcc_rel9(asm_thumb_t *as, int cond, int rel) {
rel -= 4; // account for instruction prefetch, PC is 4 bytes ahead of this instruction
assert(SIGNED_FIT9(rel));
asm_thumb_op16(as, OP_BCC_N(cond, rel));
}
void asm_thumb_b_rel12(asm_thumb_t *as, int rel) {
rel -= 4; // account for instruction prefetch, PC is 4 bytes ahead of this instruction
assert(SIGNED_FIT12(rel));
asm_thumb_op16(as, OP_B_N(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

View File

@@ -0,0 +1,435 @@
/*
* 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"
#include "py/persistentcode.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;
#if MICROPY_DYNAMIC_COMPILER
static inline bool asm_thumb_allow_armv7m(asm_thumb_t *as) {
return MP_NATIVE_ARCH_ARMV7M <= mp_dynamic_compiler.native_arch
&& mp_dynamic_compiler.native_arch <= MP_NATIVE_ARCH_ARMV7EMDP;
}
#else
static inline bool asm_thumb_allow_armv7m(asm_thumb_t *as) {
return MICROPY_EMIT_THUMB_ARMV7M;
}
#endif
static inline void asm_thumb_end_pass(asm_thumb_t *as) {
(void)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_LDR (0x4800)
#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);
}
static inline void asm_thumb_ldr_rlo_pcrel_i8(asm_thumb_t *as, uint rlo, uint i8) {
asm_thumb_format_3(as, ASM_THUMB_FORMAT_3_LDR, 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);
}
static inline void asm_thumb_mvn_rlo_rlo(asm_thumb_t *as, uint rlo_dest, uint rlo_src) {
asm_thumb_format_4(as, ASM_THUMB_FORMAT_4_MVN, rlo_dest, rlo_src);
}
static inline void asm_thumb_neg_rlo_rlo(asm_thumb_t *as, uint rlo_dest, uint rlo_src) {
asm_thumb_format_4(as, ASM_THUMB_FORMAT_4_NEG, 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 uint16_offset) {
asm_thumb_format_9_10(as, ASM_THUMB_FORMAT_10_STRH, rlo_src, rlo_base, uint16_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 uint16_offset) {
asm_thumb_format_9_10(as, ASM_THUMB_FORMAT_10_LDRH, rlo_dest, rlo_base, uint16_offset);
}
static inline void asm_thumb_lsl_rlo_rlo_i5(asm_thumb_t *as, uint rlo_dest, uint rlo_src, uint shift) {
asm_thumb_format_1(as, ASM_THUMB_FORMAT_1_LSL, rlo_dest, rlo_src, shift);
}
static inline void asm_thumb_asr_rlo_rlo_i5(asm_thumb_t *as, uint rlo_dest, uint rlo_src, uint shift) {
asm_thumb_format_1(as, ASM_THUMB_FORMAT_1_ASR, rlo_dest, rlo_src, shift);
}
// FORMAT 11: sign/zero extend
#define ASM_THUMB_FORMAT_11_ENCODE(op, rlo_dest, rlo_src) \
((op) | ((rlo_src) << 3) | (rlo_dest))
#define ASM_THUMB_FORMAT_11_SXTH (0xb200)
#define ASM_THUMB_FORMAT_11_SXTB (0xb240)
#define ASM_THUMB_FORMAT_11_UXTH (0xb280)
#define ASM_THUMB_FORMAT_11_UXTB (0xb2c0)
static inline void asm_thumb_format_11(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, ASM_THUMB_FORMAT_11_ENCODE(op, rlo_dest, rlo_src));
}
static inline void asm_thumb_sxth_rlo_rlo(asm_thumb_t *as, uint rlo_dest, uint rlo_src) {
asm_thumb_format_11(as, ASM_THUMB_FORMAT_11_SXTH, rlo_dest, rlo_src);
}
// 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);
void 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 word_offset); // convenience
void asm_thumb_ldrh_reg_reg_i12_optimised(asm_thumb_t *as, uint reg_dest, uint reg_base, uint uint16_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
void asm_thumb_bcc_rel9(asm_thumb_t *as, int cc, int rel);
void asm_thumb_b_rel12(asm_thumb_t *as, int rel);
// 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_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_LOAD16_REG_REG_OFFSET(as, reg_dest, reg_base, uint16_offset) asm_thumb_ldrh_reg_reg_i12_optimised((as), (reg_dest), (reg_base), (uint16_offset))
#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

View File

@@ -0,0 +1,632 @@
/*
* 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) {
if (src_r64 < 8 && 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_FROM_R64(dest_r64) | REX_B_FROM_R64(src_r64), 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) {
if (src_r64 < 8 && 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_FROM_R64(dest_r64) | REX_B_FROM_R64(src_r64), 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) {
if (src_r64 < 8 && 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_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);
}
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
asm_x64_write_byte_2(as, REX_PREFIX | REX_W | REX_R_FROM_R64(dest_r64) | REX_B_FROM_R64(src_r64), 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

View File

@@ -0,0 +1,221 @@
/*
* 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_LOAD16_REG_REG_OFFSET(as, reg_dest, reg_base, uint16_offset) asm_x64_mov_mem16_to_r64zx((as), (reg_base), 2 * (uint16_offset), (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

View File

@@ -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

View File

@@ -0,0 +1,216 @@
/*
* 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_LOAD16_REG_REG_OFFSET(as, reg_dest, reg_base, uint16_offset) asm_x86_mov_mem16_to_r32zx((as), (reg_base), 2 * (uint16_offset), (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

View File

@@ -0,0 +1,265 @@
/*
* 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/runtime.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_l32i_optimised(asm_xtensa_t *as, uint reg_dest, uint reg_base, uint word_offset) {
if (word_offset < 16) {
asm_xtensa_op_l32i_n(as, reg_dest, reg_base, word_offset);
} else if (word_offset < 256) {
asm_xtensa_op_l32i(as, reg_dest, reg_base, word_offset);
} else {
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("asm overflow"));
}
}
void asm_xtensa_s32i_optimised(asm_xtensa_t *as, uint reg_src, uint reg_base, uint word_offset) {
if (word_offset < 16) {
asm_xtensa_op_s32i_n(as, reg_src, reg_base, word_offset);
} else if (word_offset < 256) {
asm_xtensa_op_s32i(as, reg_src, reg_base, word_offset);
} else {
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("asm overflow"));
}
}
void asm_xtensa_call_ind(asm_xtensa_t *as, uint idx) {
asm_xtensa_l32i_optimised(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) {
asm_xtensa_l32i_optimised(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

View File

@@ -0,0 +1,411 @@
/*
* 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_l32i_optimised(asm_xtensa_t *as, uint reg_dest, uint reg_base, uint word_offset);
void asm_xtensa_s32i_optimised(asm_xtensa_t *as, uint reg_src, uint reg_base, uint word_offset);
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_l32i_optimised((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_LOAD16_REG_REG_OFFSET(as, reg_dest, reg_base, uint16_offset) asm_xtensa_op_l16ui((as), (reg_dest), (reg_base), (uint16_offset))
#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_s32i_optimised((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

View File

@@ -0,0 +1,339 @@
/*
* 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/bc0.h"
#include "py/bc.h"
#include "py/objfun.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
void mp_encode_uint(void *env, mp_encode_uint_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[MP_ENCODE_UINT_MAX_BYTES];
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(env, buf + sizeof(buf) - p);
if (c != NULL) {
while (p != buf + sizeof(buf) - 1) {
*c++ = *p++ | 0x80;
}
*c = *p;
}
}
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;
}
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 a pointer to the beginning of the prelude
// - code_state->sp should be: &code_state->state[0] - 1
// - code_state->n_state should be the number of objects in the local state
STATIC void mp_setup_code_state_helper(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;
// 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);
MP_BC_PRELUDE_SIZE_DECODE(code_state->ip);
(void)n_state_unused;
(void)n_exc_stack_unused;
mp_obj_t *code_state_state = code_state->sp + 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;
}
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];
// get pointer to arg_names array
const uint8_t *arg_names = code_state->ip;
arg_names = mp_decode_uint_skip(arg_names);
for (size_t j = 0; j < n_pos_args + n_kwonly_args; j++) {
qstr arg_qstr = mp_decode_uint(&arg_names);
#if MICROPY_EMIT_BYTECODE_USES_QSTR_TABLE
arg_qstr = self->context->constants.qstr_table[arg_qstr];
#endif
if (wanted_arg_name == MP_OBJ_NEW_QSTR(arg_qstr)) {
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
const uint8_t *arg_names = mp_decode_uint_skip(code_state->ip);
for (size_t i = 0; i < n_pos_args; i++) {
arg_names = mp_decode_uint_skip(arg_names);
}
for (size_t i = 0; i < n_kwonly_args; i++) {
qstr arg_qstr = mp_decode_uint(&arg_names);
#if MICROPY_EMIT_BYTECODE_USES_QSTR_TABLE
arg_qstr = self->context->constants.qstr_table[arg_qstr];
#endif
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, MP_OBJ_NEW_QSTR(arg_qstr), 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'"), arg_qstr);
}
}
}
} 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);
}
}
// jump over code info (source file, argument names and line-number mapping)
const uint8_t *ip = code_state->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]);
}
// 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);
}
// 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->n_state should be the number of objects in the local state
void mp_setup_code_state(mp_code_state_t *code_state, size_t n_args, size_t n_kw, const mp_obj_t *args) {
code_state->ip = code_state->fun_bc->bytecode;
code_state->sp = &code_state->state[0] - 1;
#if MICROPY_STACKLESS
code_state->prev = NULL;
#endif
#if MICROPY_PY_SYS_SETTRACE
code_state->prev_state = NULL;
code_state->frame = NULL;
#endif
mp_setup_code_state_helper(code_state, n_args, n_kw, args);
}
#if MICROPY_EMIT_NATIVE
// 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 a pointer to the beginning of the prelude
// - code_state->n_state should be the number of objects in the local state
void mp_setup_code_state_native(mp_code_state_native_t *code_state, size_t n_args, size_t n_kw, const mp_obj_t *args) {
code_state->sp = &code_state->state[0] - 1;
mp_setup_code_state_helper((mp_code_state_t *)code_state, n_args, n_kw, args);
}
#endif

View File

@@ -0,0 +1,338 @@
/*
* 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"
// 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
// argname0 : var qstr
// ... : var qstr
// argnameN : var qstr N = num_pos_args + num_kwonly_args - 1
// <line number info>
//
// closure section:
// local_num0 : byte
// ... : byte
// local_numN : byte N = n_cells-1
//
// <bytecode>
//
//
// constant table layout:
//
// const0 : obj
// constN : obj
#define MP_ENCODE_UINT_MAX_BYTES ((MP_BYTES_PER_OBJ_WORD * 8 + 6) / 7)
#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); \
(void)n_state; (void)n_exc_stack; (void)scope_flags; \
(void)n_pos_args; (void)n_kwonly_args; (void)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); \
(void)n_info; (void)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_idx;
const byte *line_info;
const byte *line_info_top;
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;
// Constants associated with a module, to interface bytecode with runtime.
typedef struct _mp_module_constants_t {
#if MICROPY_EMIT_BYTECODE_USES_QSTR_TABLE
qstr_short_t *qstr_table;
#else
qstr source_file;
#endif
mp_obj_t *obj_table;
} mp_module_constants_t;
// State associated with a module.
typedef struct _mp_module_context_t {
mp_obj_module_t module;
mp_module_constants_t constants;
} mp_module_context_t;
// Outer level struct defining a compiled module.
typedef struct _mp_compiled_module_t {
const mp_module_context_t *context;
const struct _mp_raw_code_t *rc;
#if MICROPY_PERSISTENT_CODE_SAVE
bool has_native;
size_t n_qstr;
size_t n_obj;
#endif
} mp_compiled_module_t;
// Outer level struct defining a frozen module.
typedef struct _mp_frozen_module_t {
const mp_module_constants_t constants;
const struct _mp_raw_code_t *rc;
} mp_frozen_module_t;
// State for an executing function.
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.
struct _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;
// State for an executing native function (based on mp_code_state_t).
typedef struct _mp_code_state_native_t {
struct _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;
mp_obj_t state[0];
} mp_code_state_native_t;
// Allocator may return NULL, in which case data is not stored (can be used to compute size).
typedef uint8_t *(*mp_encode_uint_allocator_t)(void *env, size_t nbytes);
void mp_encode_uint(void *env, mp_encode_uint_allocator_t allocator, mp_uint_t val);
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,
#ifndef __cplusplus
volatile
#endif
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_setup_code_state_native(mp_code_state_native_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 struct _mp_raw_code_t *rc, const mp_module_constants_t *cm);
void mp_bytecode_print2(const mp_print_t *print, const byte *ip, size_t len, struct _mp_raw_code_t *const *child_table, const mp_module_constants_t *cm);
const byte *mp_bytecode_print_str(const mp_print_t *print, const byte *ip_start, const byte *ip, struct _mp_raw_code_t *const *child_table, const mp_module_constants_t *cm);
#define mp_bytecode_print_inst(print, code, x_table) mp_bytecode_print2(print, code, 1, x_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)))
static inline void mp_module_context_alloc_tables(mp_module_context_t *context, size_t n_qstr, size_t n_obj) {
#if MICROPY_EMIT_BYTECODE_USES_QSTR_TABLE
size_t nq = (n_qstr * sizeof(qstr_short_t) + sizeof(mp_uint_t) - 1) / sizeof(mp_uint_t);
size_t no = n_obj;
mp_uint_t *mem = m_new(mp_uint_t, nq + no);
context->constants.qstr_table = (qstr_short_t *)mem;
context->constants.obj_table = (mp_obj_t *)(mem + nq);
#else
if (n_obj == 0) {
context->constants.obj_table = NULL;
} else {
context->constants.obj_table = m_new(mp_obj_t, n_obj);
}
#endif
}
static inline size_t mp_bytecode_get_source_line(const byte *line_info, const byte *line_info_top, size_t bc_offset) {
size_t source_line = 1;
while (line_info < line_info_top) {
size_t 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

View File

@@ -0,0 +1,162 @@
/*
* 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
// All opcodes are encoded as a byte with an optional argument. Arguments are
// variable-length encoded so they can be as small as possible. The possible
// encodings for arguments are (ip[0] is the opcode):
//
// - unsigned relative bytecode offset:
// - if ip[1] high bit is clear then: arg = ip[1]
// - if ip[1] high bit is set then: arg = ip[1] & 0x7f | ip[2] << 7
//
// - signed relative bytecode offset:
// - if ip[1] high bit is clear then: arg = ip[1] - 0x40
// - if ip[1] high bit is set then: arg = (ip[1] & 0x7f | ip[2] << 7) - 0x4000
#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_UNWIND_JUMP (MP_BC_BASE_JUMP_E + 0x00) // signed relative bytecode offset; then a byte
#define MP_BC_JUMP (MP_BC_BASE_JUMP_E + 0x02) // signed relative bytecode offset
#define MP_BC_POP_JUMP_IF_TRUE (MP_BC_BASE_JUMP_E + 0x03) // signed relative bytecode offset
#define MP_BC_POP_JUMP_IF_FALSE (MP_BC_BASE_JUMP_E + 0x04) // signed relative bytecode offset
#define MP_BC_JUMP_IF_TRUE_OR_POP (MP_BC_BASE_JUMP_E + 0x05) // unsigned relative bytecode offset
#define MP_BC_JUMP_IF_FALSE_OR_POP (MP_BC_BASE_JUMP_E + 0x06) // unsigned relative bytecode offset
#define MP_BC_SETUP_WITH (MP_BC_BASE_JUMP_E + 0x07) // unsigned relative bytecode offset
#define MP_BC_SETUP_EXCEPT (MP_BC_BASE_JUMP_E + 0x08) // unsigned relative bytecode offset
#define MP_BC_SETUP_FINALLY (MP_BC_BASE_JUMP_E + 0x09) // unsigned relative bytecode offset
#define MP_BC_POP_EXCEPT_JUMP (MP_BC_BASE_JUMP_E + 0x0a) // unsigned relative bytecode offset
#define MP_BC_FOR_ITER (MP_BC_BASE_JUMP_E + 0x0b) // unsigned relative bytecode offset
#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

View File

@@ -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;
}
unsigned 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 (MP_BYTES_PER_OBJ_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 (MP_BYTES_PER_OBJ_WORD < 8 && size > sizeof(val)) {
int c = (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;
}
}

View File

@@ -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

View File

@@ -0,0 +1,165 @@
/*
* 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"
typedef enum {
MP_IMPORT_STAT_NO_EXIST,
MP_IMPORT_STAT_DIR,
MP_IMPORT_STAT_FILE,
} mp_import_stat_t;
#if MICROPY_VFS
// Delegate to the VFS for import stat and builtin open.
#define mp_builtin_open_obj mp_vfs_open_obj
mp_import_stat_t mp_vfs_import_stat(const char *path);
mp_obj_t mp_vfs_open(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs);
MP_DECLARE_CONST_FUN_OBJ_KW(mp_vfs_open_obj);
static inline mp_import_stat_t mp_import_stat(const char *path) {
return mp_vfs_import_stat(path);
}
static inline mp_obj_t mp_builtin_open(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) {
return mp_vfs_open(n_args, args, kwargs);
}
#else
// A port can provide implementations of these functions.
mp_import_stat_t mp_import_stat(const char *path);
mp_obj_t mp_builtin_open(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs);
// A port can provide this object.
MP_DECLARE_CONST_FUN_OBJ_KW(mp_builtin_open_obj);
#endif
mp_obj_t mp_builtin___import__(size_t n_args, const mp_obj_t *args);
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);
MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_builtin_input_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_uos;
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 mp_obj_module_t mp_module_uplatform;
extern const char MICROPY_PY_BUILTINS_HELP_TEXT[];
#endif // MICROPY_INCLUDED_PY_BUILTIN_H

View File

@@ -0,0 +1,173 @@
/*
* 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);
((mp_module_context_t *)fun_bc->context)->module.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 = mp_obj_malloc(mp_obj_code_t, &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

View File

@@ -0,0 +1,172 @@
/*
* 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 len = strlen(name);
// name should end in '.py' and we strip it off
mp_obj_list_append(list, mp_obj_new_str(name, len - 3));
name += len + 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
extern const char mp_frozen_names[];
mp_help_add_from_names(list, mp_frozen_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

View File

@@ -0,0 +1,604 @@
/*
* 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
* Copyright (c) 2021 Jim Mussared
*
* 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
// Must be a string of one byte.
#define PATH_SEP_CHAR "/"
// Virtual sys.path entry that maps to the frozen modules.
#define MP_FROZEN_PATH_PREFIX ".frozen/"
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;
}
// Wrapper for mp_import_stat (which is provided by the port, and typically
// uses mp_vfs_import_stat) to also search frozen modules. Given an exact
// path to a file or directory (e.g. "foo/bar", foo/bar.py" or "foo/bar.mpy"),
// will return whether the path is a file, directory, or doesn't exist.
STATIC mp_import_stat_t stat_path_or_frozen(const char *path) {
#if MICROPY_MODULE_FROZEN
// Only try and load as a frozen module if it starts with .frozen/.
const int frozen_path_prefix_len = strlen(MP_FROZEN_PATH_PREFIX);
if (strncmp(path, MP_FROZEN_PATH_PREFIX, frozen_path_prefix_len) == 0) {
return mp_find_frozen_module(path + frozen_path_prefix_len, NULL, NULL);
}
#endif
return mp_import_stat(path);
}
// Given a path to a .py file, try and find this path as either a .py or .mpy
// in either the filesystem or frozen modules.
STATIC mp_import_stat_t stat_file_py_or_mpy(vstr_t *path) {
mp_import_stat_t stat = stat_path_or_frozen(vstr_null_terminated_str(path));
if (stat == MP_IMPORT_STAT_FILE) {
return stat;
}
#if MICROPY_PERSISTENT_CODE_LOAD
// Didn't find .py -- try the .mpy instead by inserting an 'm' into the '.py'.
vstr_ins_byte(path, path->len - 2, 'm');
stat = stat_path_or_frozen(vstr_null_terminated_str(path));
if (stat == MP_IMPORT_STAT_FILE) {
return stat;
}
#endif
return MP_IMPORT_STAT_NO_EXIST;
}
// Given an import path (e.g. "foo/bar"), try and find "foo/bar" (a directory)
// or "foo/bar.(m)py" in either the filesystem or frozen modules.
STATIC mp_import_stat_t stat_dir_or_file(vstr_t *path) {
mp_import_stat_t stat = stat_path_or_frozen(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);
}
// Given a top-level module, try and find it in each of the sys.path entries
// via stat_dir_or_file.
STATIC mp_import_stat_t stat_top_level_dir_or_file(qstr mod_name, vstr_t *dest) {
DEBUG_printf("stat_top_level_dir_or_file: '%s'\n", qstr_str(mod_name));
#if MICROPY_PY_SYS
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[0]);
}
vstr_add_str(dest, qstr_str(mod_name));
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 (or not enabled), so just stat the given path
// directly.
vstr_add_str(dest, qstr_str(mod_name));
return stat_dir_or_file(dest);
}
#if MICROPY_MODULE_FROZEN_STR || MICROPY_ENABLE_COMPILER
STATIC void do_load_from_lexer(mp_module_context_t *context, mp_lexer_t *lex) {
#if MICROPY_PY___FILE__
qstr source_name = lex->source_name;
mp_store_attr(MP_OBJ_FROM_PTR(&context->module), 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 = context->module.globals;
mp_parse_compile_execute(lex, MP_PARSE_FILE_INPUT, mod_globals, mod_globals);
}
#endif
#if (MICROPY_HAS_FILE_READER && MICROPY_PERSISTENT_CODE_LOAD) || MICROPY_MODULE_FROZEN_MPY
STATIC void do_execute_raw_code(mp_module_context_t *context, const mp_raw_code_t *rc, const mp_module_context_t *mc, const char *source_name) {
(void)source_name;
#if MICROPY_PY___FILE__
mp_store_attr(MP_OBJ_FROM_PTR(&context->module), 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 = context->module.globals;
// 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(rc, mc, 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_module_context_t *module_obj, vstr_t *file) {
#if MICROPY_MODULE_FROZEN || MICROPY_ENABLE_COMPILER || (MICROPY_PERSISTENT_CODE_LOAD && MICROPY_HAS_FILE_READER)
const 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;
const int frozen_path_prefix_len = strlen(MP_FROZEN_PATH_PREFIX);
if (strncmp(file_str, MP_FROZEN_PATH_PREFIX, frozen_path_prefix_len) == 0) {
mp_find_frozen_module(file_str + frozen_path_prefix_len, &frozen_type, &modref);
// 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) {
const mp_frozen_module_t *frozen = modref;
module_obj->constants = frozen->constants;
do_execute_raw_code(module_obj, frozen->rc, module_obj, file_str + frozen_path_prefix_len);
return;
}
#endif
}
#endif // MICROPY_MODULE_FROZEN
// 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_compiled_module_t cm = mp_raw_code_load_file(file_str, module_obj);
do_execute_raw_code(module_obj, cm.rc, cm.context, 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
}
// Convert a relative (to the current module) import, going up "level" levels,
// into an absolute import.
STATIC void evaluate_relative_import(mp_int_t level, const char **module_name, size_t *module_name_len) {
// What we want to do here is to take the name of the current module,
// remove <level> trailing components, and concatenate the passed-in
// module name.
// For example, level=3, module_name="foo.bar", __name__="a.b.c.d" --> "a.foo.bar"
// "Relative imports use a module's __name__ attribute to determine that
// module's position in the package hierarchy."
// http://legacy.python.org/dev/peps/pep-0328/#relative-imports-and-name
mp_obj_t current_module_name_obj = mp_obj_dict_get(MP_OBJ_FROM_PTR(mp_globals_get()), MP_OBJ_NEW_QSTR(MP_QSTR___name__));
assert(current_module_name_obj != MP_OBJ_NULL);
#if MICROPY_MODULE_OVERRIDE_MAIN_IMPORT && MICROPY_CPYTHON_COMPAT
if (MP_OBJ_QSTR_VALUE(current_module_name_obj) == MP_QSTR___main__) {
// This is a module loaded by -m command-line switch (e.g. unix port),
// and so its __name__ has been set to "__main__". Get its real name
// that we stored during import in the __main__ attribute.
current_module_name_obj = mp_obj_dict_get(MP_OBJ_FROM_PTR(mp_globals_get()), MP_OBJ_NEW_QSTR(MP_QSTR___main__));
}
#endif
// If we have a __path__ in the globals dict, then we're a package.
bool is_pkg = mp_map_lookup(&mp_globals_get()->map, MP_OBJ_NEW_QSTR(MP_QSTR___path__), MP_MAP_LOOKUP);
#if DEBUG_PRINT
DEBUG_printf("Current module/package: ");
mp_obj_print_helper(MICROPY_DEBUG_PRINTER, current_module_name_obj, PRINT_REPR);
DEBUG_printf(", is_package: %d", is_pkg);
DEBUG_printf("\n");
#endif
size_t current_module_name_len;
const char *current_module_name = mp_obj_str_get_data(current_module_name_obj, &current_module_name_len);
const char *p = current_module_name + current_module_name_len;
if (is_pkg) {
// If we're evaluating relative to a package, then take off one fewer
// level (i.e. the relative search starts inside the package, rather
// than as a sibling of the package).
--level;
}
// Walk back 'level' dots (or run out of path).
while (level && p > current_module_name) {
if (*--p == '.') {
--level;
}
}
// We must have some component left over to import from.
if (p == current_module_name) {
mp_raise_msg(&mp_type_ImportError, MP_ERROR_TEXT("can't perform relative import"));
}
// New length is len("<chopped path>.<module_name>"). Note: might be one byte
// more than we need if module_name is empty (for the extra . we will
// append).
uint new_module_name_len = (size_t)(p - current_module_name) + 1 + *module_name_len;
char *new_mod = mp_local_alloc(new_module_name_len);
memcpy(new_mod, current_module_name, p - current_module_name);
// Only append ".<module_name>" if there was one).
if (*module_name_len != 0) {
new_mod[p - current_module_name] = '.';
memcpy(new_mod + (p - current_module_name) + 1, *module_name, *module_name_len);
} else {
--new_module_name_len;
}
// Copy into a QSTR.
qstr new_mod_q = qstr_from_strn(new_mod, new_module_name_len);
mp_local_free(new_mod);
DEBUG_printf("Resolved base name for relative import: '%s'\n", qstr_str(new_mod_q));
*module_name = qstr_str(new_mod_q);
*module_name_len = new_module_name_len;
}
// Load a module at the specified absolute path, possibly as a submodule of the given outer module.
// full_mod_name: The full absolute path to this module (e.g. "foo.bar.baz").
// level_mod_name: The final component of the path (e.g. "baz").
// outer_module_obj: The parent module (we need to store this module as an
// attribute on it) (or MP_OBJ_NULL for top-level).
// path: The filesystem path where we found the parent module
// (or empty for a top level module).
// override_main: Whether to set the __name__ to "__main__" (and use __main__
// for the actual path).
STATIC mp_obj_t process_import_at_level(qstr full_mod_name, qstr level_mod_name, mp_obj_t outer_module_obj, vstr_t *path, bool override_main) {
mp_import_stat_t stat = MP_IMPORT_STAT_NO_EXIST;
// Exact-match of built-in (or already-loaded) takes priority.
mp_obj_t module_obj = mp_module_get_loaded_or_builtin(full_mod_name);
// Even if we find the module, go through the motions of searching for it
// because we may actually be in the process of importing a sub-module.
// So we need to (re-)find the correct path to be finding the sub-module
// on the next iteration of process_import_at_level.
if (outer_module_obj == MP_OBJ_NULL) {
DEBUG_printf("Searching for top-level module\n");
// First module in the dotted-name; search for a directory or file
// relative to all the locations in sys.path.
stat = stat_top_level_dir_or_file(full_mod_name, path);
// If the module "foo" doesn't exist on the filesystem, and it's not a
// builtin, try and find "ufoo" as a built-in. (This feature was
// formerly known as "weak links").
#if MICROPY_MODULE_WEAK_LINKS
if (stat == MP_IMPORT_STAT_NO_EXIST && module_obj == MP_OBJ_NULL) {
char *umodule_buf = vstr_str(path);
umodule_buf[0] = 'u';
strcpy(umodule_buf + 1, qstr_str(level_mod_name));
qstr umodule_name = qstr_from_str(umodule_buf);
module_obj = mp_module_get_builtin(umodule_name);
}
#elif MICROPY_PY_SYS
if (stat == MP_IMPORT_STAT_NO_EXIST && module_obj == MP_OBJ_NULL && level_mod_name == MP_QSTR_sys) {
module_obj = MP_OBJ_FROM_PTR(&mp_module_sys);
}
#endif
} else {
DEBUG_printf("Searching for sub-module\n");
// Add the current part of the module name to the path.
vstr_add_char(path, PATH_SEP_CHAR[0]);
vstr_add_str(path, qstr_str(level_mod_name));
// Because it's not top level, we already know which path the parent was found in.
stat = stat_dir_or_file(path);
}
DEBUG_printf("Current path: %.*s\n", (int)vstr_len(path), vstr_str(path));
if (module_obj == MP_OBJ_NULL) {
// Not a built-in and not already-loaded.
if (stat == MP_IMPORT_STAT_NO_EXIST) {
// And the file wasn't found -- 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'"), full_mod_name);
#endif
}
// Not a built-in but found on the filesystem, try and load it.
DEBUG_printf("Found path: %.*s\n", (int)vstr_len(path), vstr_str(path));
// Prepare for loading from the filesystem. Create a new shell module.
module_obj = mp_obj_new_module(full_mod_name);
#if MICROPY_MODULE_OVERRIDE_MAIN_IMPORT
// If this module is being loaded via -m on unix, then
// override __name__ to "__main__". 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 the package name itself, which is
// important for future imports).
if (override_main && 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. Need this for
// resolving relative imports later. "__main__ was 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(full_mod_name));
#endif
}
#endif // MICROPY_MODULE_OVERRIDE_MAIN_IMPORT
if (stat == MP_IMPORT_STAT_DIR) {
// Directory -- execute "path/__init__.py".
DEBUG_printf("%.*s is dir\n", (int)vstr_len(path), vstr_str(path));
// Store the __path__ attribute onto this module.
// 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_str(path, PATH_SEP_CHAR "__init__.py");
if (stat_file_py_or_mpy(path) == MP_IMPORT_STAT_FILE) {
do_load(MP_OBJ_TO_PTR(module_obj), path);
} else {
// No-op. Nothing to load.
// mp_warning("%s is imported as namespace package", vstr_str(&path));
}
// Remove /__init__.py suffix.
path->len = orig_path_len;
} else { // MP_IMPORT_STAT_FILE
// File -- execute "path.(m)py".
do_load(MP_OBJ_TO_PTR(module_obj), path);
// Note: 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) {
// If it's a sub-module (not a built-in one), then make it available on
// the parent module.
mp_store_attr(outer_module_obj, level_mod_name, module_obj);
}
return module_obj;
}
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_helper(MICROPY_DEBUG_PRINTER, args[i], PRINT_REPR);
DEBUG_printf("\n");
}
#endif
// This is the import path, with any leading dots stripped.
// "import foo.bar" --> module_name="foo.bar"
// "from foo.bar import baz" --> module_name="foo.bar"
// "from . import foo" --> module_name=""
// "from ...foo.bar import baz" --> module_name="foo.bar"
mp_obj_t module_name_obj = args[0];
// These are the imported names.
// i.e. "from foo.bar import baz, zap" --> fromtuple=("baz", "zap",)
// Note: There's a special case on the Unix port, where this is set to mp_const_false which means that it's __main__.
mp_obj_t fromtuple = mp_const_none;
// Level is the number of leading dots in a relative import.
// i.e. "from . import foo" --> level=1
// i.e. "from ...foo.bar import baz" --> level=3
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 module_name_len;
const char *module_name = mp_obj_str_get_data(module_name_obj, &module_name_len);
if (level != 0) {
// Turn "foo.bar" into "<current module minus 3 components>.foo.bar".
evaluate_relative_import(level, &module_name, &module_name_len);
}
if (module_name_len == 0) {
mp_raise_ValueError(NULL);
}
DEBUG_printf("Starting module search for '%s'\n", module_name);
VSTR_FIXED(path, MICROPY_ALLOC_PATH_MAX)
mp_obj_t top_module_obj = MP_OBJ_NULL;
mp_obj_t outer_module_obj = MP_OBJ_NULL;
// Search for the end of each component.
size_t current_component_start = 0;
for (size_t i = 1; i <= module_name_len; i++) {
if (i == module_name_len || module_name[i] == '.') {
// The module name up to this depth (e.g. foo.bar.baz).
qstr full_mod_name = qstr_from_strn(module_name, i);
// The current level name (e.g. baz).
qstr level_mod_name = qstr_from_strn(module_name + current_component_start, i - current_component_start);
DEBUG_printf("Processing module: '%s' at level '%s'\n", qstr_str(full_mod_name), qstr_str(level_mod_name));
DEBUG_printf("Previous path: =%.*s=\n", (int)vstr_len(&path), vstr_str(&path));
#if MICROPY_MODULE_OVERRIDE_MAIN_IMPORT
// On unix, if this is being loaded via -m (magic mp_const_false),
// then handle that if it's the final component.
bool override_main = (i == module_name_len && fromtuple == mp_const_false);
#else
bool override_main = false;
#endif
// Import this module.
mp_obj_t module_obj = process_import_at_level(full_mod_name, level_mod_name, outer_module_obj, &path, override_main);
// Set this as the parent module, and remember the top-level module if it's the first.
outer_module_obj = module_obj;
if (top_module_obj == MP_OBJ_NULL) {
top_module_obj = module_obj;
}
current_component_start = i + 1;
}
}
if (fromtuple != mp_const_none) {
// If fromtuple is not empty, return leaf module
return outer_module_obj;
} else {
// 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_loaded_or_builtin(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
char umodule_buf[MICROPY_ALLOC_PATH_MAX];
umodule_buf[0] = 'u';
strcpy(umodule_buf + 1, args[0]);
qstr umodule_name_qstr = qstr_from_str(umodule_buf);
module_obj = mp_module_get_loaded_or_builtin(umodule_name_qstr);
if (module_obj != MP_OBJ_NULL) {
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

View File

@@ -0,0 +1,46 @@
/*
* 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_globals_get() will be used for the context
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_compiled_module_t mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_file, bool is_repl, mp_module_context_t *globals);
#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

View File

@@ -0,0 +1,292 @@
/*
* 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/objfun.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_bytes
#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_NoneType (*mp_obj_get_type(mp_const_none))
#define mp_type_bool (*mp_obj_get_type(mp_const_false))
#define mp_type_int (*(mp_obj_type_t *)(mp_load_global(MP_QSTR_int)))
#define mp_type_str (*mp_fun_table.type_str)
#define mp_type_bytes (*(mp_obj_type_t *)(mp_load_global(MP_QSTR_bytes)))
#define mp_type_tuple (*((mp_obj_base_t *)mp_const_empty_tuple)->type)
#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_bytes (mp_type_bytes.make_new(NULL, 0, 0, NULL))
#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_new_dict(n) (mp_fun_table.new_dict((n)))
#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_get_array(o, len, items) (mp_obj_get_array_dyn((o), (len), (items)))
#define mp_obj_list_append(list, item) (mp_fun_table.list_append((list), (item)))
#define mp_obj_dict_store(dict, key, val) (mp_fun_table.dict_store((dict), (key), (val)))
#define mp_obj_malloc_helper(n, t) (mp_obj_malloc_helper_dyn(n, t))
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);
}
static inline void *mp_obj_malloc_helper_dyn(size_t num_bytes, const mp_obj_type_t *type) {
mp_obj_base_t *base = (mp_obj_base_t *)m_malloc(num_bytes);
base->type = type;
return base;
}
/******************************************************************************/
// 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, context, def_args) \
(mp_fun_table.make_function_from_raw_code((rc), (context), (def_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->context->module.globals); \
mp_raw_code_t rc; \
rc.kind = MP_CODE_NATIVE_VIPER; \
rc.scope_flags = 0; \
(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), self->context, 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_type_arg(type, arg) (mp_raise_dyn(mp_obj_new_exception_arg1_dyn((type), (arg))))
#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
/******************************************************************************/
// Inline function definitions.
// *items may point inside a GC block
static inline void mp_obj_get_array_dyn(mp_obj_t o, size_t *len, mp_obj_t **items) {
const mp_obj_type_t *type = mp_obj_get_type(o);
if (type == &mp_type_tuple) {
mp_obj_tuple_t *t = MP_OBJ_TO_PTR(o);
*len = t->len;
*items = &t->items[0];
} else if (type == &mp_type_list) {
mp_obj_list_t *l = MP_OBJ_TO_PTR(o);
*len = l->len;
*items = l->items;
} else {
mp_raise_TypeError("expected tuple/list");
}
}
#endif // MICROPY_INCLUDED_PY_DYNRUNTIME_H

View File

@@ -0,0 +1,150 @@
# 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
MICROPY_FLOAT_IMPL ?= double
else ifeq ($(ARCH),x64)
# x64
CROSS =
CFLAGS += -fno-stack-protector
MICROPY_FLOAT_IMPL ?= double
else ifeq ($(ARCH),armv6m)
# thumb
CROSS = arm-none-eabi-
CFLAGS += -mthumb -mcpu=cortex-m0
MICROPY_FLOAT_IMPL ?= none
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 $@ $^

View File

@@ -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_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 (may be run multiple times if the emitter requests it)
} 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_common_t {
pass_kind_t pass;
uint16_t ct_cur_child;
mp_raw_code_t **children;
#if MICROPY_EMIT_BYTECODE_USES_QSTR_TABLE
mp_map_t qstr_map;
#endif
mp_obj_list_t const_obj_list;
} mp_emit_common_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_emit_common_t * emit_common, 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);
bool (*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;
#if MICROPY_EMIT_BYTECODE_USES_QSTR_TABLE
qstr_short_t mp_emit_common_use_qstr(mp_emit_common_t *emit, qstr qst);
#else
static inline qstr_short_t mp_emit_common_use_qstr(mp_emit_common_t *emit, qstr qst) {
return qst;
}
#endif
size_t mp_emit_common_use_const_obj(mp_emit_common_t *emit, mp_obj_t const_obj);
static inline size_t mp_emit_common_alloc_const_child(mp_emit_common_t *emit, mp_raw_code_t *rc) {
if (emit->pass == MP_PASS_EMIT) {
emit->children[emit->ct_cur_child] = rc;
}
return emit->ct_cur_child++;
}
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(mp_emit_common_t *emit_common);
emit_t *emit_native_x64_new(mp_emit_common_t *emit_common, mp_obj_t *error_slot, uint *label_slot, mp_uint_t max_num_labels);
emit_t *emit_native_x86_new(mp_emit_common_t *emit_common, mp_obj_t *error_slot, uint *label_slot, mp_uint_t max_num_labels);
emit_t *emit_native_thumb_new(mp_emit_common_t *emit_common, mp_obj_t *error_slot, uint *label_slot, mp_uint_t max_num_labels);
emit_t *emit_native_arm_new(mp_emit_common_t *emit_common, mp_obj_t *error_slot, uint *label_slot, mp_uint_t max_num_labels);
emit_t *emit_native_xtensa_new(mp_emit_common_t *emit_common, mp_obj_t *error_slot, uint *label_slot, mp_uint_t max_num_labels);
emit_t *emit_native_xtensawin_new(mp_emit_common_t *emit_common, 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);
bool 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

View File

@@ -0,0 +1,888 @@
/*
* 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 <unistd.h>
#include <assert.h>
#include "py/mpstate.h"
#include "py/smallint.h"
#include "py/emit.h"
#include "py/bc0.h"
#if MICROPY_ENABLE_COMPILER
#define DUMMY_DATA_SIZE (MP_ENCODE_UINT_MAX_BYTES)
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;
mp_emit_common_t *emit_common;
scope_t *scope;
mp_uint_t last_source_line_offset;
mp_uint_t last_source_line;
size_t max_num_labels;
size_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
bool overflow;
size_t n_info;
size_t n_cell;
};
emit_t *emit_bc_new(mp_emit_common_t *emit_common) {
emit_t *emit = m_new0(emit_t, 1);
emit->emit_common = emit_common;
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(size_t, emit->max_num_labels);
}
void emit_bc_free(emit_t *emit) {
m_del(size_t, emit->label_offsets, emit->max_num_labels);
m_del_obj(emit_t, emit);
}
// all functions must go through this one to emit code info
STATIC uint8_t *emit_get_cur_to_write_code_info(void *emit_in, size_t num_bytes_to_write) {
emit_t *emit = emit_in;
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) {
mp_encode_uint(emit, emit_get_cur_to_write_code_info, mp_emit_common_use_qstr(emit->emit_common, qst));
}
#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 uint8_t *emit_get_cur_to_write_bytecode(void *emit_in, size_t num_bytes_to_write) {
emit_t *emit = emit_in;
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 mp_encode_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[MP_ENCODE_UINT_MAX_BYTES];
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);
mp_encode_uint(emit, emit_get_cur_to_write_bytecode, val);
}
STATIC void emit_write_bytecode_byte_const(emit_t *emit, int stack_adj, byte b, mp_uint_t n) {
emit_write_bytecode_byte_uint(emit, stack_adj, b, n);
}
STATIC void emit_write_bytecode_byte_qstr(emit_t *emit, int stack_adj, byte b, qstr qst) {
emit_write_bytecode_byte_uint(emit, stack_adj, b, mp_emit_common_use_qstr(emit->emit_common, qst));
}
STATIC void emit_write_bytecode_byte_obj(emit_t *emit, int stack_adj, byte b, mp_obj_t obj) {
emit_write_bytecode_byte_const(emit, stack_adj, b, mp_emit_common_use_const_obj(emit->emit_common, obj));
}
STATIC void emit_write_bytecode_byte_child(emit_t *emit, int stack_adj, byte b, mp_raw_code_t *rc) {
emit_write_bytecode_byte_const(emit, stack_adj, b,
mp_emit_common_alloc_const_child(emit->emit_common, rc));
#if MICROPY_PY_SYS_SETTRACE
rc->line_of_definition = emit->last_source_line;
#endif
}
// Emit a jump opcode to a destination label.
// The offset to the label is relative to the ip following this instruction.
// The offset is encoded as either 1 or 2 bytes, depending on how big it is.
// The encoding of this jump opcode can change size from one pass to the next,
// but it must only ever decrease in size on successive passes.
STATIC void emit_write_bytecode_byte_label(emit_t *emit, int stack_adj, byte b1, mp_uint_t label) {
mp_emit_bc_adjust_stack_size(emit, stack_adj);
// Determine if the jump offset is signed or unsigned, based on the opcode.
const bool is_signed = b1 <= MP_BC_POP_JUMP_IF_FALSE;
// Default to a 2-byte encoding (the largest) with an unknown jump offset.
unsigned int jump_encoding_size = 1;
ssize_t bytecode_offset = 0;
// Compute the jump size and offset only when code size is known.
if (emit->pass >= MP_PASS_CODE_SIZE) {
// The -2 accounts for this jump opcode taking 2 bytes (at least).
bytecode_offset = emit->label_offsets[label] - emit->bytecode_offset - 2;
// Check if the bytecode_offset is small enough to use a 1-byte encoding.
if ((is_signed && -64 <= bytecode_offset && bytecode_offset <= 63)
|| (!is_signed && (size_t)bytecode_offset <= 127)) {
// Use a 1-byte jump offset.
jump_encoding_size = 0;
}
// Adjust the offset depending on the size of the encoding of the offset.
bytecode_offset -= jump_encoding_size;
assert(is_signed || bytecode_offset >= 0);
}
// Emit the opcode.
byte *c = emit_get_cur_to_write_bytecode(emit, 2 + jump_encoding_size);
c[0] = b1;
if (jump_encoding_size == 0) {
if (is_signed) {
bytecode_offset += 0x40;
}
assert(0 <= bytecode_offset && bytecode_offset <= 0x7f);
c[1] = bytecode_offset;
} else {
if (is_signed) {
bytecode_offset += 0x4000;
}
if (emit->pass == MP_PASS_EMIT && !(0 <= bytecode_offset && bytecode_offset <= 0x7fff)) {
emit->overflow = true;
}
c[1] = 0x80 | (bytecode_offset & 0x7f);
c[2] = bytecode_offset >> 7;
}
}
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;
emit->bytecode_offset = 0;
emit->code_info_offset = 0;
emit->overflow = false;
// 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 (emit->pass >= MP_PASS_CODE_SIZE) {
size_t n_info = emit->n_info;
size_t n_cell = emit->n_cell;
MP_BC_PRELUDE_SIZE_ENCODE(n_info, n_cell, emit_write_code_info_byte, emit);
}
emit->n_info = emit->code_info_offset;
// Write the name of this function.
emit_write_code_info_qstr(emit, scope->simple_name);
// Write argument names, needed to resolve positional args passed as keywords.
{
// 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_write_code_info_qstr(emit, qst);
}
}
}
bool mp_emit_bc_end_pass(emit_t *emit) {
if (emit->pass == MP_PASS_SCOPE) {
return true;
}
// check stack is back to zero size
assert(emit->stack_size == 0);
// 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 (emit->pass == MP_PASS_CODE_SIZE) {
// 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);
} else if (emit->pass == MP_PASS_EMIT) {
// Code info and/or bytecode can shrink during this pass.
assert(emit->code_info_offset <= emit->code_info_size);
assert(emit->bytecode_offset <= emit->bytecode_size);
if (emit->code_info_offset != emit->code_info_size
|| emit->bytecode_offset != emit->bytecode_size) {
// Code info and/or bytecode changed size in this pass, so request the
// compiler to do another pass with these updated sizes.
emit->code_info_size = emit->code_info_offset;
emit->bytecode_size = emit->bytecode_offset;
return false;
}
if (emit->overflow) {
mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("bytecode overflow"));
}
// Bytecode is finalised, assign it to the raw code object.
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->emit_common->children,
#if MICROPY_PERSISTENT_CODE_SAVE
emit->emit_common->ct_cur_child,
#endif
emit->scope->scope_flags);
}
return true;
}
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;
}
// Label offsets can change from one pass to the next, but they must only
// decrease (ie code can only shrink). There will be multiple MP_PASS_EMIT
// stages until the labels no longer change, which is when the code size
// stays constant after a MP_PASS_EMIT.
assert(l < emit->max_num_labels);
assert(emit->pass == MP_PASS_STACK_SIZE || emit->bytecode_offset <= emit->label_offsets[l]);
// Assign label offset.
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) {
assert(MP_SMALL_INT_FITS(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);
}
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);
}
}
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_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_label(emit, -1, MP_BC_POP_JUMP_IF_TRUE, label);
} else {
emit_write_bytecode_byte_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_label(emit, -1, MP_BC_JUMP_IF_TRUE_OR_POP, label);
} else {
emit_write_bytecode_byte_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_label(emit, 0, MP_BC_JUMP, label & ~MP_EMIT_BREAK_FROM_FOR);
} else {
emit_write_bytecode_byte_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_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_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_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_child(emit, 1, MP_BC_MAKE_FUNCTION, scope->raw_code);
} else {
emit_write_bytecode_byte_child(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_child(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_child(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) {
// each positional arg is one object, each kwarg is two objects, the key
// and the value and one extra object for the star args bitmap.
stack_adj -= (int)n_positional + 2 * (int)n_keyword + 1;
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

View File

@@ -0,0 +1,122 @@
/*
* 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"
#include "py/nativeglue.h"
#if MICROPY_ENABLE_COMPILER
#if MICROPY_EMIT_BYTECODE_USES_QSTR_TABLE
qstr_short_t mp_emit_common_use_qstr(mp_emit_common_t *emit, qstr qst) {
mp_map_elem_t *elem = mp_map_lookup(&emit->qstr_map, MP_OBJ_NEW_QSTR(qst), MP_MAP_LOOKUP_ADD_IF_NOT_FOUND);
if (elem->value == MP_OBJ_NULL) {
elem->value = MP_OBJ_NEW_SMALL_INT(emit->qstr_map.used - 1);
}
return MP_OBJ_SMALL_INT_VALUE(elem->value);
}
#endif
// Compare two objects for strict equality, including equality of type. This is
// different to the semantics of mp_obj_equal which, eg, has (True,) == (1.0,).
static bool strictly_equal(mp_obj_t a, mp_obj_t b) {
if (a == b) {
return true;
}
#if MICROPY_EMIT_NATIVE
if (a == MP_OBJ_FROM_PTR(&mp_fun_table) || b == MP_OBJ_FROM_PTR(&mp_fun_table)) {
return false;
}
#endif
const mp_obj_type_t *a_type = mp_obj_get_type(a);
const mp_obj_type_t *b_type = mp_obj_get_type(b);
if (a_type != b_type) {
return false;
}
if (a_type == &mp_type_tuple) {
mp_obj_tuple_t *a_tuple = MP_OBJ_TO_PTR(a);
mp_obj_tuple_t *b_tuple = MP_OBJ_TO_PTR(b);
if (a_tuple->len != b_tuple->len) {
return false;
}
for (size_t i = 0; i < a_tuple->len; ++i) {
if (!strictly_equal(a_tuple->items[i], b_tuple->items[i])) {
return false;
}
}
return true;
} else {
return mp_obj_equal(a, b);
}
}
size_t mp_emit_common_use_const_obj(mp_emit_common_t *emit, mp_obj_t const_obj) {
for (size_t i = 0; i < emit->const_obj_list.len; ++i) {
if (strictly_equal(emit->const_obj_list.items[i], const_obj)) {
return i;
}
}
mp_obj_list_append(MP_OBJ_FROM_PTR(&emit->const_obj_list), const_obj);
return emit->const_obj_list.len - 1;
}
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 (id->kind == ID_INFO_KIND_GLOBAL_IMPLICIT) {
if (SCOPE_IS_FUNC_LIKE(scope->kind)) {
// rebind as a local variable
id->kind = ID_INFO_KIND_LOCAL;
} else {
// mark this as assigned, to prevent it from being closed over
id->kind = ID_INFO_KIND_GLOBAL_IMPLICIT_ASSIGNED;
}
}
}
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 || id->kind == ID_INFO_KIND_GLOBAL_IMPLICIT_ASSIGNED) {
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

View File

@@ -0,0 +1,234 @@
/*
* 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/objfun.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
mp_raw_code_t **children,
#if MICROPY_PERSISTENT_CODE_SAVE
size_t n_children,
#endif
mp_uint_t scope_flags) {
rc->kind = MP_CODE_BYTECODE;
rc->scope_flags = scope_flags;
rc->fun_data = code;
#if MICROPY_PERSISTENT_CODE_SAVE || MICROPY_DEBUG_PRINTERS
rc->fun_data_len = len;
#endif
rc->children = children;
#if MICROPY_PERSISTENT_CODE_SAVE
rc->n_children = n_children;
#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_PERSISTENT_CODE_SAVE || 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_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,
mp_raw_code_t **children,
#if MICROPY_PERSISTENT_CODE_SAVE
size_t n_children,
uint16_t prelude_offset,
#endif
mp_uint_t scope_flags, mp_uint_t n_pos_args, mp_uint_t type_sig) {
assert(kind == MP_CODE_NATIVE_PY || kind == MP_CODE_NATIVE_VIPER || kind == MP_CODE_NATIVE_ASM);
// Some architectures require flushing/invalidation of the I/D caches,
// so that the generated native code which was created in data RAM will
// be available for execution from instruction RAM.
#if MICROPY_EMIT_THUMB || MICROPY_EMIT_INLINE_THUMB
#if __ICACHE_PRESENT == 1
// Flush D-cache, so the code emitted is stored in RAM.
MP_HAL_CLEAN_DCACHE(fun_data, fun_len);
// Invalidate I-cache, so the newly-created code is reloaded from RAM.
SCB_InvalidateICache();
#endif
#elif MICROPY_EMIT_ARM
#if (defined(__linux__) && defined(__GNUC__)) || __ARM_ARCH == 7
__builtin___clear_cache(fun_data, (uint8_t *)fun_data + fun_len);
#elif defined(__arm__)
// Flush I-cache and D-cache.
asm volatile (
"0:"
"mrc p15, 0, r15, c7, c10, 3\n" // test and clean D-cache
"bne 0b\n"
"mov r0, #0\n"
"mcr p15, 0, r0, c7, c7, 0\n" // invalidate I-cache and D-cache
: : : "r0", "cc");
#endif
#endif
rc->kind = kind;
rc->scope_flags = scope_flags;
rc->fun_data = fun_data;
#if MICROPY_PERSISTENT_CODE_SAVE || MICROPY_DEBUG_PRINTERS
rc->fun_data_len = fun_len;
#endif
rc->children = children;
#if MICROPY_PERSISTENT_CODE_SAVE
rc->n_children = n_children;
rc->prelude_offset = prelude_offset;
#endif
// These two entries are only needed for MP_CODE_NATIVE_ASM.
rc->n_pos_args = n_pos_args;
rc->type_sig = type_sig;
#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, const mp_module_context_t *context, const mp_obj_t *def_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 == NULL || def_args[0] == MP_OBJ_NULL || mp_obj_is_type(def_args[0], &mp_type_tuple));
// def_kw_args must be MP_OBJ_NULL or a dict
assert(def_args == NULL || def_args[1] == MP_OBJ_NULL || mp_obj_is_type(def_args[1], &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, rc->fun_data, context, rc->children);
// 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, rc->fun_data, context, rc->children);
// 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, const mp_module_context_t *context, 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, context, args);
} else {
// default positional and keyword args not given
ffun = mp_make_function_from_raw_code(rc, context, NULL);
}
// wrap function in closure object
return mp_obj_new_closure(ffun, n_closed_over & 0xff, args + ((n_closed_over >> 7) & 2));
}

View File

@@ -0,0 +1,106 @@
/*
* 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;
// compiled bytecode: instance in RAM, referenced by outer scope, usually freed after first (and only) use
// mpy file: instance in RAM, created when .mpy file is loaded (same comments as above)
// frozen: instance in ROM
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;
#if MICROPY_PERSISTENT_CODE_SAVE || MICROPY_DEBUG_PRINTERS
size_t fun_data_len; // so mp_raw_code_save and mp_bytecode_print work
#endif
struct _mp_raw_code_t **children;
#if MICROPY_PERSISTENT_CODE_SAVE
size_t n_children;
#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;
#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
mp_raw_code_t **children,
#if MICROPY_PERSISTENT_CODE_SAVE
size_t n_children,
#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,
mp_raw_code_t **children,
#if MICROPY_PERSISTENT_CODE_SAVE
size_t n_children,
uint16_t prelude_offset,
#endif
mp_uint_t scope_flags, mp_uint_t n_pos_args, mp_uint_t type_sig);
mp_obj_t mp_make_function_from_raw_code(const mp_raw_code_t *rc, const mp_module_context_t *context, const mp_obj_t *def_args);
mp_obj_t mp_make_closure_from_raw_code(const mp_raw_code_t *rc, const mp_module_context_t *context, mp_uint_t n_closed_over, const mp_obj_t *args);
#endif // MICROPY_INCLUDED_PY_EMITGLUE_H

View File

@@ -0,0 +1,865 @@
/*
* 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;
};
#if MICROPY_DYNAMIC_COMPILER
static inline bool emit_inline_thumb_allow_float(emit_inline_asm_t *emit) {
return MP_NATIVE_ARCH_ARMV7EMSP <= mp_dynamic_compiler.native_arch
&& mp_dynamic_compiler.native_arch <= MP_NATIVE_ARCH_ARMV7EMDP;
}
#else
static inline bool emit_inline_thumb_allow_float(emit_inline_asm_t *emit) {
return MICROPY_EMIT_INLINE_THUMB_FLOAT;
}
#endif
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 = &reg_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;
}
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;
}
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
// 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" },
};
// shorthand alias for whether we allow ARMv7-M instructions
#define ARMV7M asm_thumb_allow_armv7m(&emit->as)
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 (emit_inline_thumb_allow_float(emit) && 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;
}
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]);
bool wide = op_len == 5 && op_str[4] == 'w';
if (wide && !ARMV7M) {
goto unknown_op;
}
if (!asm_thumb_bcc_nw_label(&emit->as, cc, label_num, wide)) {
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 & 0xbf00) == 0) {
if ((reglist & (1 << 14)) == 0) {
asm_thumb_op16(&emit->as, 0xb400 | reglist);
} else {
// 16-bit encoding for pushing low registers and LR
asm_thumb_op16(&emit->as, 0xb500 | (reglist & 0xff));
}
} 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 & 0x7f00) == 0) {
if ((reglist & (1 << 15)) == 0) {
asm_thumb_op16(&emit->as, 0xbc00 | reglist);
} else {
// 16-bit encoding for popping low registers and PC, i.e., returning
asm_thumb_op16(&emit->as, 0xbd00 | (reglist & 0xff));
}
} 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

View File

@@ -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 = &reg_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

View File

@@ -0,0 +1,18 @@
// 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 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

View File

@@ -0,0 +1,18 @@
// 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 N_THUMB (1)
#define EXPORT_FUN(name) emit_native_thumb_##name
#include "py/emitnative.c"
#endif

View File

@@ -0,0 +1,18 @@
// 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 N_X64 (1)
#define EXPORT_FUN(name) emit_native_x64_##name
#include "py/emitnative.c"
#endif

View File

@@ -0,0 +1,70 @@
// 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
// 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_NEW_CLOSURE] = 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

View File

@@ -0,0 +1,18 @@
// 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 N_XTENSA (1)
#define EXPORT_FUN(name) emit_native_xtensa_##name
#include "py/emitnative.c"
#endif

View File

@@ -0,0 +1,20 @@
// 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 N_NLR_SETJMP (1)
#define N_XTENSAWIN (1)
#define EXPORT_FUN(name) emit_native_xtensawin_##name
#include "py/emitnative.c"
#endif

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,135 @@
/*
* 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
* Copyright (c) 2021 Jim Mussared
*
* 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
// Null-separated frozen file names. All string-type entries are listed first,
// followed by mpy-type entries. Use mp_frozen_str_sizes to determine how
// many string entries.
extern const char mp_frozen_names[];
#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
// Size in bytes of each string entry, followed by a zero (terminator).
extern const uint32_t mp_frozen_str_sizes[];
// Null-separated string content.
extern const char mp_frozen_str_content[];
#endif // MICROPY_MODULE_FROZEN_STR
#if MICROPY_MODULE_FROZEN_MPY
#include "py/emitglue.h"
extern const mp_frozen_module_t *const mp_frozen_mpy_content[];
#endif // MICROPY_MODULE_FROZEN_MPY
// Search for "str" as a frozen entry, returning the stat result
// (no-exist/file/dir), as well as the type (none/str/mpy) and data.
// frozen_type can be NULL if its value isn't needed (and then data is assumed to be NULL).
mp_import_stat_t mp_find_frozen_module(const char *str, int *frozen_type, void **data) {
size_t len = strlen(str);
const char *name = mp_frozen_names;
if (frozen_type != NULL) {
*frozen_type = MP_FROZEN_NONE;
}
// Count the number of str lengths we have to find how many str entries.
size_t num_str = 0;
#if MICROPY_MODULE_FROZEN_STR && MICROPY_MODULE_FROZEN_MPY
for (const uint32_t *s = mp_frozen_str_sizes; *s != 0; ++s) {
++num_str;
}
#endif
for (size_t i = 0; *name != 0; i++) {
size_t entry_len = strlen(name);
if (entry_len >= len && memcmp(str, name, len) == 0) {
// Query is a prefix of the current entry.
if (entry_len == len) {
// Exact match --> file.
if (frozen_type != NULL) {
#if MICROPY_MODULE_FROZEN_STR
if (i < num_str) {
*frozen_type = MP_FROZEN_STR;
// Use the size table to figure out where this index starts.
size_t offset = 0;
for (size_t j = 0; j < i; ++j) {
offset += mp_frozen_str_sizes[j] + 1;
}
size_t content_len = mp_frozen_str_sizes[i];
const char *content = &mp_frozen_str_content[offset];
// Note: str & len have been updated by find_frozen_entry to strip
// the ".frozen/" prefix (to avoid this being a distinct qstr to
// the original path QSTR in frozen_content.c).
qstr source = qstr_from_strn(str, len);
mp_lexer_t *lex = MICROPY_MODULE_FROZEN_LEXER(source, content, content_len, 0);
*data = lex;
}
#endif
#if MICROPY_MODULE_FROZEN_MPY
if (i >= num_str) {
*frozen_type = MP_FROZEN_MPY;
// Load the corresponding index as a raw_code, taking
// into account any string entries to offset by.
*data = (void *)mp_frozen_mpy_content[i - num_str];
}
#endif
}
return MP_IMPORT_STAT_FILE;
} else if (name[len] == '/') {
// Matches up to directory separator, this is a valid
// directory path.
return MP_IMPORT_STAT_DIR;
}
}
// Skip null separator.
name += entry_len + 1;
}
return MP_IMPORT_STAT_NO_EXIST;
}
#endif // MICROPY_MODULE_FROZEN

View File

@@ -0,0 +1,40 @@
/*
* 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/builtin.h"
enum {
MP_FROZEN_NONE,
MP_FROZEN_STR,
MP_FROZEN_MPY,
};
mp_import_stat_t mp_find_frozen_module(const char *str, int *frozen_type, void **data);
#endif // MICROPY_INCLUDED_PY_FROZENMOD_H

View File

@@ -0,0 +1,989 @@
/*
* 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) / MP_BYTES_PER_OBJ_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 * MP_BITS_PER_BYTE / (MP_BITS_PER_BYTE + MP_BITS_PER_BYTE * BLOCKS_PER_ATB / BLOCKS_PER_FTB + MP_BITS_PER_BYTE * BLOCKS_PER_ATB * BYTES_PER_BLOCK);
#else
MP_STATE_MEM(gc_alloc_table_byte_len) = total_byte_len / (1 + MP_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_THREAD(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) {
// This does not need to be atomic or have the GC mutex because:
// - each thread has its own gc_lock_depth so there are no races between threads;
// - a hard interrupt will only change gc_lock_depth during its execution, and
// upon return will restore the value of gc_lock_depth.
MP_STATE_THREAD(gc_lock_depth)++;
}
void gc_unlock(void) {
// This does not need to be atomic, See comment above in gc_lock.
MP_STATE_THREAD(gc_lock_depth)--;
}
bool gc_is_locked(void) {
return MP_STATE_THREAD(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 (;;) {
MICROPY_GC_HOOK_LOOP
// 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++) {
MICROPY_GC_HOOK_LOOP
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++) {
MICROPY_GC_HOOK_LOOP
// 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++) {
MICROPY_GC_HOOK_LOOP
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", (void *)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_THREAD(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
}
// Address sanitizer needs to know that the access to ptrs[i] must always be
// considered OK, even if it's a load from an address that would normally be
// prohibited (due to being undefined, in a red zone, etc).
#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8))
__attribute__((no_sanitize_address))
#endif
static void *gc_get_ptr(void **ptrs, int i) {
return ptrs[i];
}
void gc_collect_root(void **ptrs, size_t len) {
for (size_t i = 0; i < len; i++) {
MICROPY_GC_HOOK_LOOP
void *ptr = gc_get_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_THREAD(gc_lock_depth)--;
GC_EXIT();
}
void gc_sweep_all(void) {
GC_ENTER();
MP_STATE_THREAD(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;
}
// check if GC is locked
if (MP_STATE_THREAD(gc_lock_depth) > 0) {
return NULL;
}
GC_ENTER();
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) {
if (MP_STATE_THREAD(gc_lock_depth) > 0) {
// TODO how to deal with this error?
return;
}
GC_ENTER();
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;
}
if (MP_STATE_THREAD(gc_lock_depth) > 0) {
return NULL;
}
void *ptr = ptr_in;
GC_ENTER();
// 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 (const qstr_pool_t *pool = MP_STATE_VM(last_pool); c == 'h' && pool != NULL; pool = pool->prev) {
if ((const qstr_pool_t *)ptr == pool) {
c = 'Q';
break;
}
for (const char *const *q = pool->qstrs, *const *q_top = pool->qstrs + pool->len; q < q_top; q++) {
if ((const char *)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

View File

@@ -0,0 +1,72 @@
/*
* 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 <stdbool.h>
#include <stddef.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

View File

@@ -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))

View File

@@ -0,0 +1,917 @@
/*
* 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;
}
#if MICROPY_PY_FSTRINGS
STATIC bool is_char_or4(mp_lexer_t *lex, byte c1, byte c2, byte c3, byte c4) {
return lex->chr0 == c1 || lex->chr0 == c2 || lex->chr0 == c3 || lex->chr0 == c4;
}
#endif
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, '\'', '\"')
#if MICROPY_PY_FSTRINGS
|| (is_char_or4(lex, 'r', 'u', 'b', 'f') && is_char_following_or(lex, '\'', '\"'))
|| (((is_char_and(lex, 'r', 'f') || is_char_and(lex, 'f', 'r'))
&& is_char_following_following_or(lex, '\'', '\"')))
#else
|| (is_char_or3(lex, 'r', 'u', 'b') && is_char_following_or(lex, '\'', '\"'))
#endif
|| ((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;
}
// shift the input queue forward
lex->chr0 = lex->chr1;
lex->chr1 = lex->chr2;
// and add the next byte from either the fstring args or the reader
#if MICROPY_PY_FSTRINGS
if (lex->fstring_args_idx) {
// if there are saved chars, then we're currently injecting fstring args
if (lex->fstring_args_idx < lex->fstring_args.len) {
lex->chr2 = lex->fstring_args.buf[lex->fstring_args_idx++];
} else {
// no more fstring arg bytes
lex->chr2 = '\0';
}
if (lex->chr0 == '\0') {
// consumed all fstring data, restore saved input queue
lex->chr0 = lex->chr0_saved;
lex->chr1 = lex->chr1_saved;
lex->chr2 = lex->chr2_saved;
// stop consuming fstring arg data
vstr_reset(&lex->fstring_args);
lex->fstring_args_idx = 0;
}
} else
#endif
{
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, bool is_fstring) {
// 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;
#if MICROPY_PY_FSTRINGS
if (is_fstring) {
// assume there's going to be interpolation, so prep the injection data
// fstring_args_idx==0 && len(fstring_args)>0 means we're extracting the args.
// only when fstring_args_idx>0 will we consume the arg data
// note: lex->fstring_args will be empty already (it's reset when finished)
vstr_add_str(&lex->fstring_args, ".format(");
}
#endif
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 MICROPY_PY_FSTRINGS
while (is_fstring && is_char(lex, '{')) {
next_char(lex);
if (is_char(lex, '{')) {
// "{{" is passed through unchanged to be handled by str.format
vstr_add_byte(&lex->vstr, '{');
next_char(lex);
} else {
// remember the start of this argument (if we need it for f'{a=}').
size_t i = lex->fstring_args.len;
// extract characters inside the { until we reach the
// format specifier or closing }.
// (MicroPython limitation) note: this is completely unaware of
// Python syntax and will not handle any expression containing '}' or ':'.
// e.g. f'{"}"}' or f'{foo({})}'.
unsigned int nested_bracket_level = 0;
while (!is_end(lex) && (nested_bracket_level != 0 || !is_char_or(lex, ':', '}'))) {
unichar c = CUR_CHAR(lex);
if (c == '[' || c == '{') {
nested_bracket_level += 1;
} else if (c == ']' || c == '}') {
nested_bracket_level -= 1;
}
// like the default case at the end of this function, stay 8-bit clean
vstr_add_byte(&lex->fstring_args, c);
next_char(lex);
}
if (lex->fstring_args.buf[lex->fstring_args.len - 1] == '=') {
// if the last character of the arg was '=', then inject "arg=" before the '{'.
// f'{a=}' --> 'a={}'.format(a)
vstr_add_strn(&lex->vstr, lex->fstring_args.buf + i, lex->fstring_args.len - i);
// remove the trailing '='
lex->fstring_args.len--;
}
// comma-separate args
vstr_add_byte(&lex->fstring_args, ',');
}
vstr_add_byte(&lex->vstr, '{');
}
#endif
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
if (c < 0x110000 && lex->tok_kind == MP_TOKEN_STRING) {
// Valid unicode character in a str object.
vstr_add_char(&lex->vstr, c);
} else if (c < 0x100 && lex->tok_kind == MP_TOKEN_BYTES) {
// Valid byte in a bytes object.
vstr_add_byte(&lex->vstr, c);
}
#else
if (c < 0x100) {
// Without unicode everything is just added as an 8-bit byte.
vstr_add_byte(&lex->vstr, c);
}
#endif
else {
// Character out of range; this raises a generic SyntaxError.
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) {
#if MICROPY_PY_FSTRINGS
if (lex->fstring_args.len && lex->fstring_args_idx == 0) {
// moving onto the next token means the literal string is complete.
// switch into injecting the format args.
vstr_add_byte(&lex->fstring_args, ')');
lex->chr0_saved = lex->chr0;
lex->chr1_saved = lex->chr1;
lex->chr2_saved = lex->chr2;
lex->chr0 = lex->fstring_args.buf[0];
lex->chr1 = lex->fstring_args.buf[1];
lex->chr2 = lex->fstring_args.buf[2];
// we've already extracted 3 chars, but setting this non-zero also
// means we'll start consuming the fstring data
lex->fstring_args_idx = 3;
}
#endif
// 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;
bool is_fstring = 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;
}
#if MICROPY_PY_FSTRINGS
if (is_char_following(lex, 'f')) {
// raw-f-strings unsupported, immediately return (invalid) token.
lex->tok_kind = MP_TOKEN_FSTRING_RAW;
break;
}
#endif
}
#if MICROPY_PY_FSTRINGS
else if (is_char(lex, 'f')) {
if (is_char_following(lex, 'r')) {
// raw-f-strings unsupported, immediately return (invalid) token.
lex->tok_kind = MP_TOKEN_FSTRING_RAW;
break;
}
n_char = 1;
is_fstring = true;
}
#endif
// 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, is_fstring);
// 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);
#if MICROPY_PY_FSTRINGS
vstr_init(&lex->fstring_args, 0);
#endif
// 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);
#if MICROPY_PY_FSTRINGS
vstr_clear(&lex->fstring_args);
#endif
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

View File

@@ -0,0 +1,203 @@
/*
* 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,
#if MICROPY_PY_FSTRINGS
MP_TOKEN_MALFORMED_FSTRING,
MP_TOKEN_FSTRING_RAW,
#endif
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
#if MICROPY_PY_FSTRINGS
unichar chr0_saved, chr1_saved, chr2_saved; // current cached characters from alt source
#endif
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
#if MICROPY_PY_FSTRINGS
vstr_t fstring_args; // extracted arguments to pass to .format()
size_t fstring_args_idx; // how many bytes of fstring_args have been read
#endif
} 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);
// If MICROPY_READER_POSIX or MICROPY_READER_VFS aren't enabled then
// this function must be implemented by the port.
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
void mp_lexer_free(mp_lexer_t *lex);
void mp_lexer_to_next(mp_lexer_t *lex);
#endif // MICROPY_INCLUDED_PY_LEXER_H

View File

@@ -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)

View File

@@ -0,0 +1,82 @@
"""
This pre-processor parses a single file containing a list of
MP_REGISTER_MODULE(module_name, obj_module)
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 sys
import re
import io
import argparse
pattern = re.compile(r"\s*MP_REGISTER_MODULE\((.*?),\s*(.*?)\);", flags=re.DOTALL)
def find_module_registrations(filename):
"""Find any MP_REGISTER_MODULE definitions in the provided file.
:param str filename: path to file to check
:return: List[(module_name, obj_module)]
"""
global pattern
with io.open(filename, 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)] modules: module defs
:return: None
"""
# Print header file for all external modules.
mod_defs = set()
print("// Automatically generated by makemoduledefs.py.\n")
for module_name, obj_module in modules:
mod_def = "MODULE_DEF_{}".format(module_name.upper())
mod_defs.add(mod_def)
if "," in obj_module:
print(
"ERROR: Call to MP_REGISTER_MODULE({}, {}) should be MP_REGISTER_MODULE({}, {})\n".format(
module_name, obj_module, module_name, obj_module.split(",")[0]
),
file=sys.stderr,
)
sys.exit(1)
print(
(
"extern const struct _mp_obj_module_t {obj_module};\n"
"#undef {mod_def}\n"
"#define {mod_def} {{ MP_ROM_QSTR({module_name}), MP_ROM_PTR(&{obj_module}) }},\n"
).format(
module_name=module_name,
obj_module=obj_module,
mod_def=mod_def,
)
)
print("\n#define MICROPY_REGISTERED_MODULES \\")
for mod_def in sorted(mod_defs):
print(" {mod_def} \\".format(mod_def=mod_def))
print("// MICROPY_REGISTERED_MODULES")
def main():
parser = argparse.ArgumentParser()
parser.add_argument("file", nargs=1, help="file with MP_REGISTER_MODULE definitions")
args = parser.parse_args()
modules = find_module_registrations(args.file[0])
generate_module_table_header(sorted(modules))
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,364 @@
"""
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 escape_bytes(qstr, qbytes):
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)
return qstr
else:
# qstr contains non-printable codes so render entire thing as hex pairs
return "".join(("\\x%02x" % b) for b in qbytes)
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 qlen >= (1 << (8 * cfg_bytes_len)):
print("qstr is too long:", qstr)
assert False
qdata = escape_bytes(qstr, qbytes)
return '%d, %d, "%s"' % (qhash, qlen, 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, 0, 0, "")')
# 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:])

View File

@@ -0,0 +1,225 @@
"""
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 io
import os
import re
import subprocess
import sys
import multiprocessing, multiprocessing.dummy
# Extract MP_QSTR_FOO macros.
_MODE_QSTR = "qstr"
# Extract MP_COMPRESSED_ROM_TEXT("") macros. (Which come from MP_ERROR_TEXT)
_MODE_COMPRESS = "compress"
# Extract MP_REGISTER_MODULE(...) macros.
_MODE_MODULE = "module"
def is_c_source(fname):
return os.path.splitext(fname)[1] in [".c"]
def is_cxx_source(fname):
return os.path.splitext(fname)[1] in [".cc", ".cp", ".cxx", ".cpp", ".CPP", ".c++", ".C"]
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 is_cxx_source(source):
cxxsources.append(source)
elif is_c_source(source):
csources.append(source)
try:
os.makedirs(os.path.dirname(args.output[0]))
except OSError:
pass
def pp(flags):
def run(files):
return subprocess.check_output(args.pp + flags + files)
return run
try:
cpus = multiprocessing.cpu_count()
except NotImplementedError:
cpus = 1
p = multiprocessing.dummy.Pool(cpus)
with open(args.output[0], "wb") as out_file:
for flags, sources in (
(args.cflags, csources),
(args.cxxflags, cxxsources),
):
batch_size = (len(sources) + cpus - 1) // cpus
chunks = [sources[i : i + batch_size] for i in range(0, len(sources), batch_size or 1)]
for output in p.imap(pp(flags), chunks):
out_file.write(output)
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\("([^"]*)"\)')
elif args.mode == _MODE_MODULE:
re_match = re.compile(r"MP_REGISTER_MODULE\(.*?,\s*.*?\);")
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 not is_c_source(fname) and not is_cxx_source(fname):
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 in (_MODE_COMPRESS, _MODE_MODULE):
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"
elif args.mode == _MODE_MODULE:
mode_full = "Module registrations"
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, _MODE_MODULE):
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()

View File

@@ -0,0 +1,117 @@
"""
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", "--tags", "--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
build_date = datetime.date.today()
if "SOURCE_DATE_EPOCH" in os.environ:
build_date = datetime.datetime.utcfromtimestamp(
int(os.environ["SOURCE_DATE_EPOCH"])
).date()
# 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,
build_date.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])

View File

@@ -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.
*/
#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_TRACKED_ALLOC
#define MICROPY_TRACKED_ALLOC_STORE_SIZE (!MICROPY_ENABLE_GC)
typedef struct _m_tracked_node_t {
struct _m_tracked_node_t *prev;
struct _m_tracked_node_t *next;
#if MICROPY_TRACKED_ALLOC_STORE_SIZE
uintptr_t size;
#endif
uint8_t data[];
} m_tracked_node_t;
#if MICROPY_DEBUG_VERBOSE
STATIC size_t m_tracked_count_links(size_t *nb) {
m_tracked_node_t *node = MP_STATE_VM(m_tracked_head);
size_t n = 0;
*nb = 0;
while (node != NULL) {
++n;
#if MICROPY_TRACKED_ALLOC_STORE_SIZE
*nb += node->size;
#else
*nb += gc_nbytes(node);
#endif
node = node->next;
}
return n;
}
#endif
void *m_tracked_calloc(size_t nmemb, size_t size) {
m_tracked_node_t *node = m_malloc_maybe(sizeof(m_tracked_node_t) + nmemb * size);
if (node == NULL) {
return NULL;
}
#if MICROPY_DEBUG_VERBOSE
size_t nb;
size_t n = m_tracked_count_links(&nb);
DEBUG_printf("m_tracked_calloc(%u, %u) -> (%u;%u) %p\n", (int)nmemb, (int)size, (int)n, (int)nb, node);
#endif
if (MP_STATE_VM(m_tracked_head) != NULL) {
MP_STATE_VM(m_tracked_head)->prev = node;
}
node->prev = NULL;
node->next = MP_STATE_VM(m_tracked_head);
MP_STATE_VM(m_tracked_head) = node;
#if MICROPY_TRACKED_ALLOC_STORE_SIZE
node->size = nmemb * size;
#endif
#if !MICROPY_GC_CONSERVATIVE_CLEAR
memset(&node->data[0], 0, nmemb * size);
#endif
return &node->data[0];
}
void m_tracked_free(void *ptr_in) {
if (ptr_in == NULL) {
return;
}
m_tracked_node_t *node = (m_tracked_node_t *)((uint8_t *)ptr_in - sizeof(m_tracked_node_t));
#if MICROPY_DEBUG_VERBOSE
size_t data_bytes;
#if MICROPY_TRACKED_ALLOC_STORE_SIZE
data_bytes = node->size;
#else
data_bytes = gc_nbytes(node);
#endif
size_t nb;
size_t n = m_tracked_count_links(&nb);
DEBUG_printf("m_tracked_free(%p, [%p, %p], nbytes=%u, links=%u;%u)\n", node, node->prev, node->next, (int)data_bytes, (int)n, (int)nb);
#endif
if (node->next != NULL) {
node->next->prev = node->prev;
}
if (node->prev != NULL) {
node->prev->next = node->next;
} else {
MP_STATE_VM(m_tracked_head) = node->next;
}
m_free(node
#if MICROPY_MALLOC_USES_ALLOCATED_SIZE
#if MICROPY_TRACKED_ALLOC_STORE_SIZE
, node->size
#else
, gc_nbytes(node)
#endif
#endif
);
}
#endif // MICROPY_TRACKED_ALLOC
#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

View File

@@ -0,0 +1,460 @@
/*
* 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
#if MICROPY_OPT_MAP_LOOKUP_CACHE
// MP_STATE_VM(map_lookup_cache) provides a cache of index to the last known
// position of that index in any map. On a cache hit, this allows
// short-circuiting the full linear search in the case of an ordered map
// (i.e. all builtin modules and objects' locals dicts), and computation of
// the hash (and potentially some linear probing) in the case of a regular
// map. Note the same cache is shared across all maps.
// Gets the index into the cache for this index. Shift down by two to remove
// mp_obj_t tag bits.
#define MAP_CACHE_OFFSET(index) ((((uintptr_t)(index)) >> 2) % MICROPY_OPT_MAP_LOOKUP_CACHE_SIZE)
// Gets the map cache entry for the corresponding index.
#define MAP_CACHE_ENTRY(index) (MP_STATE_VM(map_lookup_cache)[MAP_CACHE_OFFSET(index)])
// Retrieve the mp_obj_t at the location suggested by the cache.
#define MAP_CACHE_GET(map, index) (&(map)->table[MAP_CACHE_ENTRY(index) % (map)->alloc])
// Update the cache for this index.
#define MAP_CACHE_SET(index, pos) MAP_CACHE_ENTRY(index) = (pos) & 0xff;
#else
#define MAP_CACHE_SET(index, pos)
#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 *MICROPY_WRAP_MP_MAP_LOOKUP(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);
#if MICROPY_OPT_MAP_LOOKUP_CACHE
// Try the cache for lookup or add-if-not-found.
if (lookup_kind != MP_MAP_LOOKUP_REMOVE_IF_FOUND && map->alloc) {
mp_map_elem_t *slot = MAP_CACHE_GET(map, index);
// Note: Just comparing key for value equality will have false negatives, but
// these will be handled by the regular path below.
if (slot->key == index) {
return slot;
}
}
#endif
// 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
MAP_CACHE_SET(index, elem - map->table);
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
}
MAP_CACHE_SET(index, pos);
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

View File

@@ -0,0 +1,330 @@
/*
* 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)]))
// Round-up integer division
#define MP_CEIL_DIVIDE(a, b) (((a) + (b) - 1) / (b))
#define MP_ROUND_DIVIDE(a, b) (((a) + (b) / 2) / (b))
/** 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_TRACKED_ALLOC
// These alloc/free functions track the pointers in a linked list so the GC does not reclaim
// them. They can be used by code that requires traditional C malloc/free semantics.
void *m_tracked_calloc(size_t nmemb, size_t size);
void m_tracked_free(void *ptr_in);
#endif
#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
#if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_NONE
#error "MICROPY_ERROR_REPORTING_NONE requires MICROPY_ROM_TEXT_COMPRESSION disabled"
#endif
#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

View File

@@ -0,0 +1,72 @@
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
$(info Use make V=1 or set BUILD_VERBOSE in your environment to increase build verbosity.)
BUILD_VERBOSE = 0
endif
ifeq ($(BUILD_VERBOSE),0)
Q = @
else
Q =
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_TOOL = $(PYTHON) $(TOP)/tools/mpy-tool.py
MPY_LIB_DIR = $(TOP)/../micropython-lib
ifeq ($(MICROPY_MPYCROSS),)
MICROPY_MPYCROSS = $(TOP)/mpy-cross/mpy-cross
MICROPY_MPYCROSS_DEPENDENCY = $(MICROPY_MPYCROSS)
endif
all:
.PHONY: all
.DELETE_ON_ERROR:
MKENV_INCLUDED = 1

View File

@@ -0,0 +1,187 @@
# CMake fragment for MicroPython rules
set(MICROPY_GENHDR_DIR "${CMAKE_BINARY_DIR}/genhdr")
set(MICROPY_MPVERSION "${MICROPY_GENHDR_DIR}/mpversion.h")
set(MICROPY_QSTRDEFS_PY "${MICROPY_PY_DIR}/qstrdefs.h")
set(MICROPY_QSTRDEFS_LAST "${MICROPY_GENHDR_DIR}/qstr.i.last")
set(MICROPY_QSTRDEFS_SPLIT "${MICROPY_GENHDR_DIR}/qstr.split")
set(MICROPY_QSTRDEFS_COLLECTED "${MICROPY_GENHDR_DIR}/qstrdefs.collected.h")
set(MICROPY_QSTRDEFS_PREPROCESSED "${MICROPY_GENHDR_DIR}/qstrdefs.preprocessed.h")
set(MICROPY_QSTRDEFS_GENERATED "${MICROPY_GENHDR_DIR}/qstrdefs.generated.h")
set(MICROPY_MODULEDEFS_SPLIT "${MICROPY_GENHDR_DIR}/moduledefs.split")
set(MICROPY_MODULEDEFS_COLLECTED "${MICROPY_GENHDR_DIR}/moduledefs.collected")
set(MICROPY_MODULEDEFS "${MICROPY_GENHDR_DIR}/moduledefs.h")
# Need to do this before extracting MICROPY_CPP_DEF below. Rest of frozen
# manifest handling is at the end of this file.
if(MICROPY_FROZEN_MANIFEST)
target_compile_definitions(${MICROPY_TARGET} PUBLIC
MICROPY_QSTR_EXTRA_POOL=mp_qstr_frozen_const_pool
MICROPY_MODULE_FROZEN_MPY=\(1\)
)
endif()
# Provide defaults for preprocessor flags if not already defined
if(NOT MICROPY_CPP_FLAGS)
get_target_property(MICROPY_CPP_INC ${MICROPY_TARGET} INCLUDE_DIRECTORIES)
get_target_property(MICROPY_CPP_DEF ${MICROPY_TARGET} COMPILE_DEFINITIONS)
endif()
# Compute MICROPY_CPP_FLAGS for preprocessor
list(APPEND MICROPY_CPP_INC ${MICROPY_CPP_INC_EXTRA})
list(APPEND MICROPY_CPP_DEF ${MICROPY_CPP_DEF_EXTRA})
set(_prefix "-I")
foreach(_arg ${MICROPY_CPP_INC})
list(APPEND MICROPY_CPP_FLAGS ${_prefix}${_arg})
endforeach()
set(_prefix "-D")
foreach(_arg ${MICROPY_CPP_DEF})
list(APPEND MICROPY_CPP_FLAGS ${_prefix}${_arg})
endforeach()
list(APPEND MICROPY_CPP_FLAGS ${MICROPY_CPP_FLAGS_EXTRA})
find_package(Python3 REQUIRED COMPONENTS Interpreter)
target_sources(${MICROPY_TARGET} PRIVATE
${MICROPY_MPVERSION}
${MICROPY_QSTRDEFS_GENERATED}
${MICROPY_MODULEDEFS}
)
# Command to force the build of another command
add_custom_command(
OUTPUT MICROPY_FORCE_BUILD
COMMENT ""
COMMAND echo -n
)
# Generate mpversion.h
add_custom_command(
OUTPUT ${MICROPY_MPVERSION}
COMMAND ${CMAKE_COMMAND} -E make_directory ${MICROPY_GENHDR_DIR}
COMMAND ${Python3_EXECUTABLE} ${MICROPY_DIR}/py/makeversionhdr.py ${MICROPY_MPVERSION}
DEPENDS MICROPY_FORCE_BUILD
)
# Generate qstrs
# If any of the dependencies in this rule change then the C-preprocessor step must be run.
# It only needs to be passed the list of MICROPY_SOURCE_QSTR files that have changed since
# it was last run, but it looks like it's not possible to specify that with cmake.
add_custom_command(
OUTPUT ${MICROPY_QSTRDEFS_LAST}
COMMAND ${Python3_EXECUTABLE} ${MICROPY_PY_DIR}/makeqstrdefs.py pp ${CMAKE_C_COMPILER} -E output ${MICROPY_GENHDR_DIR}/qstr.i.last cflags ${MICROPY_CPP_FLAGS} -DNO_QSTR cxxflags ${MICROPY_CPP_FLAGS} -DNO_QSTR sources ${MICROPY_SOURCE_QSTR}
DEPENDS ${MICROPY_MPVERSION}
${MICROPY_SOURCE_QSTR}
VERBATIM
COMMAND_EXPAND_LISTS
)
add_custom_command(
OUTPUT ${MICROPY_QSTRDEFS_SPLIT}
COMMAND ${Python3_EXECUTABLE} ${MICROPY_PY_DIR}/makeqstrdefs.py split qstr ${MICROPY_GENHDR_DIR}/qstr.i.last ${MICROPY_GENHDR_DIR}/qstr _
COMMAND touch ${MICROPY_QSTRDEFS_SPLIT}
DEPENDS ${MICROPY_QSTRDEFS_LAST}
VERBATIM
COMMAND_EXPAND_LISTS
)
add_custom_command(
OUTPUT ${MICROPY_QSTRDEFS_COLLECTED}
COMMAND ${Python3_EXECUTABLE} ${MICROPY_PY_DIR}/makeqstrdefs.py cat qstr _ ${MICROPY_GENHDR_DIR}/qstr ${MICROPY_QSTRDEFS_COLLECTED}
DEPENDS ${MICROPY_QSTRDEFS_SPLIT}
VERBATIM
COMMAND_EXPAND_LISTS
)
add_custom_command(
OUTPUT ${MICROPY_QSTRDEFS_PREPROCESSED}
COMMAND cat ${MICROPY_QSTRDEFS_PY} ${MICROPY_QSTRDEFS_PORT} ${MICROPY_QSTRDEFS_COLLECTED} | sed "s/^Q(.*)/\"&\"/" | ${CMAKE_C_COMPILER} -E ${MICROPY_CPP_FLAGS} - | sed "s/^\\\"\\(Q(.*)\\)\\\"/\\1/" > ${MICROPY_QSTRDEFS_PREPROCESSED}
DEPENDS ${MICROPY_QSTRDEFS_PY}
${MICROPY_QSTRDEFS_PORT}
${MICROPY_QSTRDEFS_COLLECTED}
VERBATIM
COMMAND_EXPAND_LISTS
)
add_custom_command(
OUTPUT ${MICROPY_QSTRDEFS_GENERATED}
COMMAND ${Python3_EXECUTABLE} ${MICROPY_PY_DIR}/makeqstrdata.py ${MICROPY_QSTRDEFS_PREPROCESSED} > ${MICROPY_QSTRDEFS_GENERATED}
DEPENDS ${MICROPY_QSTRDEFS_PREPROCESSED}
VERBATIM
COMMAND_EXPAND_LISTS
)
# Generate moduledefs.h
add_custom_command(
OUTPUT ${MICROPY_MODULEDEFS_SPLIT}
COMMAND ${Python3_EXECUTABLE} ${MICROPY_PY_DIR}/makeqstrdefs.py split module ${MICROPY_GENHDR_DIR}/qstr.i.last ${MICROPY_GENHDR_DIR}/module _
COMMAND touch ${MICROPY_MODULEDEFS_SPLIT}
DEPENDS ${MICROPY_QSTRDEFS_LAST}
VERBATIM
COMMAND_EXPAND_LISTS
)
add_custom_command(
OUTPUT ${MICROPY_MODULEDEFS_COLLECTED}
COMMAND ${Python3_EXECUTABLE} ${MICROPY_PY_DIR}/makeqstrdefs.py cat module _ ${MICROPY_GENHDR_DIR}/module ${MICROPY_MODULEDEFS_COLLECTED}
DEPENDS ${MICROPY_MODULEDEFS_SPLIT}
VERBATIM
COMMAND_EXPAND_LISTS
)
add_custom_command(
OUTPUT ${MICROPY_MODULEDEFS}
COMMAND ${Python3_EXECUTABLE} ${MICROPY_PY_DIR}/makemoduledefs.py ${MICROPY_MODULEDEFS_COLLECTED} > ${MICROPY_MODULEDEFS}
DEPENDS ${MICROPY_MODULEDEFS_COLLECTED}
)
# Build frozen code if enabled
if(MICROPY_FROZEN_MANIFEST)
set(MICROPY_FROZEN_CONTENT "${CMAKE_BINARY_DIR}/frozen_content.c")
target_sources(${MICROPY_TARGET} PRIVATE
${MICROPY_FROZEN_CONTENT}
)
# Note: target_compile_definitions already added earlier.
if(NOT MICROPY_LIB_DIR)
set(MICROPY_LIB_DIR ${MICROPY_DIR}/../micropython-lib)
endif()
# If MICROPY_MPYCROSS is not explicitly defined in the environment (which
# is what makemanifest.py will use) then create an mpy-cross dependency
# to automatically build mpy-cross if needed.
set(MICROPY_MPYCROSS $ENV{MICROPY_MPYCROSS})
if(NOT MICROPY_MPYCROSS)
set(MICROPY_MPYCROSS_DEPENDENCY ${MICROPY_DIR}/mpy-cross/mpy-cross)
if(NOT MICROPY_MAKE_EXECUTABLE)
set(MICROPY_MAKE_EXECUTABLE make)
endif()
add_custom_command(
OUTPUT ${MICROPY_MPYCROSS_DEPENDENCY}
COMMAND ${MICROPY_MAKE_EXECUTABLE} -C ${MICROPY_DIR}/mpy-cross
)
endif()
add_custom_command(
OUTPUT ${MICROPY_FROZEN_CONTENT}
COMMAND ${Python3_EXECUTABLE} ${MICROPY_DIR}/tools/makemanifest.py -o ${MICROPY_FROZEN_CONTENT} -v "MPY_DIR=${MICROPY_DIR}" -v "MPY_LIB_DIR=${MICROPY_LIB_DIR}" -v "PORT_DIR=${MICROPY_PORT_DIR}" -v "BOARD_DIR=${MICROPY_BOARD_DIR}" -b "${CMAKE_BINARY_DIR}" -f${MICROPY_CROSS_FLAGS} ${MICROPY_FROZEN_MANIFEST}
DEPENDS MICROPY_FORCE_BUILD
${MICROPY_QSTRDEFS_GENERATED}
${MICROPY_MPYCROSS_DEPENDENCY}
VERBATIM
)
endif()
# Update submodules
if(ECHO_SUBMODULES)
# If cmake is run with GIT_SUBMODULES defined on command line, process the port / board
# settings then print the final GIT_SUBMODULES variable as a fatal error and exit.
message(FATAL_ERROR "GIT_SUBMODULES=${GIT_SUBMODULES}")
endif()

View File

@@ -0,0 +1,237 @@
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 =
# Generate moduledefs.h.
OBJ_EXTRA_ORDER_DEPS += $(HEADER_BUILD)/moduledefs.h
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
# 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 %.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 $@
# Module definitions via MP_REGISTER_MODULE.
$(HEADER_BUILD)/moduledefs.split: $(HEADER_BUILD)/qstr.i.last
$(ECHO) "GEN $@"
$(Q)$(PYTHON) $(PY_SRC)/makeqstrdefs.py split module $< $(HEADER_BUILD)/module _
$(Q)$(TOUCH) $@
$(HEADER_BUILD)/moduledefs.collected: $(HEADER_BUILD)/moduledefs.split
$(ECHO) "GEN $@"
$(Q)$(PYTHON) $(PY_SRC)/makeqstrdefs.py cat module _ $(HEADER_BUILD)/module $@
# 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 ($(MICROPY_MPYCROSS_DEPENDENCY),)
# to automatically build mpy-cross, if needed
$(MICROPY_MPYCROSS_DEPENDENCY):
$(MAKE) -C $(dir $@)
endif
ifneq ($(FROZEN_DIR),)
$(error Support for FROZEN_DIR was removed. Please use manifest.py instead, see https://docs.micropython.org/en/latest/reference/manifest.html)
endif
ifneq ($(FROZEN_MPY_DIR),)
$(error Support for FROZEN_MPY_DIR was removed. Please use manifest.py instead, see https://docs.micropython.org/en/latest/reference/manifest.html)
endif
ifneq ($(FROZEN_MANIFEST),)
# to build frozen_content.c from a manifest
$(BUILD)/frozen_content.c: FORCE $(BUILD)/genhdr/qstrdefs.generated.h | $(MICROPY_MPYCROSS_DEPENDENCY)
$(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)",) --mpy-tool-flags="$(MPY_TOOL_FLAGS)" $(FROZEN_MANIFEST)
endif
ifneq ($(PROG),)
# Build a standalone executable (unix does this)
# The executable should have an .exe extension for builds targetting 'pure'
# Windows, i.e. msvc or mingw builds, but not when using msys or cygwin's gcc.
COMPILER_TARGET := $(shell $(CC) -dumpmachine)
ifneq (,$(findstring mingw,$(COMPILER_TARGET)))
PROG := $(PROG).exe
endif
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) $@
endif
$(Q)$(SIZE) $$(find $(BUILD) -path "$(BUILD)/build/frozen*.o") $@
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 sync $(addprefix $(TOP)/,$(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)
$(Q)$(AR) rcs $(LIBMICROPYTHON) $^
$(LIBMICROPYTHON_EXTRA_CMD)
clean:
$(RM) -rf $(BUILD) $(CLEAN_EXTRA)
.PHONY: clean
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)

View File

@@ -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);
#endif

View File

@@ -0,0 +1,791 @@
/*
* 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 "shared/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_StopIteration(MP_STATE_THREAD(stop_iteration_arg));
} 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_StopIteration(MP_STATE_THREAD(stop_iteration_arg));
} 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) },
#if MICROPY_PY_IO
{ MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&mp_builtin_open_obj) },
#endif
{ 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
MICROPY_PORT_EXTRA_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,
};
MP_REGISTER_MODULE(MP_QSTR_builtins, mp_module_builtins);

View File

@@ -0,0 +1,154 @@
/*
* 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,
};
MP_REGISTER_MODULE(MP_QSTR_cmath, mp_module_cmath);
#endif // MICROPY_PY_BUILTINS_FLOAT && MICROPY_PY_BUILTINS_COMPLEX && MICROPY_PY_CMATH

View File

@@ -0,0 +1,51 @@
/*
* 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,
};
MP_REGISTER_MODULE(MP_QSTR_ucollections, mp_module_collections);
#endif // MICROPY_PY_COLLECTIONS

View File

@@ -0,0 +1,120 @@
/*
* 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,
};
MP_REGISTER_MODULE(MP_QSTR_gc, mp_module_gc);
#endif

View File

@@ -0,0 +1,238 @@
/*
* 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 = mp_obj_malloc_var(mp_obj_bufwriter_t, byte, alloc, 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 mp_type_bufwriter = {
{ &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
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_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(&mp_type_bufwriter) },
#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,
};
MP_REGISTER_MODULE(MP_QSTR_uio, mp_module_io);
#endif

View File

@@ -0,0 +1,440 @@
/*
* 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_rel_tol, ARG_abs_tol };
static const mp_arg_t allowed_args[] = {
{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 - 2, pos_args + 2, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
const mp_float_t a = mp_obj_get_float(pos_args[0]);
const mp_float_t b = mp_obj_get_float(pos_args[1]);
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 },
#if MICROPY_PY_MATH_CONSTANTS
{ MP_ROM_QSTR(MP_QSTR_tau), mp_const_float_tau },
{ MP_ROM_QSTR(MP_QSTR_inf), mp_const_float_inf },
{ MP_ROM_QSTR(MP_QSTR_nan), mp_const_float_nan },
#endif
{ 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,
};
MP_REGISTER_MODULE(MP_QSTR_math, mp_module_math);
#endif // MICROPY_PY_BUILTINS_FLOAT && MICROPY_PY_MATH

View File

@@ -0,0 +1,213 @@
/*
* 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_THREAD(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_THREAD(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,
};
MP_REGISTER_MODULE(MP_QSTR_micropython, mp_module_micropython);

View File

@@ -0,0 +1,271 @@
/*
* 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,
};
MP_REGISTER_MODULE(MP_QSTR_ustruct, mp_module_ustruct);
#endif

View File

@@ -0,0 +1,289 @@
/*
* 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/objmodule.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"
#include "extmod/moduplatform.h"
#include "genhdr/mpversion.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; " MICROPY_BANNER_NAME_AND_VERSION);
// 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) }
};
STATIC const MP_DEFINE_STR_OBJ(mp_sys_implementation_machine_obj, MICROPY_BANNER_MACHINE);
#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_PTR(&mp_sys_implementation_machine_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), \
MP_ROM_PTR(&mp_sys_implementation_machine_obj)
#endif
#if MICROPY_PY_ATTRTUPLE
STATIC const qstr impl_fields[] = {
MP_QSTR_name,
MP_QSTR_version,
MP_QSTR__machine,
#if MICROPY_PERSISTENT_CODE_LOAD
MP_QSTR__mpy,
#endif
};
STATIC MP_DEFINE_ATTRTUPLE(
mp_sys_implementation_obj,
impl_fields,
3 + MICROPY_PERSISTENT_CODE_LOAD,
SYS_IMPLEMENTATION_ELEMS
);
#else
STATIC const mp_rom_obj_tuple_t mp_sys_implementation_obj = {
{&mp_type_tuple},
3 + 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) {
if (n_args == 0) {
mp_raise_type(&mp_type_SystemExit);
} else {
mp_raise_type_arg(&mp_type_SystemExit, args[0]);
}
}
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
#if MICROPY_PY_SYS_ATTR_DELEGATION
STATIC const uint16_t sys_mutable_keys[] = {
#if MICROPY_PY_SYS_PS1_PS2
MP_QSTR_ps1,
MP_QSTR_ps2,
#endif
#if MICROPY_PY_SYS_TRACEBACKLIMIT
MP_QSTR_tracebacklimit,
#endif
MP_QSTRnull,
};
STATIC void mp_module_sys_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) {
MP_STATIC_ASSERT(MP_ARRAY_SIZE(sys_mutable_keys) == MP_SYS_MUTABLE_NUM + 1);
MP_STATIC_ASSERT(MP_ARRAY_SIZE(MP_STATE_VM(sys_mutable)) == MP_SYS_MUTABLE_NUM);
mp_module_generic_attr(attr, dest, sys_mutable_keys, MP_STATE_VM(sys_mutable));
}
#endif
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
#if MICROPY_PY_SYS_ATTR_DELEGATION
// Delegation of attr lookup.
MP_MODULE_ATTR_DELEGATION_ENTRY(&mp_module_sys_attr),
#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,
};
MP_REGISTER_MODULE(MP_QSTR_usys, mp_module_sys);
#endif

View File

@@ -0,0 +1,305 @@
/*
* 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 = mp_obj_malloc(mp_obj_thread_lock_t, &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
// The GC starts off unlocked on this thread.
ts.gc_lock_depth = 0;
ts.mp_pending_exception = MP_OBJ_NULL;
// 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:
// 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,
};
MP_REGISTER_MODULE(MP_QSTR__thread, mp_module_thread);
#endif // MICROPY_PY_THREAD

View File

@@ -0,0 +1,124 @@
/*
* 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,
};
MP_REGISTER_MODULE(MP_QSTR_uerrno, mp_module_uerrno);
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

View File

@@ -0,0 +1,152 @@
/*
* 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
#define MP_ECANCELED (125) // Operation canceled
#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
#define MP_ECANCELED ECANCELED
#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

View File

@@ -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

View File

@@ -0,0 +1,576 @@
/*
* 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 && (size_t)prec < len) {
len = prec;
}
chrs += mp_print_strn(print, str, len, 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
size_t len = strlen(str);
if (prec >= 0 && (size_t)prec < len) {
len = prec;
}
chrs += mp_print_strn(print, str, len, 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;
assert(*fmt == 'u' || *fmt == 'd' || !"unsupported fmt char");
chrs += mp_print_int(print, arg_value, *fmt == 'd', 10, 'a', flags, fill, width);
break;
}
#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;
}

View File

@@ -0,0 +1,82 @@
/*
* 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;
typedef struct _mp_print_ext_t {
mp_print_t base;
const char *item_separator;
const char *key_separator;
}mp_print_ext_t;
#define MP_PRINT_GET_EXT(print) ((mp_print_ext_t *)print)
// 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

View File

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

View File

@@ -0,0 +1,336 @@
/*
* 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.
enum {
#if MICROPY_PY_SYS_PS1_PS2
MP_SYS_MUTABLE_PS1,
MP_SYS_MUTABLE_PS2,
#endif
#if MICROPY_PY_SYS_TRACEBACKLIMIT
MP_SYS_MUTABLE_TRACEBACKLIMIT,
#endif
MP_SYS_MUTABLE_NUM,
};
// 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
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];
// 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;
#if MICROPY_TRACKED_ALLOC
struct _m_tracked_node_t *m_tracked_head;
#endif
// 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;
#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;
#if MICROPY_PY_SYS
// If MICROPY_PY_SYS_PATH_ARGV_DEFAULTS is not enabled then these two lists
// must be initialised after the call to mp_init.
mp_obj_list_t mp_sys_path_obj;
mp_obj_list_t mp_sys_argv_obj;
#if MICROPY_PY_SYS_ATTR_DELEGATION
// Contains mutable sys attributes.
mp_obj_t sys_mutable[MP_SYS_MUTABLE_NUM];
#endif
#endif
// 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)
char *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;
#if MICROPY_SCHEDULER_STATIC_NODES
// These will usually point to statically allocated memory. They are not
// traced by the GC. They are assumed to be zero'd out before mp_init() is
// called (usually because this struct lives in the BSS).
struct _mp_sched_node_t *sched_head;
struct _mp_sched_node_t *sched_tail;
#endif
// These index sched_queue.
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
#if MICROPY_OPT_MAP_LOOKUP_CACHE
// See mp_map_lookup.
uint8_t map_lookup_cache[MICROPY_OPT_MAP_LOOKUP_CACHE_SIZE];
#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
// Locking of the GC is done per thread.
uint16_t gc_lock_depth;
////////////////////////////////////////////////////////////
// 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;
// pending exception object (MP_OBJ_NULL if not pending)
volatile mp_obj_t mp_pending_exception;
// If MP_OBJ_STOP_ITERATION is propagated then this holds its argument.
mp_obj_t stop_iteration_arg;
#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)
#define MP_STATE_MAIN_THREAD(x) (mp_state_ctx.thread.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_MAIN_THREAD(x)
#endif
#endif // MICROPY_INCLUDED_PY_MPSTATE_H

View File

@@ -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

View File

@@ -0,0 +1,154 @@
/*
* 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 {
// Zero has neg=0, len=0. Negative zero is not allowed.
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->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

View File

@@ -0,0 +1,351 @@
/*
* 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_obj_new_closure,
mp_arg_check_num_sig,
mp_setup_code_state_native,
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,
};
#elif MICROPY_EMIT_NATIVE && MICROPY_DYNAMIC_COMPILER
const int mp_fun_table;
#endif // MICROPY_EMIT_NATIVE

View File

@@ -0,0 +1,183 @@
/*
* 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_NEW_CLOSURE,
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, const mp_module_context_t *cm, const mp_obj_t *def_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 (*new_closure)(mp_obj_t fun, size_t n_closed_over, const mp_obj_t *closed);
void (*arg_check_num_sig)(size_t n_args, size_t n_kw, uint32_t sig);
void (*setup_code_state_native)(mp_code_state_native_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;
#if (MICROPY_EMIT_NATIVE && !MICROPY_DYNAMIC_COMPILER) || MICROPY_ENABLE_DYNRUNTIME
extern const mp_fun_table_t mp_fun_table;
#elif MICROPY_EMIT_NATIVE && MICROPY_DYNAMIC_COMPILER
// In dynamic-compiler mode eliminate dependency on entries in mp_fun_table.
// This only needs to be an independent pointer, content doesn't matter.
extern const int mp_fun_table;
#endif
#endif // MICROPY_INCLUDED_PY_NATIVEGLUE_H

View File

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

View File

@@ -0,0 +1,180 @@
/*
* 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_AARCH64 (13)
#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(__aarch64__)
#define MICROPY_NLR_AARCH64 (1)
#define MICROPY_NLR_NUM_REGS (MICROPY_NLR_NUM_REGS_AARCH64)
#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

View File

@@ -0,0 +1,83 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2021 Yonatan Goldschmidt
*
* 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" // needed for NLR defs
#if MICROPY_NLR_AARCH64
// AArch64 callee-saved registers are x19-x29.
// https://en.wikipedia.org/wiki/Calling_convention#ARM_(A64)
// Implemented purely as inline assembly; inside a function, we have to deal with undoing the prologue, restoring
// SP and LR. This way, we don't.
__asm(
#if defined(__APPLE__) && defined(__MACH__)
"_nlr_push: \n"
".global _nlr_push \n"
#else
"nlr_push: \n"
".global nlr_push \n"
#endif
"mov x9, sp \n"
"stp lr, x9, [x0, #16]\n" // 16 == offsetof(nlr_buf_t, regs)
"stp x19, x20, [x0, #32]\n"
"stp x21, x22, [x0, #48]\n"
"stp x23, x24, [x0, #64]\n"
"stp x25, x26, [x0, #80]\n"
"stp x27, x28, [x0, #96]\n"
"str x29, [x0, #112]\n"
#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
);
NORETURN void nlr_jump(void *val) {
MP_NLR_JUMP_HEAD(val, top)
MP_STATIC_ASSERT(offsetof(nlr_buf_t, regs) == 16); // asm assumes it
__asm volatile (
"ldr x29, [%0, #112]\n"
"ldp x27, x28, [%0, #96]\n"
"ldp x25, x26, [%0, #80]\n"
"ldp x23, x24, [%0, #64]\n"
"ldp x21, x22, [%0, #48]\n"
"ldp x19, x20, [%0, #32]\n"
"ldp lr, x9, [%0, #16]\n" // 16 == offsetof(nlr_buf_t, regs)
"mov sp, x9 \n"
"mov x0, #1 \n" // non-local return
"ret \n"
:
: "r" (top)
:
);
MP_UNREACHABLE
}
#endif // MICROPY_NLR_AARCH64

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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 %rdi
#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

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,611 @@
/*
* 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
// Allocates an object and also sets type, for mp_obj_malloc{,_var} macros.
void *mp_obj_malloc_helper(size_t num_bytes, const mp_obj_type_t *type) {
mp_obj_base_t *base = (mp_obj_base_t *)m_malloc(num_bytes);
base->type = type;
return base;
}
const mp_obj_type_t *MICROPY_WRAP_MP_OBJ_GET_TYPE(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

View File

@@ -0,0 +1,693 @@
/*
* 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);
mp_obj_memoryview_init(self, typecode, 0, nitems, 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));
// If the input object is a memoryview then need to point the items of the
// new memoryview to the start of the buffer so the GC can trace it.
if (mp_obj_get_type(args[0]) == &mp_type_memoryview) {
mp_obj_array_t *other = MP_OBJ_TO_PTR(args[0]);
self->memview_offset = other->memview_offset;
self->items = other->items;
}
// 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 int typecode_for_comparison(int typecode, bool *is_unsigned) {
if (typecode == BYTEARRAY_TYPECODE) {
typecode = 'B';
}
if (typecode <= 'Z') {
typecode += 32; // to lowercase
*is_unsigned = true;
}
return typecode;
}
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:
case MP_BINARY_OP_LESS:
case MP_BINARY_OP_LESS_EQUAL:
case MP_BINARY_OP_MORE:
case MP_BINARY_OP_MORE_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;
}
// mp_seq_cmp_bytes is used so only compatible representations can be correctly compared.
// The type doesn't matter: array/bytearray/str/bytes all have the same buffer layout, so
// just check if the typecodes are compatible; for testing equality the types should have the
// same code except for signedness, and not be floating point because nan never equals nan.
// For > and < the types should be the same and unsigned.
// Note that typecode_for_comparison always returns lowercase letters to save code size.
// No need for (& TYPECODE_MASK) here: xxx_get_buffer already takes care of that.
bool is_unsigned = false;
const int lhs_code = typecode_for_comparison(lhs_bufinfo.typecode, &is_unsigned);
const int rhs_code = typecode_for_comparison(rhs_bufinfo.typecode, &is_unsigned);
if (lhs_code == rhs_code && lhs_code != 'f' && lhs_code != 'd' && (op == MP_BINARY_OP_EQUAL || is_unsigned)) {
return mp_obj_new_bool(mp_seq_cmp_bytes(op, lhs_bufinfo.buf, lhs_bufinfo.len, rhs_bufinfo.buf, rhs_bufinfo.len));
}
// mp_obj_equal_not_equal treats returning MP_OBJ_NULL as 'fall back to pointer comparison'
// for MP_BINARY_OP_EQUAL but that is incompatible with CPython.
mp_raise_NotImplementedError(NULL);
}
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 = mp_obj_malloc(mp_obj_array_t, &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 mp_type_array_it = {
{ &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 = &mp_type_array_it;
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

View File

@@ -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

View File

@@ -0,0 +1,94 @@
/*
* 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 = mp_obj_malloc_var(mp_obj_tuple_t, mp_obj_t, n + 1, &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

View File

@@ -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

View File

@@ -0,0 +1,115 @@
/*
* 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 = mp_obj_malloc(mp_obj_bound_meth_t, &mp_type_bound_meth);
o->meth = meth;
o->self = self;
return MP_OBJ_FROM_PTR(o);
}

Some files were not shown because too many files have changed in this diff Show More