diff options
author | Andre Vieira <andre.simoesdiasvieira@arm.com> | 2019-05-16 13:57:57 +0100 |
---|---|---|
committer | Andre Vieira <andre.simoesdiasvieira@arm.com> | 2019-05-16 16:37:09 +0100 |
commit | 143275ea7e56baf5ad7a736698b79a90b473183a (patch) | |
tree | 7c342d48f690ab50fc8ef1d2b70ba0f6f6bd8254 /opcodes/arm-dis.c | |
parent | f08d8ce3cd39f34f26dbc681d873e59a0d7e6780 (diff) | |
download | gdb-143275ea7e56baf5ad7a736698b79a90b473183a.zip gdb-143275ea7e56baf5ad7a736698b79a90b473183a.tar.gz gdb-143275ea7e56baf5ad7a736698b79a90b473183a.tar.bz2 |
[PATCH 39/57][Arm][OBJDUMP] Add support for MVE instructions: vpt, vpst and vcmp
opcodes/ChangeLog:
2019-05-16 Andre Vieira <andre.simoesdiasvieira@arm.com>
Michael Collison <michael.collison@arm.com>
* arm-dis.c (enum mve_instructions): Add new instructions.
(enum mve_unpredictable): Add new values.
(mve_opcodes): Add new instructions.
(vec_condnames): New array with vector conditions.
(mve_predicatenames): New array with predicate suffixes.
(mve_vec_sizename): New array with vector sizes.
(enum vpt_pred_state): New enum with vector predication states.
(struct vpt_block): New struct type for vpt blocks.
(vpt_block_state): Global struct to keep track of state.
(mve_extract_pred_mask): New helper function.
(num_instructions_vpt_block): Likewise.
(mark_outside_vpt_block): Likewise.
(mark_inside_vpt_block): Likewise.
(invert_next_predicate_state): Likewise.
(update_next_predicate_state): Likewise.
(update_vpt_block_state): Likewise.
(is_vpt_instruction): Likewise.
(is_mve_encoding_conflict): Add entries for new instructions.
(is_mve_unpredictable): Likewise.
(print_mve_unpredictable): Handle new cases.
(print_instruction_predicate): Likewise.
(print_mve_size): New function.
(print_vec_condition): New function.
(print_insn_mve): Handle vpt blocks and new print operands.
Diffstat (limited to 'opcodes/arm-dis.c')
-rw-r--r-- | opcodes/arm-dis.c | 626 |
1 files changed, 614 insertions, 12 deletions
diff --git a/opcodes/arm-dis.c b/opcodes/arm-dis.c index ddf707d..84ee48d 100644 --- a/opcodes/arm-dis.c +++ b/opcodes/arm-dis.c @@ -21,6 +21,7 @@ MA 02110-1301, USA. */ #include "sysdep.h" +#include <assert.h> #include "disassemble.h" #include "opcode/arm.h" @@ -69,6 +70,23 @@ struct arm_private_data enum mve_instructions { + MVE_VPST, + MVE_VPT_FP_T1, + MVE_VPT_FP_T2, + MVE_VPT_VEC_T1, + MVE_VPT_VEC_T2, + MVE_VPT_VEC_T3, + MVE_VPT_VEC_T4, + MVE_VPT_VEC_T5, + MVE_VPT_VEC_T6, + MVE_VCMP_FP_T1, + MVE_VCMP_FP_T2, + MVE_VCMP_VEC_T1, + MVE_VCMP_VEC_T2, + MVE_VCMP_VEC_T3, + MVE_VCMP_VEC_T4, + MVE_VCMP_VEC_T5, + MVE_VCMP_VEC_T6, MVE_NONE }; @@ -76,6 +94,10 @@ enum mve_unpredictable { UNPRED_IT_BLOCK, /* Unpredictable because mve insn in it block. */ + UNPRED_FCA_0_FCB_1, /* Unpredictable because fcA = 0 and + fcB = 1 (vpt). */ + UNPRED_R13, /* Unpredictable because r13 (sp) or + r15 (sp) used. */ UNPRED_NONE /* No unpredictable behavior. */ }; @@ -1781,10 +1803,114 @@ static const struct opcode32 neon_opcodes[] = %% % - */ + %i print MVE predicate(s) for vpt and vpst + %n print vector comparison code for predicated instruction + %v print vector predicate for instruction in predicated + block + %<bitfield>Q print as a MVE Q register + %<bitfield>Z as %<>r but r15 is ZR instead of PC and r13 is + UNPREDICTABLE + %<bitfield>s print size for vector predicate & non VMOV instructions +*/ static const struct mopcode32 mve_opcodes[] = { + /* MVE. */ + + {ARM_FEATURE_COPROC (FPU_MVE), + MVE_VPST, + 0xfe310f4d, 0xffbf1fff, + "vpst%i" + }, + + /* Floating point VPT T1. */ + {ARM_FEATURE_COPROC (FPU_MVE_FP), + MVE_VPT_FP_T1, + 0xee310f00, 0xefb10f50, + "vpt%i.f%28s\t%n, %17-19Q, %1-3,5Q"}, + /* Floating point VPT T2. */ + {ARM_FEATURE_COPROC (FPU_MVE_FP), + MVE_VPT_FP_T2, + 0xee310f40, 0xefb10f50, + "vpt%i.f%28s\t%n, %17-19Q, %0-3Z"}, + + /* Vector VPT T1. */ + {ARM_FEATURE_COPROC (FPU_MVE), + MVE_VPT_VEC_T1, + 0xfe010f00, 0xff811f51, + "vpt%i.i%20-21s\t%n, %17-19Q, %1-3,5Q"}, + /* Vector VPT T2. */ + {ARM_FEATURE_COPROC (FPU_MVE), + MVE_VPT_VEC_T2, + 0xfe010f01, 0xff811f51, + "vpt%i.u%20-21s\t%n, %17-19Q, %1-3,5Q"}, + /* Vector VPT T3. */ + {ARM_FEATURE_COPROC (FPU_MVE), + MVE_VPT_VEC_T3, + 0xfe011f00, 0xff811f50, + "vpt%i.s%20-21s\t%n, %17-19Q, %1-3,5Q"}, + /* Vector VPT T4. */ + {ARM_FEATURE_COPROC (FPU_MVE), + MVE_VPT_VEC_T4, + 0xfe010f40, 0xff811f70, + "vpt%i.i%20-21s\t%n, %17-19Q, %0-3Z"}, + /* Vector VPT T5. */ + {ARM_FEATURE_COPROC (FPU_MVE), + MVE_VPT_VEC_T5, + 0xfe010f60, 0xff811f70, + "vpt%i.u%20-21s\t%n, %17-19Q, %0-3Z"}, + /* Vector VPT T6. */ + {ARM_FEATURE_COPROC (FPU_MVE), + MVE_VPT_VEC_T6, + 0xfe011f40, 0xff811f50, + "vpt%i.s%20-21s\t%n, %17-19Q, %0-3Z"}, + + /* Vector VCMP floating point T1. */ + {ARM_FEATURE_COPROC (FPU_MVE_FP), + MVE_VCMP_FP_T1, + 0xee310f00, 0xeff1ef50, + "vcmp%v.f%28s\t%n, %17-19Q, %1-3,5Q"}, + + /* Vector VCMP floating point T2. */ + {ARM_FEATURE_COPROC (FPU_MVE_FP), + MVE_VCMP_FP_T2, + 0xee310f40, 0xeff1ef50, + "vcmp%v.f%28s\t%n, %17-19Q, %0-3Z"}, + + /* Vector VCMP T1. */ + {ARM_FEATURE_COPROC (FPU_MVE), + MVE_VCMP_VEC_T1, + 0xfe010f00, 0xffc1ff51, + "vcmp%v.i%20-21s\t%n, %17-19Q, %1-3,5Q"}, + /* Vector VCMP T2. */ + {ARM_FEATURE_COPROC (FPU_MVE), + MVE_VCMP_VEC_T2, + 0xfe010f01, 0xffc1ff51, + "vcmp%v.u%20-21s\t%n, %17-19Q, %1-3,5Q"}, + /* Vector VCMP T3. */ + {ARM_FEATURE_COPROC (FPU_MVE), + MVE_VCMP_VEC_T3, + 0xfe011f00, 0xffc1ff50, + "vcmp%v.s%20-21s\t%n, %17-19Q, %1-3,5Q"}, + /* Vector VCMP T4. */ + {ARM_FEATURE_COPROC (FPU_MVE), + MVE_VCMP_VEC_T4, + 0xfe010f40, 0xffc1ff70, + "vcmp%v.i%20-21s\t%n, %17-19Q, %0-3Z"}, + /* Vector VCMP T5. */ + {ARM_FEATURE_COPROC (FPU_MVE), + MVE_VCMP_VEC_T5, + 0xfe010f60, 0xffc1ff70, + "vcmp%v.u%20-21s\t%n, %17-19Q, %0-3Z"}, + /* Vector VCMP T6. */ + {ARM_FEATURE_COPROC (FPU_MVE), + MVE_VCMP_VEC_T6, + 0xfe011f40, 0xffc1ff50, + "vcmp%v.s%20-21s\t%n, %17-19Q, %0-3Z"}, + + {ARM_FEATURE_CORE_LOW (0), + MVE_NONE, + 0x00000000, 0x00000000, 0} }; /* Opcode tables: ARM, 16-bit Thumb, 32-bit Thumb. All three are partially @@ -3379,6 +3505,56 @@ static const char *const iwmmxt_cregnames[] = "wcgr0", "wcgr1", "wcgr2", "wcgr3", "reserved", "reserved", "reserved", "reserved" }; +static const char *const vec_condnames[] = +{ "eq", "ne", "cs", "hi", "ge", "lt", "gt", "le" +}; + +static const char *const mve_predicatenames[] = +{ "", "ttt", "tt", "tte", "t", "tee", "te", "tet", "", + "eee", "ee", "eet", "e", "ett", "et", "ete" +}; + +/* Names for 2-bit size field for mve vector isntructions. */ +static const char *const mve_vec_sizename[] = + { "8", "16", "32", "64"}; + +/* Indicates whether we are processing a then predicate, + else predicate or none at all. */ +enum vpt_pred_state +{ + PRED_NONE, + PRED_THEN, + PRED_ELSE +}; + +/* Information used to process a vpt block and subsequent instructions. */ +struct vpt_block +{ + /* Are we in a vpt block. */ + bfd_boolean in_vpt_block; + + /* Next predicate state if in vpt block. */ + enum vpt_pred_state next_pred_state; + + /* Mask from vpt/vpst instruction. */ + long predicate_mask; + + /* Instruction number in vpt block. */ + long current_insn_num; + + /* Number of instructions in vpt block.. */ + long num_pred_insn; +}; + +static struct vpt_block vpt_block_state = +{ + FALSE, + PRED_NONE, + 0, + 0, + 0 +}; + /* Default to GCC register name set. */ static unsigned int regname_selected = 1; @@ -3401,6 +3577,113 @@ static bfd_vma ifthen_address; /* Functions. */ +/* Extract the predicate mask for a VPT or VPST instruction. + The mask is composed of bits 13-15 (Mkl) and bit 22 (Mkh). */ + +static long +mve_extract_pred_mask (long given) +{ + return ((given & 0x00400000) >> 19) | ((given & 0xe000) >> 13); +} + +/* Return the number of instructions in a MVE predicate block. */ +static long +num_instructions_vpt_block (long given) +{ + long mask = mve_extract_pred_mask (given); + if (mask == 0) + return 0; + + if (mask == 8) + return 1; + + if ((mask & 7) == 4) + return 2; + + if ((mask & 3) == 2) + return 3; + + if ((mask & 1) == 1) + return 4; + + return 0; +} + +static void +mark_outside_vpt_block (void) +{ + vpt_block_state.in_vpt_block = FALSE; + vpt_block_state.next_pred_state = PRED_NONE; + vpt_block_state.predicate_mask = 0; + vpt_block_state.current_insn_num = 0; + vpt_block_state.num_pred_insn = 0; +} + +static void +mark_inside_vpt_block (long given) +{ + vpt_block_state.in_vpt_block = TRUE; + vpt_block_state.next_pred_state = PRED_THEN; + vpt_block_state.predicate_mask = mve_extract_pred_mask (given); + vpt_block_state.current_insn_num = 0; + vpt_block_state.num_pred_insn = num_instructions_vpt_block (given); + assert (vpt_block_state.num_pred_insn >= 1); +} + +static enum vpt_pred_state +invert_next_predicate_state (enum vpt_pred_state astate) +{ + if (astate == PRED_THEN) + return PRED_ELSE; + else if (astate == PRED_ELSE) + return PRED_THEN; + else + return PRED_NONE; +} + +static enum vpt_pred_state +update_next_predicate_state (void) +{ + long pred_mask = vpt_block_state.predicate_mask; + long mask_for_insn = 0; + + switch (vpt_block_state.current_insn_num) + { + case 1: + mask_for_insn = 8; + break; + + case 2: + mask_for_insn = 4; + break; + + case 3: + mask_for_insn = 2; + break; + + case 4: + return PRED_NONE; + } + + if (pred_mask & mask_for_insn) + return invert_next_predicate_state (vpt_block_state.next_pred_state); + else + return vpt_block_state.next_pred_state; +} + +static void +update_vpt_block_state (void) +{ + vpt_block_state.current_insn_num++; + if (vpt_block_state.current_insn_num == vpt_block_state.num_pred_insn) + { + /* No more instructions to process in vpt block. */ + mark_outside_vpt_block (); + return; + } + + vpt_block_state.next_pred_state = update_next_predicate_state (); +} /* Decode a bitfield of the form matching regexp (N(-N)?,)*N(-N)?. Returns pointer to following character of the format string and @@ -3504,6 +3787,38 @@ is_mve_architecture (struct disassemble_info *info) return FALSE; } +static bfd_boolean +is_vpt_instruction (long given) +{ + + /* If mkh:mkl is '0000' then its not a vpt/vpst instruction. */ + if ((given & 0x0040e000) == 0) + return FALSE; + + /* VPT floating point T1 variant. */ + if (((given & 0xefb10f50) == 0xee310f00 && ((given & 0x1001) != 0x1)) + /* VPT floating point T2 variant. */ + || ((given & 0xefb10f50) == 0xee310f40) + /* VPT vector T1 variant. */ + || ((given & 0xff811f51) == 0xfe010f00) + /* VPT vector T2 variant. */ + || ((given & 0xff811f51) == 0xfe010f01 + && ((given & 0x300000) != 0x300000)) + /* VPT vector T3 variant. */ + || ((given & 0xff811f50) == 0xfe011f00) + /* VPT vector T4 variant. */ + || ((given & 0xff811f70) == 0xfe010f40) + /* VPT vector T5 variant. */ + || ((given & 0xff811f70) == 0xfe010f60) + /* VPT vector T6 variant. */ + || ((given & 0xff811f50) == 0xfe011f40) + /* VPST vector T variant. */ + || ((given & 0xffbf1fff) == 0xfe310f4d)) + return TRUE; + else + return FALSE; +} + /* Decode a bitfield from opcode GIVEN, with starting bitfield = START and ending bitfield = END. END must be greater than START. */ @@ -3550,7 +3865,69 @@ static bfd_boolean is_mve_encoding_conflict (unsigned long given, enum mve_instructions matched_insn) { - return FALSE; + switch (matched_insn) + { + case MVE_VPST: + if (arm_decode_field_multiple (given, 13, 15, 22, 22) == 0) + return TRUE; + else + return FALSE; + + case MVE_VPT_FP_T1: + if (arm_decode_field_multiple (given, 13, 15, 22, 22) == 0) + return TRUE; + if ((arm_decode_field (given, 12, 12) == 0) + && (arm_decode_field (given, 0, 0) == 1)) + return TRUE; + return FALSE; + + case MVE_VPT_FP_T2: + if (arm_decode_field_multiple (given, 13, 15, 22, 22) == 0) + return TRUE; + if (arm_decode_field (given, 0, 3) == 0xd) + return TRUE; + return FALSE; + + case MVE_VPT_VEC_T1: + case MVE_VPT_VEC_T2: + case MVE_VPT_VEC_T3: + case MVE_VPT_VEC_T4: + case MVE_VPT_VEC_T5: + case MVE_VPT_VEC_T6: + if (arm_decode_field_multiple (given, 13, 15, 22, 22) == 0) + return TRUE; + if (arm_decode_field (given, 20, 21) == 3) + return TRUE; + return FALSE; + + case MVE_VCMP_FP_T1: + if ((arm_decode_field (given, 12, 12) == 0) + && (arm_decode_field (given, 0, 0) == 1)) + return TRUE; + else + return FALSE; + + case MVE_VCMP_FP_T2: + if (arm_decode_field (given, 0, 3) == 0xd) + return TRUE; + else + return FALSE; + + case MVE_VCMP_VEC_T1: + case MVE_VCMP_VEC_T2: + case MVE_VCMP_VEC_T3: + case MVE_VCMP_VEC_T4: + case MVE_VCMP_VEC_T5: + case MVE_VCMP_VEC_T6: + if (arm_decode_field (given, 20, 21) == 3) + return TRUE; + else + return FALSE; + + default: + return FALSE; + + } } /* Return FALSE if GIVEN is not an undefined encoding for MATCHED_INSN. @@ -3576,18 +3953,37 @@ is_mve_unpredictable (unsigned long given, enum mve_instructions matched_insn, { *unpredictable_code = UNPRED_NONE; - return FALSE; -} + switch (matched_insn) + { + case MVE_VCMP_FP_T2: + case MVE_VPT_FP_T2: + if ((arm_decode_field (given, 12, 12) == 0) + && (arm_decode_field (given, 5, 5) == 1)) + { + *unpredictable_code = UNPRED_FCA_0_FCB_1; + return TRUE; + } + else + return FALSE; -#define W_BIT 21 -#define I_BIT 22 -#define U_BIT 23 -#define P_BIT 24 + case MVE_VPT_VEC_T4: + case MVE_VPT_VEC_T5: + case MVE_VPT_VEC_T6: + case MVE_VCMP_VEC_T4: + case MVE_VCMP_VEC_T5: + case MVE_VCMP_VEC_T6: + if (arm_decode_field (given, 0, 3) == 0xd) + { + *unpredictable_code = UNPRED_R13; + return TRUE; + } + else + return FALSE; -#define WRITEBACK_BIT_SET (given & (1 << W_BIT)) -#define IMMEDIATE_BIT_SET (given & (1 << I_BIT)) -#define NEGATIVE_BIT_SET ((given & (1 << U_BIT)) == 0) -#define PRE_BIT_SET (given & (1 << P_BIT)) + default: + return FALSE; + } +} static void print_mve_undefined (struct disassemble_info *info, @@ -3621,11 +4017,154 @@ print_mve_unpredictable (struct disassemble_info *info, func (stream, "mve instruction in it block"); break; + case UNPRED_FCA_0_FCB_1: + func (stream, "condition bits, fca = 0 and fcb = 1"); + break; + + case UNPRED_R13: + func (stream, "use of r13 (sp)"); + break; + case UNPRED_NONE: break; } } +static void +print_instruction_predicate (struct disassemble_info *info) +{ + void *stream = info->stream; + fprintf_ftype func = info->fprintf_func; + + if (vpt_block_state.next_pred_state == PRED_THEN) + func (stream, "t"); + else if (vpt_block_state.next_pred_state == PRED_ELSE) + func (stream, "e"); +} + +static void +print_mve_size (struct disassemble_info *info, + unsigned long size, + enum mve_instructions matched_insn) +{ + void *stream = info->stream; + fprintf_ftype func = info->fprintf_func; + + switch (matched_insn) + { + case MVE_VCMP_VEC_T1: + case MVE_VCMP_VEC_T2: + case MVE_VCMP_VEC_T3: + case MVE_VCMP_VEC_T4: + case MVE_VCMP_VEC_T5: + case MVE_VCMP_VEC_T6: + case MVE_VPT_VEC_T1: + case MVE_VPT_VEC_T2: + case MVE_VPT_VEC_T3: + case MVE_VPT_VEC_T4: + case MVE_VPT_VEC_T5: + case MVE_VPT_VEC_T6: + if (size <= 3) + func (stream, "%s", mve_vec_sizename[size]); + else + func (stream, "<undef size>"); + break; + + case MVE_VCMP_FP_T1: + case MVE_VCMP_FP_T2: + case MVE_VPT_FP_T1: + case MVE_VPT_FP_T2: + if (size == 0) + func (stream, "32"); + else if (size == 1) + func (stream, "16"); + break; + + default: + break; + } +} + +static void +print_vec_condition (struct disassemble_info *info, long given, + enum mve_instructions matched_insn) +{ + void *stream = info->stream; + fprintf_ftype func = info->fprintf_func; + long vec_cond = 0; + + switch (matched_insn) + { + case MVE_VPT_FP_T1: + case MVE_VCMP_FP_T1: + vec_cond = (((given & 0x1000) >> 10) + | ((given & 1) << 1) + | ((given & 0x0080) >> 7)); + func (stream, "%s",vec_condnames[vec_cond]); + break; + + case MVE_VPT_FP_T2: + case MVE_VCMP_FP_T2: + vec_cond = (((given & 0x1000) >> 10) + | ((given & 0x0020) >> 4) + | ((given & 0x0080) >> 7)); + func (stream, "%s",vec_condnames[vec_cond]); + break; + + case MVE_VPT_VEC_T1: + case MVE_VCMP_VEC_T1: + vec_cond = (given & 0x0080) >> 7; + func (stream, "%s",vec_condnames[vec_cond]); + break; + + case MVE_VPT_VEC_T2: + case MVE_VCMP_VEC_T2: + vec_cond = 2 | ((given & 0x0080) >> 7); + func (stream, "%s",vec_condnames[vec_cond]); + break; + + case MVE_VPT_VEC_T3: + case MVE_VCMP_VEC_T3: + vec_cond = 4 | ((given & 1) << 1) | ((given & 0x0080) >> 7); + func (stream, "%s",vec_condnames[vec_cond]); + break; + + case MVE_VPT_VEC_T4: + case MVE_VCMP_VEC_T4: + vec_cond = (given & 0x0080) >> 7; + func (stream, "%s",vec_condnames[vec_cond]); + break; + + case MVE_VPT_VEC_T5: + case MVE_VCMP_VEC_T5: + vec_cond = 2 | ((given & 0x0080) >> 7); + func (stream, "%s",vec_condnames[vec_cond]); + break; + + case MVE_VPT_VEC_T6: + case MVE_VCMP_VEC_T6: + vec_cond = 4 | ((given & 0x0020) >> 4) | ((given & 0x0080) >> 7); + func (stream, "%s",vec_condnames[vec_cond]); + break; + + case MVE_NONE: + case MVE_VPST: + default: + break; + } +} + +#define W_BIT 21 +#define I_BIT 22 +#define U_BIT 23 +#define P_BIT 24 + +#define WRITEBACK_BIT_SET (given & (1 << W_BIT)) +#define IMMEDIATE_BIT_SET (given & (1 << I_BIT)) +#define NEGATIVE_BIT_SET ((given & (1 << U_BIT)) == 0) +#define PRE_BIT_SET (given & (1 << P_BIT)) + + /* Print one coprocessor instruction on INFO->STREAM. Return TRUE if the instuction matched, FALSE if this is not a recognised coprocessor instruction. */ @@ -4975,6 +5514,62 @@ print_insn_mve (struct disassemble_info *info, long given) func (stream, "%%"); break; + case 'c': + if (ifthen_state) + func (stream, "%s", arm_conditional[IFTHEN_COND]); + break; + + case 'i': + { + long mve_mask = mve_extract_pred_mask (given); + func (stream, "%s", mve_predicatenames[mve_mask]); + } + break; + + case 'n': + print_vec_condition (info, given, insn->mve_op); + break; + + case 'v': + print_instruction_predicate (info); + break; + + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + { + int width; + unsigned long value; + + c = arm_decode_bitfield (c, given, &value, &width); + + switch (*c) + { + case 'Z': + if (value == 13) + is_unpredictable = TRUE; + else if (value == 15) + func (stream, "zr"); + else + func (stream, "%s", arm_regnames[value]); + break; + case 's': + print_mve_size (info, + value, + insn->mve_op); + break; + case 'Q': + if (value & 0x8) + func (stream, "<illegal reg q%ld.5>", value); + else + func (stream, "q%ld", value); + break; + default: + abort (); + } + break; + default: + abort (); + } } } else @@ -4990,6 +5585,13 @@ print_insn_mve (struct disassemble_info *info, long given) if (is_undefined) print_mve_undefined (info, undefined_cond); + if ((vpt_block_state.in_vpt_block == FALSE) + && !ifthen_state + && (is_vpt_instruction (given) == TRUE)) + mark_inside_vpt_block (given); + else if (vpt_block_state.in_vpt_block == TRUE) + update_vpt_block_state (); + return TRUE; } } |