From 670ec21de99faddc3a86f9e18fb9ef1a093b3dbf Mon Sep 17 00:00:00 2001 From: Nick Clifton Date: Tue, 27 Jul 2004 11:37:12 +0000 Subject: [PATCH] Add CRX insns: pushx, popx Add support to GAS for expressions which are the difference of two symbols --- bfd/ChangeLog | 8 +++ bfd/bfd-in2.h | 3 + bfd/elf32-crx.c | 102 +++++++++++++++++++++++++++-- bfd/reloc.c | 6 ++ gas/ChangeLog | 8 ++- gas/config/obj-coff.c | 2 +- gas/config/tc-crx.c | 73 ++++++++++++++++++--- gas/config/tc-crx.h | 8 ++- gas/config/tc-ppc.c | 9 ++- gas/testsuite/ChangeLog | 4 ++ gas/testsuite/gas/all/gas.exp | 3 +- gas/testsuite/gas/macros/strings.s | 1 + include/elf/ChangeLog | 5 ++ include/elf/crx.h | 3 + opcodes/ChangeLog | 4 ++ opcodes/crx-opc.c | 24 +++---- 16 files changed, 229 insertions(+), 34 deletions(-) diff --git a/bfd/ChangeLog b/bfd/ChangeLog index 3b7a2d5fc4..9ea925801f 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,3 +1,11 @@ +2004-07-27 Tomer Levi + + * reloc.c: Add BFD_RELOC_CRX_SWITCH8, BFD_RELOC_CRX_SWITCH16, + BFD_RELOC_CRX_SWITCH32. + * bfd-in2.h: Regenerate. + * elf32-crx.c: Support relocation/relaxation of + BFD_RELOC_CRX_SWITCH* types. + 2004-07-27 Alan Modra * elf64-ppc.c: Correct "Linker stubs" comment. diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h index f6618898f6..b6d14ff565 100644 --- a/bfd/bfd-in2.h +++ b/bfd/bfd-in2.h @@ -3463,6 +3463,9 @@ This is the 5 bits of a value. */ BFD_RELOC_CRX_NUM32, BFD_RELOC_CRX_IMM16, BFD_RELOC_CRX_IMM32, + BFD_RELOC_CRX_SWITCH8, + BFD_RELOC_CRX_SWITCH16, + BFD_RELOC_CRX_SWITCH32, /* These relocs are only used within the CRIS assembler. They are not (at present) written to any object files. */ diff --git a/bfd/elf32-crx.c b/bfd/elf32-crx.c index 03575f2326..79b9c64fea 100644 --- a/bfd/elf32-crx.c +++ b/bfd/elf32-crx.c @@ -77,7 +77,10 @@ static const struct crx_reloc_map crx_reloc_map[R_CRX_MAX] = {BFD_RELOC_CRX_NUM16, R_CRX_NUM16}, {BFD_RELOC_CRX_NUM32, R_CRX_NUM32}, {BFD_RELOC_CRX_IMM16, R_CRX_IMM16}, - {BFD_RELOC_CRX_IMM32, R_CRX_IMM32} + {BFD_RELOC_CRX_IMM32, R_CRX_IMM32}, + {BFD_RELOC_CRX_SWITCH8, R_CRX_SWITCH8}, + {BFD_RELOC_CRX_SWITCH16, R_CRX_SWITCH16}, + {BFD_RELOC_CRX_SWITCH32, R_CRX_SWITCH32} }; static reloc_howto_type crx_elf_howto_table[] = @@ -332,7 +335,58 @@ static reloc_howto_type crx_elf_howto_table[] = FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ - FALSE) /* pcrel_offset */ + FALSE), /* pcrel_offset */ + + /* An 8 bit switch table entry. This is generated for an expression + such as ``.byte L1 - L2''. The offset holds the difference + between the reloc address and L2. */ + HOWTO (R_CRX_SWITCH8, /* type */ + 0, /* rightshift */ + 0, /* size (0 = byte, 1 = short, 2 = long) */ + 8, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_unsigned, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_CRX_SWITCH8", /* name */ + FALSE, /* partial_inplace */ + 0xff, /* src_mask */ + 0xff, /* dst_mask */ + TRUE), /* pcrel_offset */ + + /* A 16 bit switch table entry. This is generated for an expression + such as ``.word L1 - L2''. The offset holds the difference + between the reloc address and L2. */ + HOWTO (R_CRX_SWITCH16, /* type */ + 0, /* rightshift */ + 1, /* size (0 = byte, 1 = short, 2 = long) */ + 16, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_unsigned, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_CRX_SWITCH16", /* name */ + FALSE, /* partial_inplace */ + 0xffff, /* src_mask */ + 0xffff, /* dst_mask */ + TRUE), /* pcrel_offset */ + + /* A 32 bit switch table entry. This is generated for an expression + such as ``.long L1 - L2''. The offset holds the difference + between the reloc address and L2. */ + HOWTO (R_CRX_SWITCH32, /* type */ + 0, /* rightshift */ + 2, /* size (0 = byte, 1 = short, 2 = long) */ + 32, /* bitsize */ + FALSE, /* pc_relative */ + 0, /* bitpos */ + complain_overflow_unsigned, /* complain_on_overflow */ + bfd_elf_generic_reloc, /* special_function */ + "R_CRX_SWITCH32", /* name */ + FALSE, /* partial_inplace */ + 0xffffffff, /* src_mask */ + 0xffffffff, /* dst_mask */ + TRUE) /* pcrel_offset */ }; /* Retrieve a howto ptr using a BFD reloc_code. */ @@ -406,6 +460,13 @@ crx_elf_final_link_relocate (reloc_howto_type *howto, bfd *input_bfd, return bfd_reloc_ok; break; + case R_CRX_SWITCH8: + case R_CRX_SWITCH16: + case R_CRX_SWITCH32: + /* We only care about the addend, where the difference between + expressions is kept. */ + Rvalue = 0; + default: break; } @@ -483,7 +544,7 @@ crx_elf_final_link_relocate (reloc_howto_type *howto, bfd *input_bfd, Rvalue |= (((bfd_get_16 (input_bfd, hit_data) << 16) | bfd_get_16 (input_bfd, hit_data + 2)) & ~howto->dst_mask); - if (r_type == R_CRX_NUM32) + if (r_type == R_CRX_NUM32 || r_type == R_CRX_SWITCH32) /* Relocation on DATA is purely little-endian, that is, for a multi-byte datum, the lowest address in memory contains the little end of the datum, that is, the least significant byte. @@ -562,7 +623,40 @@ elf32_crx_relax_delete_bytes (bfd *abfd, asection *sec, if (isym->st_shndx == sec_shndx && isym->st_value > addr && isym->st_value < toaddr) - isym->st_value -= count; + { + /* Adjust the addend of SWITCH relocations in this section, + which reference this local symbol. */ + for (irel = elf_section_data (sec)->relocs; irel < irelend; irel++) + { + unsigned long r_symndx; + Elf_Internal_Sym *rsym; + bfd_vma addsym, subsym; + + /* Skip if not a SWITCH relocation. */ + if (ELF32_R_TYPE (irel->r_info) != (int) R_CRX_SWITCH8 + && ELF32_R_TYPE (irel->r_info) != (int) R_CRX_SWITCH16 + && ELF32_R_TYPE (irel->r_info) != (int) R_CRX_SWITCH32) + continue; + + r_symndx = ELF32_R_SYM (irel->r_info); + rsym = (Elf_Internal_Sym *) symtab_hdr->contents + r_symndx; + + /* Skip if not the local adjusted symbol. */ + if (rsym != isym) + continue; + + addsym = isym->st_value; + subsym = addsym - irel->r_addend; + + /* Fix the addend only when -->> (addsym > addr >= subsym). */ + if (subsym <= addr) + irel->r_addend -= count; + else + continue; + } + + isym->st_value -= count; + } } /* Now adjust the global symbols defined in this section. */ diff --git a/bfd/reloc.c b/bfd/reloc.c index e8279d2105..b997437889 100644 --- a/bfd/reloc.c +++ b/bfd/reloc.c @@ -3888,6 +3888,12 @@ ENUMX BFD_RELOC_CRX_IMM16 ENUMX BFD_RELOC_CRX_IMM32 +ENUMX + BFD_RELOC_CRX_SWITCH8 +ENUMX + BFD_RELOC_CRX_SWITCH16 +ENUMX + BFD_RELOC_CRX_SWITCH32 ENUMDOC NS CRX Relocations. diff --git a/gas/ChangeLog b/gas/ChangeLog index 70bf40cf7b..7fa3398a1a 100644 --- a/gas/ChangeLog +++ b/gas/ChangeLog @@ -1,4 +1,10 @@ -2004-07-26 H.J. Lu +2004-07-27 Tomer Levi + + * config/tc-crx.c: Support evaluating the difference between two + symbols. + * config/tc-crx.h: Likewise. + + 2004-07-26 H.J. Lu * config/tc-ia64.c (start_unwind_section): Set the linked-to section. diff --git a/gas/config/obj-coff.c b/gas/config/obj-coff.c index ee37b08e14..1451d9da08 100644 --- a/gas/config/obj-coff.c +++ b/gas/config/obj-coff.c @@ -1327,7 +1327,7 @@ coff_frob_symbol (symp, punt) order to call SA_SET_SYM_ENDNDX correctly. */ if (! symbol_used_in_reloc_p (symp) && ((symbol_get_bfdsym (symp)->flags & BSF_SECTION_SYM) != 0 - || (! S_IS_EXTERNAL (symp) + || (! (S_IS_EXTERNAL (symp) || S_IS_WEAK (symp)) && ! symbol_get_tc (symp)->output && S_GET_STORAGE_CLASS (symp) != C_FILE))) *punt = 1; diff --git a/gas/config/tc-crx.c b/gas/config/tc-crx.c index 3b3c49e3ba..30621188a5 100644 --- a/gas/config/tc-crx.c +++ b/gas/config/tc-crx.c @@ -292,6 +292,34 @@ reset_vars (char *op, ins *crx_ins) strcpy (ins_parse, op); } +/* This macro decides whether a particular reloc is an entry in a + switch table. It is used when relaxing, because the linker needs + to know about all such entries so that it can adjust them if + necessary. */ + +#define SWITCH_TABLE(fix) \ + ( (fix)->fx_addsy != NULL \ + && (fix)->fx_subsy != NULL \ + && S_GET_SEGMENT ((fix)->fx_addsy) == \ + S_GET_SEGMENT ((fix)->fx_subsy) \ + && S_GET_SEGMENT (fix->fx_addsy) != undefined_section \ + && ( (fix)->fx_r_type == BFD_RELOC_CRX_NUM8 \ + || (fix)->fx_r_type == BFD_RELOC_CRX_NUM16 \ + || (fix)->fx_r_type == BFD_RELOC_CRX_NUM32)) + +/* See whether we need to force a relocation into the output file. + This is used to force out switch and PC relative relocations when + relaxing. */ + +int +crx_force_relocation (fixS *fix) +{ + if (generic_force_reloc (fix) || SWITCH_TABLE (fix)) + return 1; + + return 0; +} + /* Generate a relocation entry for a fixup. */ arelent * @@ -306,15 +334,42 @@ tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS * fixP) reloc->addend = fixP->fx_offset; if (fixP->fx_subsy != NULL) - /* We don't resolve difference expressions. */ - as_bad_where (fixP->fx_file, fixP->fx_line, - _("can't resolve `%s' {%s section} - `%s' {%s section}"), - fixP->fx_addsy ? S_GET_NAME (fixP->fx_addsy) : "0", - segment_name (fixP->fx_addsy - ? S_GET_SEGMENT (fixP->fx_addsy) - : absolute_section), - S_GET_NAME (fixP->fx_subsy), - segment_name (S_GET_SEGMENT (fixP->fx_addsy))); + { + if (SWITCH_TABLE (fixP)) + { + /* Keep the current difference in the addend. */ + reloc->addend = (S_GET_VALUE (fixP->fx_addsy) + - S_GET_VALUE (fixP->fx_subsy) + fixP->fx_offset); + + switch (fixP->fx_r_type) + { + case BFD_RELOC_CRX_NUM8: + fixP->fx_r_type = BFD_RELOC_CRX_SWITCH8; + break; + case BFD_RELOC_CRX_NUM16: + fixP->fx_r_type = BFD_RELOC_CRX_SWITCH16; + break; + case BFD_RELOC_CRX_NUM32: + fixP->fx_r_type = BFD_RELOC_CRX_SWITCH32; + break; + default: + abort (); + break; + } + } + else + { + /* We only resolve difference expressions in the same section. */ + as_bad_where (fixP->fx_file, fixP->fx_line, + _("can't resolve `%s' {%s section} - `%s' {%s section}"), + fixP->fx_addsy ? S_GET_NAME (fixP->fx_addsy) : "0", + segment_name (fixP->fx_addsy + ? S_GET_SEGMENT (fixP->fx_addsy) + : absolute_section), + S_GET_NAME (fixP->fx_subsy), + segment_name (S_GET_SEGMENT (fixP->fx_addsy))); + } + } assert ((int) fixP->fx_r_type > 0); reloc->howto = bfd_reloc_type_lookup (stdoutput, fixP->fx_r_type); diff --git a/gas/config/tc-crx.h b/gas/config/tc-crx.h index dc26a70ecd..29e59fe49b 100644 --- a/gas/config/tc-crx.h +++ b/gas/config/tc-crx.h @@ -48,6 +48,10 @@ extern const struct relax_type md_relax_table[]; linker relaxations easier. */ #define tc_fix_adjustable(fixP) 0 +/* We need to force out some relocations when relaxing. */ +#define TC_FORCE_RELOCATION(FIXP) crx_force_relocation (FIXP) +extern int crx_force_relocation (struct fix *); + /* Fixup debug sections since we will never relax them. */ #define TC_LINKRELAX_FIXUP(seg) (seg->flags & SEC_ALLOC) @@ -58,8 +62,8 @@ extern const struct relax_type md_relax_table[]; /* This is called by emit_expr when creating a reloc for a cons. We could use the definition there, except that we want to handle the CRX reloc type specially, rather than the BFD_RELOC type. */ -#define TC_CONS_FIX_NEW(FRAG,OFF,LEN,EXP) \ - fix_new_exp (FRAG, OFF, (int)LEN, EXP, 0, \ +#define TC_CONS_FIX_NEW(FRAG, OFF, LEN, EXP) \ + fix_new_exp (FRAG, OFF, (int) LEN, EXP, 0, \ LEN == 1 ? BFD_RELOC_CRX_NUM8 \ : LEN == 2 ? BFD_RELOC_CRX_NUM16 \ : LEN == 4 ? BFD_RELOC_CRX_NUM32 \ diff --git a/gas/config/tc-ppc.c b/gas/config/tc-ppc.c index d4893e7610..7bc70113ff 100644 --- a/gas/config/tc-ppc.c +++ b/gas/config/tc-ppc.c @@ -1918,9 +1918,8 @@ ppc_frob_file_before_adjust () free (dotname); if (dotsym != NULL && (symbol_used_p (dotsym) || symbol_used_in_reloc_p (dotsym))) - { - symbol_mark_used (symp); - } + symbol_mark_used (symp); + } /* Don't emit .TOC. symbol. */ @@ -4843,7 +4842,7 @@ ppc_frob_symbol (sym) table. */ if (! symbol_used_in_reloc_p (sym) && ((symbol_get_bfdsym (sym)->flags & BSF_SECTION_SYM) != 0 - || (! S_IS_EXTERNAL (sym) + || (! (S_IS_EXTERNAL (sym) || S_IS_WEAK (sym)) && ! symbol_get_tc (sym)->output && S_GET_STORAGE_CLASS (sym) != C_FILE))) return 1; @@ -4909,7 +4908,7 @@ ppc_frob_symbol (sym) } } - if (! S_IS_EXTERNAL (sym) + if (! (S_IS_EXTERNAL (sym) || S_IS_WEAK (sym)) && (symbol_get_bfdsym (sym)->flags & BSF_SECTION_SYM) == 0 && S_GET_STORAGE_CLASS (sym) != C_FILE && S_GET_STORAGE_CLASS (sym) != C_FCN diff --git a/gas/testsuite/ChangeLog b/gas/testsuite/ChangeLog index 77428e4daf..13815bbd1f 100644 --- a/gas/testsuite/ChangeLog +++ b/gas/testsuite/ChangeLog @@ -1,3 +1,7 @@ +2004-07-27 Tomer Levi + + * gas/all/gas.exp (do_930509a): Disable test for crx. + 2004-07-22 H.J. Lu * gas/i386/x86-64-inval.l: Remove the leading `+'. diff --git a/gas/testsuite/gas/all/gas.exp b/gas/testsuite/gas/all/gas.exp index 31cfa4eabe..df8d71d027 100644 --- a/gas/testsuite/gas/all/gas.exp +++ b/gas/testsuite/gas/all/gas.exp @@ -85,12 +85,13 @@ proc do_930509a {} { if !$x then { fail $testname } } -# This test is meaningless for the PA; the difference of two symbols +# This test is meaningless for the PA and CRX; the difference of two symbols # must not be resolved by the assembler. # C54x assembler (for compatibility) does not allow differences between # forward references # C30 counts a four byte offset as a difference of one. if { ![istarget hppa*-*-*] && + ![istarget crx*-*-*] && ![istarget *c30*-*-*] && ![istarget *c4x*-*-*] && ![istarget *c54x*-*-*] } then { diff --git a/gas/testsuite/gas/macros/strings.s b/gas/testsuite/gas/macros/strings.s index 291a0dca71..9e98c85d62 100644 --- a/gas/testsuite/gas/macros/strings.s +++ b/gas/testsuite/gas/macros/strings.s @@ -1,3 +1,4 @@ +#NO_APP .macro M arg1 .ascii "\arg1" .endm diff --git a/include/elf/ChangeLog b/include/elf/ChangeLog index 5563ddbeea..408088587b 100644 --- a/include/elf/ChangeLog +++ b/include/elf/ChangeLog @@ -1,3 +1,8 @@ +2004-07-27 Tomer Levi + + * crx.h: Add BFD_RELOC_CRX_SWITCH8, BFD_RELOC_CRX_SWITCH16, + BFD_RELOC_CRX_SWITCH32. + 2004-07-06 Tomer Levi * common.h (EM_CRX): Define. diff --git a/include/elf/crx.h b/include/elf/crx.h index 755a6109d1..33ba005147 100644 --- a/include/elf/crx.h +++ b/include/elf/crx.h @@ -45,6 +45,9 @@ START_RELOC_NUMBERS(elf_crx_reloc_type) RELOC_NUMBER (R_CRX_NUM32, 15) RELOC_NUMBER (R_CRX_IMM16, 16) RELOC_NUMBER (R_CRX_IMM32, 17) + RELOC_NUMBER (R_CRX_SWITCH8, 18) + RELOC_NUMBER (R_CRX_SWITCH16, 19) + RELOC_NUMBER (R_CRX_SWITCH32, 20) END_RELOC_NUMBERS(R_CRX_MAX) #endif /* _ELF_CRX_H */ diff --git a/opcodes/ChangeLog b/opcodes/ChangeLog index 159f6d0f1d..5cdcfc8538 100644 --- a/opcodes/ChangeLog +++ b/opcodes/ChangeLog @@ -1,3 +1,7 @@ +2004-07-27 Tomer Levi + + * crx-opc.c: Add popx,pushx insns. Indent code, fix comments. + 2004-07-22 Nick Clifton PR/280 diff --git a/opcodes/crx-opc.c b/opcodes/crx-opc.c index da1e322884..1a6b3137c6 100644 --- a/opcodes/crx-opc.c +++ b/opcodes/crx-opc.c @@ -357,7 +357,7 @@ const inst crx_instruction[] = /* opc12 r rbase ridx scl2 disps22 */ \ {NAME, 3, 0x33C+OPC1, 20, LD_STOR_INS | REVERSE_MATCH, {{rbase_ridx_scl2_dispu22,0}, {regr,16}}} - LD_REG_INST ("loadb", 0x0, 0x0, DISPUB4), + LD_REG_INST ("loadb", 0x0, 0x0, DISPUB4), LD_REG_INST ("loadw", 0x1, 0x1, DISPUW4), LD_REG_INST ("loadd", 0x2, 0x2, DISPUD4), @@ -439,42 +439,44 @@ const inst crx_instruction[] = CSTBIT_INST ("cbitw", i4, 0x382, 0x10, 20, 0xBD), CSTBIT_INST ("cbitd", i5, 0x1C3, 0x8, 21, 0x7B), {"cbitd", 2, 0x300838, 8, CSTBIT_INS, {{regr,4}, {regr,0}}}, - {"cbitd", 2, 0x18047B, 9, CSTBIT_INS, {{i5,4}, {regr,0}}}, + {"cbitd", 2, 0x18047B, 9, CSTBIT_INS, {{i5,4}, {regr,0}}}, CSTBIT_INST ("sbitb", i3, 0x701, 0x20, 19, 0x1FD), CSTBIT_INST ("sbitw", i4, 0x383, 0x10, 20, 0xBE), CSTBIT_INST ("sbitd", i5, 0x1C4, 0x8, 21, 0x7C), {"sbitd", 2, 0x300839, 8, CSTBIT_INS, {{regr,4}, {regr,0}}}, - {"sbitd", 2, 0x18047C, 9, CSTBIT_INS, {{i5,4}, {regr,0}}}, + {"sbitd", 2, 0x18047C, 9, CSTBIT_INS, {{i5,4}, {regr,0}}}, CSTBIT_INST ("tbitb", i3, 0x702, 0x20, 19, 0x1FE), CSTBIT_INST ("tbitw", i4, 0x384, 0x10, 20, 0xBF), CSTBIT_INST ("tbitd", i5, 0x1C5, 0x8, 21, 0x7D), {"tbitd", 2, 0x30083A, 8, CSTBIT_INS, {{regr,4}, {regr,0}}}, - {"tbitd", 2, 0x18047D, 9, CSTBIT_INS, {{i5,4}, {regr,0}}}, + {"tbitd", 2, 0x18047D, 9, CSTBIT_INS, {{i5,4}, {regr,0}}}, /* Instructions including a register list (opcode is represented as a mask). */ #define REGLIST_INST(NAME, OPC) \ /* opc12 r mask16 */ \ {NAME, 2, OPC, 20, REG_LIST, {{regr,16}, {i16,0}}} - REG1_INST ("getrfid", 0xFF9), - REG1_INST ("setrfid", 0xFFA), + REG1_INST ("getrfid", 0xFF9), + REG1_INST ("setrfid", 0xFFA), REGLIST_INST ("push", 0x346), REG1_INST ("push", 0xFFB), + REGLIST_INST ("pushx", 0x347), REGLIST_INST ("pop", 0x324), REG1_INST ("pop", 0xFFC), + REGLIST_INST ("popx", 0x327), REGLIST_INST ("popret", 0x326), REG1_INST ("popret", 0xFFD), - REGLIST_INST ("loadm", 0x324), + REGLIST_INST ("loadm", 0x324), REGLIST_INST ("loadma", 0x325), - REGLIST_INST ("popma", 0x325), + REGLIST_INST ("popma", 0x325), - REGLIST_INST ("storm", 0x344), + REGLIST_INST ("storm", 0x344), REGLIST_INST ("storma", 0x345), REGLIST_INST ("pushma", 0x345), @@ -560,7 +562,7 @@ const reg_entry crx_regtab[] = REG(ra, 0xe, CRX_R_REGTYPE), REG(sp, 0xf, CRX_R_REGTYPE), -/* Build a user register ur. */ +/* Build a user register u. */ #define REG_U(N) REG(CONCAT2(u,N), 0x80 + N, CRX_U_REGTYPE) REG_U(0), REG_U(1), REG_U(2), REG_U(3), @@ -607,7 +609,7 @@ const reg_entry crx_copregtab[] = REG_C(8), REG_C(9), REG_C(10), REG_C(11), REG_C(12), REG_C(13), REG_C(14), REG_C(15), -/* Build a Coprocessor Special register c. */ +/* Build a Coprocessor Special register cs. */ #define REG_CS(N) REG(CONCAT2(cs,N), N, CRX_CS_REGTYPE) REG_CS(0), REG_CS(1), REG_CS(2), REG_CS(3), -- 2.34.1