2016-12-09 17:03:33 +11:00
/*
* 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 {
2016-12-09 20:35:21 +11:00
asm_xtensa_t as ;
2016-12-09 17:03:33 +11:00
uint16_t pass ;
mp_obj_t * error_slot ;
mp_uint_t max_num_labels ;
qstr * label_lookup ;
} ;
2020-03-02 22:35:22 +11:00
STATIC void emit_inline_xtensa_error_msg ( emit_inline_asm_t * emit , mp_rom_error_text_t msg ) {
2016-12-09 17:03:33 +11:00
* 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 ) ;
2016-12-09 20:35:21 +11:00
memset ( & emit - > as , 0 , sizeof ( emit - > as ) ) ;
mp_asm_base_init ( & emit - > as . base , max_num_labels ) ;
2016-12-09 17:03:33 +11:00
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 ) ;
2016-12-09 20:35:21 +11:00
mp_asm_base_deinit ( & emit - > as . base , false ) ;
2016-12-09 17:03:33 +11:00
m_del_obj ( emit_inline_asm_t , emit ) ;
}
2016-12-09 21:23:17 +11:00
STATIC void emit_inline_xtensa_start_pass ( emit_inline_asm_t * emit , pass_kind_t pass , mp_obj_t * error_slot ) {
2016-12-09 17:03:33 +11:00
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 ) ) ;
}
2016-12-09 20:35:21 +11:00
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 ) ;
2016-12-09 17:03:33 +11:00
}
STATIC void emit_inline_xtensa_end_pass ( emit_inline_asm_t * emit , mp_uint_t type_sig ) {
2016-12-09 20:35:21 +11:00
asm_xtensa_exit ( & emit - > as ) ;
asm_xtensa_end_pass ( & emit - > as ) ;
2016-12-09 17:03:33 +11:00
}
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 ) {
2020-03-02 22:35:22 +11:00
emit_inline_xtensa_error_msg ( emit , MP_ERROR_TEXT ( " can only have up to 4 parameters to Xtensa assembly " ) ) ;
2016-12-09 17:03:33 +11:00
return 0 ;
}
for ( mp_uint_t i = 0 ; i < n_params ; i + + ) {
if ( ! MP_PARSE_NODE_IS_ID ( pn_params [ i ] ) ) {
2020-03-02 22:35:22 +11:00
emit_inline_xtensa_error_msg ( emit , MP_ERROR_TEXT ( " parameters must be registers in sequence a2 to a5 " ) ) ;
2016-12-09 17:03:33 +11:00
return 0 ;
}
const char * p = qstr_str ( MP_PARSE_NODE_LEAF_ARG ( pn_params [ i ] ) ) ;
if ( ! ( strlen ( p ) = = 2 & & p [ 0 ] = = ' a ' & & p [ 1 ] = = ' 2 ' + i ) ) {
2020-03-02 22:35:22 +11:00
emit_inline_xtensa_error_msg ( emit , MP_ERROR_TEXT ( " parameters must be registers in sequence a2 to a5 " ) ) ;
2016-12-09 17:03:33 +11:00
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 ;
2016-12-09 20:35:21 +11:00
mp_asm_base_label_assign ( & emit - > as . base , label_num ) ;
2016-12-09 17:03:33 +11:00
return true ;
}
2020-02-27 15:36:53 +11:00
typedef struct _reg_name_t { byte reg ;
byte name [ 3 ] ;
} reg_name_t ;
2016-12-09 17:03:33 +11:00
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 ,
2020-03-02 22:35:22 +11:00
MP_ERROR_TEXT ( " '%s' expects a register " ) , op ) ) ;
2016-12-09 17:03:33 +11:00
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 ) ) {
2020-03-02 22:35:22 +11:00
emit_inline_xtensa_error_exc ( emit , mp_obj_new_exception_msg_varg ( & mp_type_SyntaxError , MP_ERROR_TEXT ( " '%s' expects an integer " ) , op ) ) ;
2016-12-09 17:03:33 +11:00
return 0 ;
}
uint32_t i = mp_obj_get_int_truncated ( o ) ;
if ( min ! = max & & ( ( int ) i < min | | ( int ) i > max ) ) {
2020-03-02 22:35:22 +11:00
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 ) ) ;
2016-12-09 17:03:33 +11:00
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 ) ) {
2020-03-02 22:35:22 +11:00
emit_inline_xtensa_error_exc ( emit , mp_obj_new_exception_msg_varg ( & mp_type_SyntaxError , MP_ERROR_TEXT ( " '%s' expects a label " ) , op ) ) ;
2016-12-09 17:03:33 +11:00
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 ) {
2020-03-02 22:35:22 +11:00
emit_inline_xtensa_error_exc ( emit , mp_obj_new_exception_msg_varg ( & mp_type_SyntaxError , MP_ERROR_TEXT ( " label '%q' not defined " ) , label_qstr ) ) ;
2016-12-09 17:03:33 +11:00
}
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 ;
2020-02-27 15:36:53 +11:00
const char * op_str = ( const char * ) qstr_data ( op , & op_len ) ;
2016-12-09 17:03:33 +11:00
if ( n_args = = 0 ) {
if ( op = = MP_QSTR_ret_n ) {
2016-12-09 20:35:21 +11:00
asm_xtensa_op_ret_n ( & emit - > as ) ;
2016-12-09 17:03:33 +11:00
} 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 ] ) ;
2016-12-09 20:35:21 +11:00
asm_xtensa_op_callx0 ( & emit - > as , r0 ) ;
2016-12-09 17:03:33 +11:00
} else if ( op = = MP_QSTR_j ) {
int label = get_arg_label ( emit , op_str , pn_args [ 0 ] ) ;
2016-12-09 20:35:21 +11:00
asm_xtensa_j_label ( & emit - > as , label ) ;
2016-12-09 17:03:33 +11:00
} else if ( op = = MP_QSTR_jx ) {
uint r0 = get_arg_reg ( emit , op_str , pn_args [ 0 ] ) ;
2016-12-09 20:35:21 +11:00
asm_xtensa_op_jx ( & emit - > as , r0 ) ;
2016-12-09 17:03:33 +11:00
} 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 ] ) ;
2016-12-09 20:35:21 +11:00
asm_xtensa_bccz_reg_label ( & emit - > as , ASM_XTENSA_CCZ_EQ , r0 , label ) ;
2016-12-09 17:03:33 +11:00
} else if ( op = = MP_QSTR_bnez ) {
int label = get_arg_label ( emit , op_str , pn_args [ 1 ] ) ;
2016-12-09 20:35:21 +11:00
asm_xtensa_bccz_reg_label ( & emit - > as , ASM_XTENSA_CCZ_NE , r0 , label ) ;
2016-12-09 17:03:33 +11:00
} 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 ] ) ;
2016-12-09 20:35:21 +11:00
asm_xtensa_op_mov_n ( & emit - > as , r0 , r1 ) ;
2016-12-09 17:03:33 +11:00
} 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 ) ;
2016-12-09 20:35:21 +11:00
asm_xtensa_mov_reg_i32 ( & emit - > as , r0 , imm ) ;
2016-12-09 17:03:33 +11:00
} 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 ] ) ;
2016-12-09 20:35:21 +11:00
asm_xtensa_op24 ( & emit - > as , ASM_XTENSA_ENCODE_RRR ( 0 , o - > a0 , o - > a1 , r0 , r1 , r2 ) ) ;
2016-12-09 17:03:33 +11:00
} else if ( o - > type = = RRI8_B ) {
int label = get_arg_label ( emit , op_str , pn_args [ 2 ] ) ;
2016-12-09 20:35:21 +11:00
asm_xtensa_bcc_reg_reg_label ( & emit - > as , o - > a0 , r0 , r1 , label ) ;
2016-12-09 17:03:33 +11:00
} 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 ) ;
2016-12-09 20:35:21 +11:00
asm_xtensa_op24 ( & emit - > as , ASM_XTENSA_ENCODE_RRI8 ( o - > a0 , o - > a1 , r1 , r0 , ( imm > > shift ) & 0xff ) ) ;
2016-12-09 17:03:33 +11:00
}
return ;
}
}
goto unknown_op ;
} else {
goto unknown_op ;
}
return ;
unknown_op :
2020-03-02 22:35:22 +11:00
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 ) ) ;
2016-12-09 17:03:33 +11:00
return ;
/*
branch_not_in_range:
2020-03-02 22:35:22 +11:00
emit_inline_xtensa_error_msg(emit, MP_ERROR_TEXT("branch not in range"));
2016-12-09 17:03:33 +11:00
return;
*/
}
const emit_inline_asm_method_table_t emit_inline_xtensa_method_table = {
2019-03-09 12:32:09 +11:00
# if MICROPY_DYNAMIC_COMPILER
emit_inline_xtensa_new ,
emit_inline_xtensa_free ,
# endif
2016-12-09 17:03:33 +11:00
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