diff options
author | sandra <sandra@138bc75d-0d04-0410-961f-82ee72b054a4> | 2007-07-05 17:08:37 +0000 |
---|---|---|
committer | sandra <sandra@138bc75d-0d04-0410-961f-82ee72b054a4> | 2007-07-05 17:08:37 +0000 |
commit | 122a697832d3f4ee948f12fd2676840cdbb37a9b (patch) | |
tree | 412155942760389b80c2e11cd433bae27b0a1df4 /gcc | |
parent | 81e6afc2ae19f1ae98d1bad1f2ed79f583325b3c (diff) | |
download | gcc-122a697832d3f4ee948f12fd2676840cdbb37a9b.tar.gz |
2007-07-05 Sandra Loosemore <sandra@codesourcery.com>
David Ung <davidu@mips.com>
Add support for SmartMIPS ASE.
gcc/
* optabs.c (expand_binop_directly): New, broken out from...
(expand_binop): Here. Make it try rotating in the other
direction even when the second operand isn't constant.
* config/mips/mips.md (*lwxs): New.
* config/mips/mips.opt (msmartmips): New.
* config/mips/mips.c (mips_lwxs_address_p): New.
(mips_rtx_costs): Make it recognize scaled indexed addressing.
* config/mips/mips.h (TARGET_CPU_CPP_BUILTINS): Define
__mips_smartmips when compiling for TARGET_SMARTMIPS.
(ISA_HAS_ROR): Define for TARGET_SMARTMIPS.
(ISA_HAS_LWXS): New.
(ASM_SPEC): Add -msmartmips/-mno-smartmips.
* doc/invoke.texi (MIPS Options): Document -msmartmips/-mno-smartmips.
* testsuite/gcc.target/mips/smartmips-lwxs.c: New test case.
* testsuite/gcc.target/mips/smartmips-ror-1.c: New test case.
* testsuite/gcc.target/mips/smartmips-ror-2.c: New test case.
* testsuite/gcc.target/mips/smartmips-ror-3.c: New test case.
* testsuite/gcc.target/mips/smartmips-ror-4.c: New test case.
git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@126370 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/ChangeLog | 24 | ||||
-rw-r--r-- | gcc/config/mips/mips.c | 32 | ||||
-rw-r--r-- | gcc/config/mips/mips.h | 10 | ||||
-rw-r--r-- | gcc/config/mips/mips.md | 15 | ||||
-rw-r--r-- | gcc/config/mips/mips.opt | 4 | ||||
-rw-r--r-- | gcc/doc/invoke.texi | 7 | ||||
-rw-r--r-- | gcc/optabs.c | 246 | ||||
-rw-r--r-- | gcc/testsuite/gcc.target/mips/smartmips-lwxs.c | 8 | ||||
-rw-r--r-- | gcc/testsuite/gcc.target/mips/smartmips-ror-1.c | 8 | ||||
-rw-r--r-- | gcc/testsuite/gcc.target/mips/smartmips-ror-2.c | 8 | ||||
-rw-r--r-- | gcc/testsuite/gcc.target/mips/smartmips-ror-3.c | 10 | ||||
-rw-r--r-- | gcc/testsuite/gcc.target/mips/smartmips-ror-4.c | 10 |
12 files changed, 269 insertions, 113 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog index e39fb143119..2fecaf1c0cb 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,27 @@ +2007-07-05 Sandra Loosemore <sandra@codesourcery.com> + David Ung <davidu@mips.com> + + Add support for SmartMIPS ASE. + + * optabs.c (expand_binop_directly): New, broken out from... + (expand_binop): Here. Make it try rotating in the other + direction even when the second operand isn't constant. + * config/mips/mips.md (*lwxs): New. + * config/mips/mips.opt (msmartmips): New. + * config/mips/mips.c (mips_lwxs_address_p): New. + (mips_rtx_costs): Make it recognize scaled indexed addressing. + * config/mips/mips.h (TARGET_CPU_CPP_BUILTINS): Define + __mips_smartmips when compiling for TARGET_SMARTMIPS. + (ISA_HAS_ROR): Define for TARGET_SMARTMIPS. + (ISA_HAS_LWXS): New. + (ASM_SPEC): Add -msmartmips/-mno-smartmips. + * doc/invoke.texi (MIPS Options): Document -msmartmips/-mno-smartmips. + * testsuite/gcc.target/mips/smartmips-lwxs.c: New test case. + * testsuite/gcc.target/mips/smartmips-ror-1.c: New test case. + * testsuite/gcc.target/mips/smartmips-ror-2.c: New test case. + * testsuite/gcc.target/mips/smartmips-ror-3.c: New test case. + * testsuite/gcc.target/mips/smartmips-ror-4.c: New test case. + 2007-07-05 Dorit Nuzman <dorit@il.ibm.com> * tree-vectorizer.c (new_loop_vec_info): Initialize diff --git a/gcc/config/mips/mips.c b/gcc/config/mips/mips.c index da93ddcd36f..7b101e32555 100644 --- a/gcc/config/mips/mips.c +++ b/gcc/config/mips/mips.c @@ -2682,6 +2682,26 @@ m16_nsimm8_8 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED) return m16_check_op (op, (- 0x7f) << 3, 0x80 << 3, 7); } +/* Return true if ADDR matches the pattern for the lwxs load scaled indexed + address instruction. */ + +static bool +mips_lwxs_address_p (rtx addr) +{ + if (ISA_HAS_LWXS + && GET_CODE (addr) == PLUS + && REG_P (XEXP (addr, 1))) + { + rtx offset = XEXP (addr, 0); + if (GET_CODE (offset) == MULT + && REG_P (XEXP (offset, 0)) + && GET_CODE (XEXP (offset, 1)) == CONST_INT + && INTVAL (XEXP (offset, 1)) == 4) + return true; + } + return false; +} + static bool mips_rtx_costs (rtx x, int code, int outer_code, int *total) { @@ -2778,13 +2798,21 @@ mips_rtx_costs (rtx x, int code, int outer_code, int *total) case MEM: { /* If the address is legitimate, return the number of - instructions it needs, otherwise use the default handling. */ - int n = mips_address_insns (XEXP (x, 0), GET_MODE (x)); + instructions it needs. */ + rtx addr = XEXP (x, 0); + int n = mips_address_insns (addr, GET_MODE (x)); if (n > 0) { *total = COSTS_N_INSNS (n + 1); return true; } + /* Check for scaled indexed address. */ + if (mips_lwxs_address_p (addr)) + { + *total = COSTS_N_INSNS (2); + return true; + } + /* Otherwise use the default handling. */ return false; } diff --git a/gcc/config/mips/mips.h b/gcc/config/mips/mips.h index 8338829e941..2425d13ab1e 100644 --- a/gcc/config/mips/mips.h +++ b/gcc/config/mips/mips.h @@ -366,6 +366,9 @@ extern const struct mips_rtx_cost_data *mips_cost; \ if (TARGET_MIPS3D) \ builtin_define ("__mips3d"); \ + \ + if (TARGET_SMARTMIPS) \ + builtin_define ("__mips_smartmips"); \ \ if (TARGET_DSP) \ builtin_define ("__mips_dsp"); \ @@ -733,7 +736,8 @@ extern const struct mips_rtx_cost_data *mips_cost; #define ISA_HAS_ROR ((ISA_MIPS32R2 \ || TARGET_MIPS5400 \ || TARGET_MIPS5500 \ - || TARGET_SR71K) \ + || TARGET_SR71K \ + || TARGET_SMARTMIPS) \ && !TARGET_MIPS16) /* ISA has data prefetch instructions. This controls use of 'pref'. */ @@ -768,6 +772,9 @@ extern const struct mips_rtx_cost_data *mips_cost; /* ISA has instructions for accessing top part of 64-bit fp regs. */ #define ISA_HAS_MXHC1 (TARGET_FLOAT64 && ISA_MIPS32R2) +/* ISA has lwxs instruction (load w/scaled index address. */ +#define ISA_HAS_LWXS (TARGET_SMARTMIPS && !TARGET_MIPS16) + /* True if the result of a load is not available to the next instruction. A nop will then be needed between instructions like "lw $4,..." and "addiu $4,$4,1". */ @@ -883,6 +890,7 @@ extern const struct mips_rtx_cost_data *mips_cost; %{mdmx} %{mno-mdmx:-no-mdmx} \ %{mdsp} %{mno-dsp} \ %{mdspr2} %{mno-dspr2} \ +%{msmartmips} %{mno-smartmips} \ %{mmt} %{mno-mt} \ %{mfix-vr4120} %{mfix-vr4130} \ %(subtarget_asm_optimizing_spec) \ diff --git a/gcc/config/mips/mips.md b/gcc/config/mips/mips.md index 905b365dfa7..2b7a2d2368f 100644 --- a/gcc/config/mips/mips.md +++ b/gcc/config/mips/mips.md @@ -3654,6 +3654,21 @@ [(set_attr "type" "fpidxstore") (set_attr "mode" "<ANYF:UNITMODE>")]) +;; Scaled indexed address load. +;; Per md.texi, we only need to look for a pattern with multiply in the +;; address expression, not shift. + +(define_insn "*lwxs" + [(set (match_operand:SI 0 "register_operand" "=d") + (mem:SI (plus:SI (mult:SI (match_operand:SI 1 "register_operand" "d") + (const_int 4)) + (match_operand:SI 2 "register_operand" "d"))))] + "ISA_HAS_LWXS" + "lwxs\t%0,%1(%2)" + [(set_attr "type" "load") + (set_attr "mode" "SI") + (set_attr "length" "4")]) + ;; 16-bit Integer moves ;; Unlike most other insns, the move insns can't be split with diff --git a/gcc/config/mips/mips.opt b/gcc/config/mips/mips.opt index f0c2dbf4c33..f7e751fb713 100644 --- a/gcc/config/mips/mips.opt +++ b/gcc/config/mips/mips.opt @@ -209,6 +209,10 @@ msingle-float Target Report RejectNegative Mask(SINGLE_FLOAT) Restrict the use of hardware floating-point instructions to 32-bit operations +msmartmips +Target Report RejectNegative Mask(SMARTMIPS) +Use SmartMIPS instructions + msoft-float Target Report RejectNegative Mask(SOFT_FLOAT) Prevent the use of all hardware floating-point instructions diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index b1bbcf0bb02..f7523c28b3a 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -622,6 +622,7 @@ Objective-C and Objective-C++ Dialects}. -mshared -mno-shared -mxgot -mno-xgot -mgp32 -mgp64 @gol -mfp32 -mfp64 -mhard-float -msoft-float @gol -msingle-float -mdouble-float -mdsp -mno-dsp -mdspr2 -mno-dspr2 @gol +-msmartmips -mno-smartmips @gol -mpaired-single -mno-paired-single -mdmx -mno-mdmx @gol -mips3d -mno-mips3d -mmt -mno-mt @gol -mlong64 -mlong32 -msym32 -mno-sym32 @gol @@ -11662,6 +11663,12 @@ Use (do not use) the MIPS DSP ASE. @xref{MIPS DSP Built-in Functions}. Use (do not use) the MIPS DSP ASE REV 2. @xref{MIPS DSP Built-in Functions}. The option @option{-mdspr2} implies @option{-mdsp}. +@item -msmartmips +@itemx -mno-smartmips +@opindex msmartmips +@opindex mno-smartmips +Use (do not use) the MIPS SmartMIPS ASE. + @item -mpaired-single @itemx -mno-paired-single @opindex mpaired-single diff --git a/gcc/optabs.c b/gcc/optabs.c index dd146da8b6d..c07cc06abd6 100644 --- a/gcc/optabs.c +++ b/gcc/optabs.c @@ -1246,6 +1246,113 @@ swap_commutative_operands_with_target (rtx target, rtx op0, rtx op1) } +/* Helper function for expand_binop: handle the case where there + is an insn that directly implements the indicated operation. + Returns null if this is not possible. */ +static rtx +expand_binop_directly (enum machine_mode mode, optab binoptab, + rtx op0, rtx op1, + rtx target, int unsignedp, enum optab_methods methods, + int commutative_op, rtx last) +{ + int icode = (int) binoptab->handlers[(int) mode].insn_code; + enum machine_mode mode0 = insn_data[icode].operand[1].mode; + enum machine_mode mode1 = insn_data[icode].operand[2].mode; + enum machine_mode tmp_mode; + rtx pat; + rtx xop0 = op0, xop1 = op1; + rtx temp; + + if (target) + temp = target; + else + temp = gen_reg_rtx (mode); + + /* If it is a commutative operator and the modes would match + if we would swap the operands, we can save the conversions. */ + if (commutative_op) + { + if (GET_MODE (op0) != mode0 && GET_MODE (op1) != mode1 + && GET_MODE (op0) == mode1 && GET_MODE (op1) == mode0) + { + rtx tmp; + + tmp = op0; op0 = op1; op1 = tmp; + tmp = xop0; xop0 = xop1; xop1 = tmp; + } + } + + /* In case the insn wants input operands in modes different from + those of the actual operands, convert the operands. It would + seem that we don't need to convert CONST_INTs, but we do, so + that they're properly zero-extended, sign-extended or truncated + for their mode. */ + + if (GET_MODE (op0) != mode0 && mode0 != VOIDmode) + xop0 = convert_modes (mode0, + GET_MODE (op0) != VOIDmode + ? GET_MODE (op0) + : mode, + xop0, unsignedp); + + if (GET_MODE (op1) != mode1 && mode1 != VOIDmode) + xop1 = convert_modes (mode1, + GET_MODE (op1) != VOIDmode + ? GET_MODE (op1) + : mode, + xop1, unsignedp); + + /* Now, if insn's predicates don't allow our operands, put them into + pseudo regs. */ + + if (!insn_data[icode].operand[1].predicate (xop0, mode0) + && mode0 != VOIDmode) + xop0 = copy_to_mode_reg (mode0, xop0); + + if (!insn_data[icode].operand[2].predicate (xop1, mode1) + && mode1 != VOIDmode) + xop1 = copy_to_mode_reg (mode1, xop1); + + if (binoptab == vec_pack_trunc_optab + || binoptab == vec_pack_usat_optab + || binoptab == vec_pack_ssat_optab + || binoptab == vec_pack_ufix_trunc_optab + || binoptab == vec_pack_sfix_trunc_optab) + { + /* The mode of the result is different then the mode of the + arguments. */ + tmp_mode = insn_data[icode].operand[0].mode; + if (GET_MODE_NUNITS (tmp_mode) != 2 * GET_MODE_NUNITS (mode)) + return 0; + } + else + tmp_mode = mode; + + if (!insn_data[icode].operand[0].predicate (temp, tmp_mode)) + temp = gen_reg_rtx (tmp_mode); + + pat = GEN_FCN (icode) (temp, xop0, xop1); + if (pat) + { + /* If PAT is composed of more than one insn, try to add an appropriate + REG_EQUAL note to it. If we can't because TEMP conflicts with an + operand, call expand_binop again, this time without a target. */ + if (INSN_P (pat) && NEXT_INSN (pat) != NULL_RTX + && ! add_equal_note (pat, temp, binoptab->code, xop0, xop1)) + { + delete_insns_since (last); + return expand_binop (mode, binoptab, op0, op1, NULL_RTX, + unsignedp, methods); + } + + emit_insn (pat); + return temp; + } + + delete_insns_since (last); + return NULL_RTX; +} + /* Generate code to perform an operation specified by BINOPTAB on operands OP0 and OP1, with result having machine-mode MODE. @@ -1275,7 +1382,6 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1, || binoptab->code == ROTATERT); rtx entry_last = get_last_insn (); rtx last; - bool first_pass_p = true; class = GET_MODE_CLASS (mode); @@ -1329,123 +1435,43 @@ expand_binop (enum machine_mode mode, optab binoptab, rtx op0, rtx op1, } } - retry: - /* If we can do it with a three-operand insn, do so. */ if (methods != OPTAB_MUST_WIDEN && binoptab->handlers[(int) mode].insn_code != CODE_FOR_nothing) { - int icode = (int) binoptab->handlers[(int) mode].insn_code; - enum machine_mode mode0 = insn_data[icode].operand[1].mode; - enum machine_mode mode1 = insn_data[icode].operand[2].mode; - enum machine_mode tmp_mode; - rtx pat; - rtx xop0 = op0, xop1 = op1; - - if (target) - temp = target; - else - temp = gen_reg_rtx (mode); - - /* If it is a commutative operator and the modes would match - if we would swap the operands, we can save the conversions. */ - if (commutative_op) - { - if (GET_MODE (op0) != mode0 && GET_MODE (op1) != mode1 - && GET_MODE (op0) == mode1 && GET_MODE (op1) == mode0) - { - rtx tmp; - - tmp = op0; op0 = op1; op1 = tmp; - tmp = xop0; xop0 = xop1; xop1 = tmp; - } - } - - /* In case the insn wants input operands in modes different from - those of the actual operands, convert the operands. It would - seem that we don't need to convert CONST_INTs, but we do, so - that they're properly zero-extended, sign-extended or truncated - for their mode. */ - - if (GET_MODE (op0) != mode0 && mode0 != VOIDmode) - xop0 = convert_modes (mode0, - GET_MODE (op0) != VOIDmode - ? GET_MODE (op0) - : mode, - xop0, unsignedp); - - if (GET_MODE (op1) != mode1 && mode1 != VOIDmode) - xop1 = convert_modes (mode1, - GET_MODE (op1) != VOIDmode - ? GET_MODE (op1) - : mode, - xop1, unsignedp); - - /* Now, if insn's predicates don't allow our operands, put them into - pseudo regs. */ - - if (!insn_data[icode].operand[1].predicate (xop0, mode0) - && mode0 != VOIDmode) - xop0 = copy_to_mode_reg (mode0, xop0); - - if (!insn_data[icode].operand[2].predicate (xop1, mode1) - && mode1 != VOIDmode) - xop1 = copy_to_mode_reg (mode1, xop1); - - if (binoptab == vec_pack_trunc_optab - || binoptab == vec_pack_usat_optab - || binoptab == vec_pack_ssat_optab - || binoptab == vec_pack_ufix_trunc_optab - || binoptab == vec_pack_sfix_trunc_optab) - { - /* The mode of the result is different then the mode of the - arguments. */ - tmp_mode = insn_data[icode].operand[0].mode; - if (GET_MODE_NUNITS (tmp_mode) != 2 * GET_MODE_NUNITS (mode)) - return 0; - } - else - tmp_mode = mode; - - if (!insn_data[icode].operand[0].predicate (temp, tmp_mode)) - temp = gen_reg_rtx (tmp_mode); - - pat = GEN_FCN (icode) (temp, xop0, xop1); - if (pat) - { - /* If PAT is composed of more than one insn, try to add an appropriate - REG_EQUAL note to it. If we can't because TEMP conflicts with an - operand, call ourselves again, this time without a target. */ - if (INSN_P (pat) && NEXT_INSN (pat) != NULL_RTX - && ! add_equal_note (pat, temp, binoptab->code, xop0, xop1)) - { - delete_insns_since (last); - return expand_binop (mode, binoptab, op0, op1, NULL_RTX, - unsignedp, methods); - } - - emit_insn (pat); - return temp; - } - else - delete_insns_since (last); + temp = expand_binop_directly (mode, binoptab, op0, op1, target, + unsignedp, methods, commutative_op, last); + if (temp) + return temp; } - /* If we were trying to rotate by a constant value, and that didn't - work, try rotating the other direction before falling back to - shifts and bitwise-or. */ - if (first_pass_p - && (binoptab == rotl_optab || binoptab == rotr_optab) - && class == MODE_INT - && GET_CODE (op1) == CONST_INT - && INTVAL (op1) > 0 - && (unsigned int) INTVAL (op1) < GET_MODE_BITSIZE (mode)) + /* If we were trying to rotate, and that didn't work, try rotating + the other direction before falling back to shifts and bitwise-or. */ + if (((binoptab == rotl_optab + && rotr_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing) + || (binoptab == rotr_optab + && rotl_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)) + && class == MODE_INT) { - first_pass_p = false; - op1 = GEN_INT (GET_MODE_BITSIZE (mode) - INTVAL (op1)); - binoptab = binoptab == rotl_optab ? rotr_optab : rotl_optab; - goto retry; + optab otheroptab = (binoptab == rotl_optab ? rotr_optab : rotl_optab); + rtx newop1; + int bits = GET_MODE_BITSIZE (mode); + + if (GET_CODE (op1) == CONST_INT) + newop1 = GEN_INT (bits - INTVAL (op1)); + else if (targetm.shift_truncation_mask (mode) == bits - 1) + newop1 = negate_rtx (mode, op1); + else + newop1 = expand_binop (mode, sub_optab, + GEN_INT (bits), op1, + NULL_RTX, unsignedp, OPTAB_DIRECT); + + temp = expand_binop_directly (mode, otheroptab, op0, newop1, + target, unsignedp, methods, + commutative_op, last); + if (temp) + return temp; } /* If this is a multiply, see if we can do a widening operation that diff --git a/gcc/testsuite/gcc.target/mips/smartmips-lwxs.c b/gcc/testsuite/gcc.target/mips/smartmips-lwxs.c new file mode 100644 index 00000000000..cd9b0b3950f --- /dev/null +++ b/gcc/testsuite/gcc.target/mips/smartmips-lwxs.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ +/* { dg-mips-options "-O -msmartmips -mno-mips16" } */ + +int scaled_indexed_word_load (int a[], int b) +{ + return a[b]; +} +/* { dg-final { scan-assembler "\tlwxs\t" } } */ diff --git a/gcc/testsuite/gcc.target/mips/smartmips-ror-1.c b/gcc/testsuite/gcc.target/mips/smartmips-ror-1.c new file mode 100644 index 00000000000..5ad7f3424c8 --- /dev/null +++ b/gcc/testsuite/gcc.target/mips/smartmips-ror-1.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ +/* { dg-mips-options "-O -msmartmips -mno-mips16" } */ + +int rotate_left (unsigned a, unsigned s) +{ + return (a << s) | (a >> (32 - s)); +} +/* { dg-final { scan-assembler "\tror\t" } } */ diff --git a/gcc/testsuite/gcc.target/mips/smartmips-ror-2.c b/gcc/testsuite/gcc.target/mips/smartmips-ror-2.c new file mode 100644 index 00000000000..93d376d537d --- /dev/null +++ b/gcc/testsuite/gcc.target/mips/smartmips-ror-2.c @@ -0,0 +1,8 @@ +/* { dg-do compile } */ +/* { dg-mips-options "-O -msmartmips -mno-mips16" } */ + +int rotate_right (unsigned a, unsigned s) +{ + return (a >> s) | (a << (32 - s)); +} +/* { dg-final { scan-assembler "\tror\t" } } */ diff --git a/gcc/testsuite/gcc.target/mips/smartmips-ror-3.c b/gcc/testsuite/gcc.target/mips/smartmips-ror-3.c new file mode 100644 index 00000000000..ec1c6e27e59 --- /dev/null +++ b/gcc/testsuite/gcc.target/mips/smartmips-ror-3.c @@ -0,0 +1,10 @@ +/* { dg-do compile } */ +/* { dg-mips-options "-O -msmartmips -mno-mips16" } */ + +#define S 13 + +int rotate_left_constant (unsigned a) +{ + return (a << S) | (a >> (32 - S)); +} +/* { dg-final { scan-assembler "\tror\t" } } */ diff --git a/gcc/testsuite/gcc.target/mips/smartmips-ror-4.c b/gcc/testsuite/gcc.target/mips/smartmips-ror-4.c new file mode 100644 index 00000000000..2a56210539f --- /dev/null +++ b/gcc/testsuite/gcc.target/mips/smartmips-ror-4.c @@ -0,0 +1,10 @@ +/* { dg-do compile } */ +/* { dg-mips-options "-O -msmartmips -mno-mips16" } */ + +#define S 13 + +int rotate_right_constant (unsigned a) +{ + return (a >> S) | (a << (32 - S)); +} +/* { dg-final { scan-assembler "\tror\t" } } */ |