mozilla-ppc64le-xpcom.patch
branchesr24
changeset 713 3c7719dfcafa
equal deleted inserted replaced
706:4639b5ad4fce 713:3c7719dfcafa
       
     1 # HG changeset patch
       
     2 # Parent 1507f021ac93c1d27cfd00e389fba1c9421b3201
       
     3 # User Ulrich Weigand <uweigand@de.ibm.com>
       
     4 Bug 976648 - powerpc64le-linux support - xptcall port
       
     5 
       
     6 diff --git a/xpcom/reflect/xptcall/src/md/unix/Makefile.in b/xpcom/reflect/xptcall/src/md/unix/Makefile.in
       
     7 --- a/xpcom/reflect/xptcall/src/md/unix/Makefile.in
       
     8 +++ b/xpcom/reflect/xptcall/src/md/unix/Makefile.in
       
     9 @@ -161,17 +161,17 @@ endif
       
    10  ifneq (,$(filter Linuxpowerpc FreeBSDpowerpc,$(OS_ARCH)$(OS_TEST)))
       
    11  ASFILES		:= xptcinvoke_asm_ppc_linux.s xptcstubs_asm_ppc_linux.s
       
    12  AS		:= $(CC) -c -x assembler-with-cpp
       
    13  endif
       
    14  
       
    15  #
       
    16  # Linux/PPC64
       
    17  #
       
    18 -ifneq (,$(filter Linuxpowerpc64 FreeBSDpowerpc64,$(OS_ARCH)$(OS_TEST)))
       
    19 +ifneq (,$(filter Linuxpowerpc64 Linuxpowerpc64le FreeBSDpowerpc64,$(OS_ARCH)$(OS_TEST)))
       
    20  ASFILES                := xptcinvoke_asm_ppc64_linux.s xptcstubs_asm_ppc64_linux.s
       
    21  AS             := $(CC) -c -x assembler-with-cpp
       
    22  endif
       
    23  
       
    24  #
       
    25  # NetBSD/PPC
       
    26  #
       
    27  ifneq (,$(filter NetBSDmacppc NetBSDbebox NetBSDofppc NetBSDprep NetBSDamigappc,$(OS_ARCH)$(OS_TEST)))                           
       
    28 diff --git a/xpcom/reflect/xptcall/src/md/unix/moz.build b/xpcom/reflect/xptcall/src/md/unix/moz.build
       
    29 --- a/xpcom/reflect/xptcall/src/md/unix/moz.build
       
    30 +++ b/xpcom/reflect/xptcall/src/md/unix/moz.build
       
    31 @@ -186,16 +186,23 @@ if CONFIG['OS_TEST'] == 'powerpc':
       
    32  
       
    33  if CONFIG['OS_TEST'] == 'powerpc64':
       
    34      if CONFIG['OS_ARCH'] in ('Linux', 'FreeBSD'):
       
    35            CPP_SOURCES += [
       
    36                'xptcinvoke_ppc64_linux.cpp',
       
    37                'xptcstubs_ppc64_linux.cpp',
       
    38            ]
       
    39  
       
    40 +if CONFIG['OS_TEST'] == 'powerpc64le':
       
    41 +    if CONFIG['OS_ARCH'] == 'Linux':
       
    42 +          CPP_SOURCES += [
       
    43 +              'xptcinvoke_ppc64_linux.cpp',
       
    44 +              'xptcstubs_ppc64_linux.cpp',
       
    45 +          ]
       
    46 +
       
    47  if CONFIG['OS_TEST'] in ('macppc', 'bebox', 'ofppc', 'prep', 'amigappc'):
       
    48      if CONFIG['OS_ARCH'] == 'NetBSD':
       
    49          CPP_SOURCES += [
       
    50              'xptcinvoke_ppc_netbsd.cpp',
       
    51              'xptcstubs_ppc_netbsd.cpp',
       
    52          ]
       
    53  
       
    54  if CONFIG['OS_ARCH'] == 'OpenBSD' and CONFIG['OS_TEST'] == 'powerpc':
       
    55 diff --git a/xpcom/reflect/xptcall/src/md/unix/xptcinvoke_asm_ppc64_linux.s b/xpcom/reflect/xptcall/src/md/unix/xptcinvoke_asm_ppc64_linux.s
       
    56 --- a/xpcom/reflect/xptcall/src/md/unix/xptcinvoke_asm_ppc64_linux.s
       
    57 +++ b/xpcom/reflect/xptcall/src/md/unix/xptcinvoke_asm_ppc64_linux.s
       
    58 @@ -12,90 +12,121 @@
       
    59  .set f0,0; .set f1,1; .set f2,2; .set f3,3; .set f4,4
       
    60  .set f5,5; .set f6,6; .set f7,7; .set f8,8; .set f9,9
       
    61  .set f10,10; .set f11,11; .set f12,12; .set f13,13; .set f14,14
       
    62  .set f15,15; .set f16,16; .set f17,17; .set f18,18; .set f19,19
       
    63  .set f20,20; .set f21,21; .set f22,22; .set f23,23; .set f24,24
       
    64  .set f25,25; .set f26,26; .set f27,27; .set f28,28; .set f29,29
       
    65  .set f30,30; .set f31,31
       
    66  
       
    67 +# The ABI defines a fixed stack frame area of 4 doublewords (ELFv2)
       
    68 +# or 6 doublewords (ELFv1); the last of these doublewords is used
       
    69 +# as TOC pointer save area.  The fixed area is followed by a parameter
       
    70 +# save area of 8 doublewords (used for vararg routines), followed
       
    71 +# by space for parameters passed on the stack.
       
    72 +#
       
    73 +# We set STACK_TOC to the offset of the TOC pointer save area, and
       
    74 +# STACK_PARAMS to the offset of the first on-stack parameter.
       
    75 +
       
    76 +#if _CALL_ELF == 2
       
    77 +#define STACK_TOC      24
       
    78 +#define STACK_PARAMS   96
       
    79 +#else
       
    80 +#define STACK_TOC      40
       
    81 +#define STACK_PARAMS   112
       
    82 +#endif
       
    83  
       
    84  #
       
    85  # NS_InvokeByIndex(nsISupports* that, uint32_t methodIndex,
       
    86  #                    uint32_t paramCount, nsXPTCVariant* params)
       
    87  #
       
    88  
       
    89 +#if _CALL_ELF == 2
       
    90 +        .section ".text"
       
    91 +        .type   NS_InvokeByIndex,@function
       
    92 +        .globl  NS_InvokeByIndex
       
    93 +        .align 2
       
    94 +NS_InvokeByIndex:
       
    95 +0:      addis 2,12,(.TOC.-0b)@ha
       
    96 +        addi 2,2,(.TOC.-0b)@l
       
    97 +        .localentry NS_InvokeByIndex,.-NS_InvokeByIndex
       
    98 +#else
       
    99          .section ".toc","aw"
       
   100          .section ".text"
       
   101          .align 2
       
   102          .globl  NS_InvokeByIndex
       
   103          .section ".opd","aw"
       
   104          .align 3
       
   105  NS_InvokeByIndex:
       
   106          .quad   .NS_InvokeByIndex,.TOC.@tocbase
       
   107          .previous
       
   108          .type   NS_InvokeByIndex,@function
       
   109  .NS_InvokeByIndex:
       
   110 +#endif
       
   111          mflr    0
       
   112          std     0,16(r1)
       
   113  
       
   114          std     r29,-24(r1)
       
   115          std     r30,-16(r1)
       
   116          std     r31,-8(r1)
       
   117  
       
   118          mr      r29,r3                  # Save 'that' in r29
       
   119          mr      r30,r4                  # Save 'methodIndex' in r30
       
   120          mr      r31,r1                  # Save old frame
       
   121  
       
   122          # Allocate stack frame with space for params. Since at least the
       
   123          # first 7 parameters (not including 'that') will be in registers,
       
   124          # we don't actually need stack space for those. We must ensure
       
   125          # that the stack remains 16-byte aligned.
       
   126          #
       
   127 -        #  | ..128-byte stack frame.. |     | 7 GP | 13 FP | 3 NV |
       
   128 -        #  |               |(params)........| regs | regs  | regs |
       
   129 -        # (r1)...........(+112)....(+128)
       
   130 -        #                               (-23*8).(-16*8).(-3*8)..(r31)
       
   131 +        #  | (fixed area + |                | 7 GP | 13 FP | 3 NV |
       
   132 +        #  |  param. save) |(params)........| regs | regs  | regs |
       
   133 +        # (r1)......(+STACK_PARAMS)...  (-23*8).(-16*8).(-3*8)..(r31)
       
   134  
       
   135          # +stack frame, -unused stack params, +regs storage, +1 for alignment
       
   136 -        addi    r7,r5,((112/8)-7+7+13+3+1)
       
   137 +        addi    r7,r5,((STACK_PARAMS/8)-7+7+13+3+1)
       
   138          rldicr  r7,r7,3,59              # multiply by 8 and mask with ~15
       
   139          neg     r7,r7
       
   140          stdux   r1,r1,r7
       
   141  
       
   142  
       
   143          # Call invoke_copy_to_stack(uint64_t* gpregs, double* fpregs,
       
   144          #                           uint32_t paramCount, nsXPTCVariant* s, 
       
   145          #                           uint64_t* d))
       
   146  
       
   147          # r5, r6 are passed through intact (paramCount, params)
       
   148 -        # r7 (d) has to be r1+112 -- where parameters are passed on the stack.
       
   149 +        # r7 (d) has to be r1+STACK_PARAMS
       
   150 +        #        -- where parameters are passed on the stack.
       
   151          # r3, r4 are above that, easier to address from r31 than from r1
       
   152  
       
   153          subi    r3,r31,(23*8)           # r3 --> GPRS
       
   154          subi    r4,r31,(16*8)           # r4 --> FPRS
       
   155 -        addi    r7,r1,112               # r7 --> params
       
   156 +        addi    r7,r1,STACK_PARAMS      # r7 --> params
       
   157          bl      invoke_copy_to_stack
       
   158          nop
       
   159  
       
   160          # Set up to invoke function
       
   161  
       
   162          ld      r9,0(r29)               # vtable (r29 is 'that')
       
   163          mr      r3,r29                  # self is first arg, obviously
       
   164  
       
   165          sldi    r30,r30,3               # Find function descriptor 
       
   166          add     r9,r9,r30
       
   167 -        ld      r9,0(r9)
       
   168 +        ld      r12,0(r9)
       
   169  
       
   170 -        ld      r0,0(r9)                # Actual address from fd.
       
   171 -        std     r2,40(r1)               # Save r2 (TOC pointer)
       
   172 +        std     r2,STACK_TOC(r1)        # Save r2 (TOC pointer)
       
   173  
       
   174 +#if _CALL_ELF == 2
       
   175 +        mtctr   r12
       
   176 +#else
       
   177 +        ld      r0,0(r12)               # Actual address from fd.
       
   178          mtctr   0
       
   179 -        ld      r11,16(r9)              # Environment pointer from fd.
       
   180 -        ld      r2,8(r9)                # TOC pointer from fd.
       
   181 +        ld      r11,16(r12)             # Environment pointer from fd.
       
   182 +        ld      r2,8(r12)               # TOC pointer from fd.
       
   183 +#endif
       
   184  
       
   185          # Load FP and GP registers as required
       
   186          ld      r4, -(23*8)(r31) 
       
   187          ld      r5, -(22*8)(r31) 
       
   188          ld      r6, -(21*8)(r31) 
       
   189          ld      r7, -(20*8)(r31) 
       
   190          ld      r8, -(19*8)(r31) 
       
   191          ld      r9, -(18*8)(r31) 
       
   192 @@ -112,21 +143,25 @@ NS_InvokeByIndex:
       
   193          lfd     f9, -(8*8)(r31)
       
   194          lfd     f10, -(7*8)(r31)
       
   195          lfd     f11, -(6*8)(r31)
       
   196          lfd     f12, -(5*8)(r31)
       
   197          lfd     f13, -(4*8)(r31)
       
   198  
       
   199          bctrl                           # Do it
       
   200  
       
   201 -        ld      r2,40(r1)               # Load our own TOC pointer
       
   202 +        ld      r2,STACK_TOC(r1)        # Load our own TOC pointer
       
   203          ld      r1,0(r1)                # Revert stack frame
       
   204          ld      0,16(r1)                # Reload lr
       
   205          ld      29,-24(r1)              # Restore NVGPRS
       
   206          ld      30,-16(r1)
       
   207          ld      31,-8(r1)
       
   208          mtlr    0
       
   209          blr
       
   210  
       
   211 +#if _CALL_ELF == 2
       
   212 +        .size   NS_InvokeByIndex,.-NS_InvokeByIndex
       
   213 +#else
       
   214          .size   NS_InvokeByIndex,.-.NS_InvokeByIndex
       
   215 +#endif
       
   216  
       
   217          # Magic indicating no need for an executable stack
       
   218          .section .note.GNU-stack, "", @progbits ; .previous
       
   219 diff --git a/xpcom/reflect/xptcall/src/md/unix/xptcinvoke_ppc64_linux.cpp b/xpcom/reflect/xptcall/src/md/unix/xptcinvoke_ppc64_linux.cpp
       
   220 --- a/xpcom/reflect/xptcall/src/md/unix/xptcinvoke_ppc64_linux.cpp
       
   221 +++ b/xpcom/reflect/xptcall/src/md/unix/xptcinvoke_ppc64_linux.cpp
       
   222 @@ -69,17 +69,19 @@ invoke_copy_to_stack(uint64_t* gpregs,
       
   223              else
       
   224                  *(double *)d = s->val.d;
       
   225          }
       
   226          else if (!s->IsPtrData() && s->type == nsXPTType::T_FLOAT) {
       
   227              if (i < FPR_COUNT) {
       
   228                  fpregs[i]   = s->val.f; // if passed in registers, floats are promoted to doubles
       
   229              } else {
       
   230                  float *p = (float *)d;
       
   231 +#ifndef __LITTLE_ENDIAN__
       
   232                  p++;
       
   233 +#endif
       
   234                  *p = s->val.f;
       
   235              }
       
   236          }
       
   237          else {
       
   238              if (i < GPR_COUNT)
       
   239                  gpregs[i] = tempu64;
       
   240              else
       
   241                  *d = tempu64;
       
   242 diff --git a/xpcom/reflect/xptcall/src/md/unix/xptcstubs_asm_ppc64_linux.s b/xpcom/reflect/xptcall/src/md/unix/xptcstubs_asm_ppc64_linux.s
       
   243 --- a/xpcom/reflect/xptcall/src/md/unix/xptcstubs_asm_ppc64_linux.s
       
   244 +++ b/xpcom/reflect/xptcall/src/md/unix/xptcstubs_asm_ppc64_linux.s
       
   245 @@ -12,28 +12,50 @@
       
   246  .set f0,0; .set f1,1; .set f2,2; .set f3,3; .set f4,4
       
   247  .set f5,5; .set f6,6; .set f7,7; .set f8,8; .set f9,9
       
   248  .set f10,10; .set f11,11; .set f12,12; .set f13,13; .set f14,14
       
   249  .set f15,15; .set f16,16; .set f17,17; .set f18,18; .set f19,19
       
   250  .set f20,20; .set f21,21; .set f22,22; .set f23,23; .set f24,24
       
   251  .set f25,25; .set f26,26; .set f27,27; .set f28,28; .set f29,29
       
   252  .set f30,30; .set f31,31
       
   253  
       
   254 +#if _CALL_ELF == 2
       
   255 +#define STACK_PARAMS   96
       
   256 +#else
       
   257 +#define STACK_PARAMS   112
       
   258 +#endif
       
   259 +
       
   260 +#if _CALL_ELF == 2
       
   261 +        .section ".text"
       
   262 +        .type   SharedStub,@function
       
   263 +        .globl  SharedStub
       
   264 +        # Make the symbol hidden so that the branch from the stub does
       
   265 +        # not go via a PLT.  This is not only better for performance,
       
   266 +        # but may be necessary to avoid linker errors since there is
       
   267 +        # no place to restore the TOC register in a sibling call.
       
   268 +        .hidden SharedStub
       
   269 +        .align 2
       
   270 +SharedStub:
       
   271 +0:      addis 2,12,(.TOC.-0b)@ha
       
   272 +        addi 2,2,(.TOC.-0b)@l
       
   273 +        .localentry SharedStub,.-SharedStub
       
   274 +#else
       
   275          .section ".text"
       
   276          .align 2
       
   277          .globl SharedStub
       
   278          .section ".opd","aw"
       
   279          .align 3
       
   280  
       
   281  SharedStub:
       
   282          .quad   .SharedStub,.TOC.@tocbase
       
   283          .previous
       
   284          .type   SharedStub,@function
       
   285  
       
   286  .SharedStub:
       
   287 +#endif
       
   288          mflr    r0
       
   289  
       
   290          std     r4, -56(r1)                     # Save all GPRS
       
   291          std     r5, -48(r1)
       
   292          std     r6, -40(r1)
       
   293          std     r7, -32(r1)
       
   294          std     r8, -24(r1)
       
   295          std     r9, -16(r1)
       
   296 @@ -50,17 +72,17 @@ SharedStub:
       
   297          stfd    f5, -128(r1)
       
   298          stfd    f4, -136(r1)
       
   299          stfd    f3, -144(r1)
       
   300          stfd    f2, -152(r1)
       
   301          stfd    f1, -160(r1)
       
   302  
       
   303          subi    r6,r1,56                        # r6 --> gprData
       
   304          subi    r7,r1,160                       # r7 --> fprData
       
   305 -        addi    r5,r1,112                       # r5 --> extra stack args
       
   306 +        addi    r5,r1,STACK_PARAMS              # r5 --> extra stack args
       
   307  
       
   308          std     r0, 16(r1)
       
   309  	
       
   310          stdu    r1,-288(r1)
       
   311                                                  # r3 has the 'self' pointer
       
   312                                                  # already
       
   313  
       
   314          mr      r4,r11                          # r4 is methodIndex selector,
       
   315 @@ -70,12 +92,16 @@ SharedStub:
       
   316          bl      PrepareAndDispatch
       
   317          nop
       
   318  
       
   319          ld      1,0(r1)                         # restore stack
       
   320          ld      r0,16(r1)                       # restore LR
       
   321          mtlr    r0
       
   322          blr
       
   323  
       
   324 +#if _CALL_ELF == 2
       
   325 +        .size   SharedStub,.-SharedStub
       
   326 +#else
       
   327          .size   SharedStub,.-.SharedStub
       
   328 +#endif
       
   329  
       
   330          # Magic indicating no need for an executable stack
       
   331          .section .note.GNU-stack, "", @progbits ; .previous
       
   332 diff --git a/xpcom/reflect/xptcall/src/md/unix/xptcstubs_ppc64_linux.cpp b/xpcom/reflect/xptcall/src/md/unix/xptcstubs_ppc64_linux.cpp
       
   333 --- a/xpcom/reflect/xptcall/src/md/unix/xptcstubs_ppc64_linux.cpp
       
   334 +++ b/xpcom/reflect/xptcall/src/md/unix/xptcstubs_ppc64_linux.cpp
       
   335 @@ -78,17 +78,19 @@ PrepareAndDispatch(nsXPTCStubBase* self,
       
   336                  dp->val.d = fprData[i];
       
   337              else
       
   338                  dp->val.d = *(double*) ap;
       
   339          } else if (!param.IsOut() && type == nsXPTType::T_FLOAT) {
       
   340              if (i < FPR_COUNT)
       
   341                  dp->val.f = (float) fprData[i]; // in registers floats are passed as doubles
       
   342              else {
       
   343                  float *p = (float *)ap;
       
   344 +#ifndef __LITTLE_ENDIAN__
       
   345                  p++;
       
   346 +#endif
       
   347                  dp->val.f = *p;
       
   348              }
       
   349          } else { /* integer type or pointer */
       
   350              if (i < GPR_COUNT)
       
   351                  tempu64 = gprData[i];
       
   352              else
       
   353                  tempu64 = *ap;
       
   354  
       
   355 @@ -148,16 +150,53 @@ PrepareAndDispatch(nsXPTCStubBase* self,
       
   356  // Create names would be like:
       
   357  // _ZN14nsXPTCStubBase5Stub1Ev
       
   358  // _ZN14nsXPTCStubBase6Stub12Ev
       
   359  // _ZN14nsXPTCStubBase7Stub123Ev
       
   360  // _ZN14nsXPTCStubBase8Stub1234Ev
       
   361  // etc.
       
   362  // Use assembler directives to get the names right...
       
   363  
       
   364 +#if _CALL_ELF == 2
       
   365 +# define STUB_ENTRY(n)                                                  \
       
   366 +__asm__ (                                                               \
       
   367 +        ".section \".text\" \n\t"                                       \
       
   368 +        ".align 2 \n\t"                                                 \
       
   369 +        ".if "#n" < 10 \n\t"                                            \
       
   370 +        ".globl _ZN14nsXPTCStubBase5Stub"#n"Ev \n\t"                    \
       
   371 +        ".type  _ZN14nsXPTCStubBase5Stub"#n"Ev,@function \n\n"          \
       
   372 +"_ZN14nsXPTCStubBase5Stub"#n"Ev: \n\t"                                  \
       
   373 +        "0: addis 2,12,.TOC.-0b@ha \n\t"                                \
       
   374 +        "addi     2,2,.TOC.-0b@l \n\t"                                  \
       
   375 +        ".localentry _ZN14nsXPTCStubBase5Stub"#n"Ev,.-_ZN14nsXPTCStubBase5Stub"#n"Ev \n\t" \
       
   376 +                                                                        \
       
   377 +        ".elseif "#n" < 100 \n\t"                                       \
       
   378 +        ".globl _ZN14nsXPTCStubBase6Stub"#n"Ev \n\t"                    \
       
   379 +        ".type  _ZN14nsXPTCStubBase6Stub"#n"Ev,@function \n\n"          \
       
   380 +"_ZN14nsXPTCStubBase6Stub"#n"Ev: \n\t"                                  \
       
   381 +        "0: addis 2,12,.TOC.-0b@ha \n\t"                                \
       
   382 +        "addi     2,2,.TOC.-0b@l \n\t"                                  \
       
   383 +        ".localentry _ZN14nsXPTCStubBase6Stub"#n"Ev,.-_ZN14nsXPTCStubBase6Stub"#n"Ev \n\t" \
       
   384 +                                                                        \
       
   385 +        ".elseif "#n" < 1000 \n\t"                                      \
       
   386 +        ".globl _ZN14nsXPTCStubBase7Stub"#n"Ev \n\t"                    \
       
   387 +        ".type  _ZN14nsXPTCStubBase7Stub"#n"Ev,@function \n\n"          \
       
   388 +"_ZN14nsXPTCStubBase7Stub"#n"Ev: \n\t"                                  \
       
   389 +        "0: addis 2,12,.TOC.-0b@ha \n\t"                                \
       
   390 +        "addi     2,2,.TOC.-0b@l \n\t"                                  \
       
   391 +        ".localentry _ZN14nsXPTCStubBase7Stub"#n"Ev,.-_ZN14nsXPTCStubBase7Stub"#n"Ev \n\t" \
       
   392 +                                                                        \
       
   393 +        ".else  \n\t"                                                   \
       
   394 +        ".err   \"stub number "#n" >= 1000 not yet supported\"\n"       \
       
   395 +        ".endif \n\t"                                                   \
       
   396 +                                                                        \
       
   397 +        "li     11,"#n" \n\t"                                           \
       
   398 +        "b      SharedStub \n"                                          \
       
   399 +);
       
   400 +#else
       
   401  # define STUB_ENTRY(n)                                                  \
       
   402  __asm__ (                                                               \
       
   403          ".section \".toc\",\"aw\" \n\t"                                 \
       
   404          ".section \".text\" \n\t"                                       \
       
   405          ".align 2 \n\t"                                                 \
       
   406          ".if "#n" < 10 \n\t"                                            \
       
   407          ".globl _ZN14nsXPTCStubBase5Stub"#n"Ev \n\t"                    \
       
   408          ".section \".opd\",\"aw\" \n\t"                                 \
       
   409 @@ -190,16 +229,17 @@ PrepareAndDispatch(nsXPTCStubBase* self,
       
   410                                                                          \
       
   411          ".else  \n\t"                                                   \
       
   412          ".err   \"stub number "#n" >= 1000 not yet supported\"\n"       \
       
   413          ".endif \n\t"                                                   \
       
   414                                                                          \
       
   415          "li     11,"#n" \n\t"                                           \
       
   416          "b      SharedStub \n"                                          \
       
   417  );
       
   418 +#endif
       
   419  
       
   420  #define SENTINEL_ENTRY(n)                                               \
       
   421  nsresult nsXPTCStubBase::Sentinel##n()                                  \
       
   422  {                                                                       \
       
   423      NS_ERROR("nsXPTCStubBase::Sentinel called");                  \
       
   424      return NS_ERROR_NOT_IMPLEMENTED;                                    \
       
   425  }