Left: | ||
Right: |
OLD | NEW |
---|---|
1 // Copyright 2009 The Go Authors. All rights reserved. | 1 // Copyright 2009 The Go Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style | 2 // Use of this source code is governed by a BSD-style |
3 // license that can be found in the LICENSE file. | 3 // license that can be found in the LICENSE file. |
4 | 4 |
5 #include <u.h> | 5 #include <u.h> |
6 #include <libc.h> | 6 #include <libc.h> |
7 #include "go.h" | 7 #include "go.h" |
8 | 8 |
9 static Node* walkprint(Node*, NodeList**, int); | 9 static Node* walkprint(Node*, NodeList**, int); |
10 static Node* mapfn(char*, Type*); | 10 static Node* mapfn(char*, Type*); |
11 static Node* mapfndel(char*, Type*); | 11 static Node* mapfndel(char*, Type*); |
12 static Node* ascompatee1(int, Node*, Node*, NodeList**); | 12 static Node* ascompatee1(int, Node*, Node*, NodeList**); |
13 static NodeList* ascompatee(int, NodeList*, NodeList*, NodeList**); | 13 static NodeList* ascompatee(int, NodeList*, NodeList*, NodeList**); |
14 static NodeList* ascompatet(int, NodeList*, Type**, int, NodeList**); | 14 static NodeList* ascompatet(int, NodeList*, Type**, int, NodeList**); |
15 static NodeList* ascompatte(int, Node*, int, Type**, NodeList*, int, Node List**); | 15 static NodeList* ascompatte(int, Node*, int, Type**, NodeList*, int, Node List**); |
16 static Node* convas(Node*, NodeList**); | 16 static Node* convas(Node*, NodeList**); |
17 static void heapmoves(void); | 17 static void heapmoves(void); |
18 static NodeList* paramstoheap(Type **argin, int out); | 18 static NodeList* paramstoheap(Type **argin, int out); |
19 static NodeList* reorder1(NodeList*); | 19 static NodeList* reorder1(NodeList*); |
20 static NodeList* reorder3(NodeList*); | 20 static NodeList* reorder3(NodeList*); |
21 static Node* addstr(Node*, NodeList**); | 21 static Node* addstr(Node*, NodeList**); |
22 static Node* appendslice(Node*, NodeList**); | 22 static Node* appendslice(Node*, NodeList**); |
23 static Node* append(Node*, NodeList**); | 23 static Node* append(Node*, NodeList**); |
24 static Node* sliceany(Node*, NodeList**); | 24 static Node* sliceany(Node*, NodeList**); |
25 static void walkcompare(Node**, NodeList**); | 25 static void walkcompare(Node**, NodeList**); |
26 static void walkrotate(Node**); | 26 static void walkrotate(Node**); |
27 static void walkdiv(Node**, NodeList**); | |
27 static int bounded(Node*, int64); | 28 static int bounded(Node*, int64); |
28 static Mpint mpzero; | 29 static Mpint mpzero; |
29 | 30 |
30 // can this code branch reach the end | 31 // can this code branch reach the end |
31 // without an unconditional RETURN | 32 // without an unconditional RETURN |
32 // this is hard, so it is conservative | 33 // this is hard, so it is conservative |
33 static int | 34 static int |
34 walkret(NodeList *l) | 35 walkret(NodeList *l) |
35 { | 36 { |
36 Node *n; | 37 Node *n; |
(...skipping 437 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
474 shiftwalked: | 475 shiftwalked: |
475 t = n->left->type; | 476 t = n->left->type; |
476 n->bounded = bounded(n->right, 8*t->width); | 477 n->bounded = bounded(n->right, 8*t->width); |
477 if(debug['m'] && n->etype && !isconst(n->right, CTINT)) | 478 if(debug['m'] && n->etype && !isconst(n->right, CTINT)) |
478 warn("shift bounds check elided"); | 479 warn("shift bounds check elided"); |
479 goto ret; | 480 goto ret; |
480 | 481 |
481 case OAND: | 482 case OAND: |
482 case OSUB: | 483 case OSUB: |
483 case OMUL: | 484 case OMUL: |
485 case OHMUL: | |
484 case OLT: | 486 case OLT: |
485 case OLE: | 487 case OLE: |
486 case OGE: | 488 case OGE: |
487 case OGT: | 489 case OGT: |
488 case OADD: | 490 case OADD: |
489 case OCOMPLEX: | 491 case OCOMPLEX: |
490 case OLROT: | 492 case OLROT: |
491 walkexpr(&n->left, init); | 493 walkexpr(&n->left, init); |
492 walkexpr(&n->right, init); | 494 walkexpr(&n->right, init); |
493 goto ret; | 495 goto ret; |
(...skipping 392 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
886 n->left = safeexpr(n->left, init); | 888 n->left = safeexpr(n->left, init); |
887 walkexpr(&n->left, init); | 889 walkexpr(&n->left, init); |
888 l = n->left; | 890 l = n->left; |
889 walkexpr(&n->right, init); | 891 walkexpr(&n->right, init); |
890 | 892 |
891 /* | 893 /* |
892 * on 32-bit arch, rewrite 64-bit ops into l = l op r. | 894 * on 32-bit arch, rewrite 64-bit ops into l = l op r. |
893 * on 386, rewrite float ops into l = l op r. | 895 * on 386, rewrite float ops into l = l op r. |
894 * everywhere, rewrite map ops into l = l op r. | 896 * everywhere, rewrite map ops into l = l op r. |
895 * everywhere, rewrite string += into l = l op r. | 897 * everywhere, rewrite string += into l = l op r. |
896 » » * everywhere, rewrite complex /= into l = l op r. | 898 » » * everywhere, rewrite integer/complex /= into l = l op r. |
897 * TODO(rsc): Maybe this rewrite should be done always? | 899 * TODO(rsc): Maybe this rewrite should be done always? |
898 */ | 900 */ |
899 et = n->left->type->etype; | 901 et = n->left->type->etype; |
900 if((widthptr == 4 && (et == TUINT64 || et == TINT64)) || | 902 if((widthptr == 4 && (et == TUINT64 || et == TINT64)) || |
901 (thechar == '8' && isfloat[et]) || | 903 (thechar == '8' && isfloat[et]) || |
902 l->op == OINDEXMAP || | 904 l->op == OINDEXMAP || |
903 et == TSTRING || | 905 et == TSTRING || |
904 » » (iscomplex[et] && n->etype == ODIV)) { | 906 » » (!isfloat[et] && n->etype == ODIV) || |
907 » » n->etype == OMOD) { | |
905 l = safeexpr(n->left, init); | 908 l = safeexpr(n->left, init); |
906 a = l; | 909 a = l; |
907 if(a->op == OINDEXMAP) { | 910 if(a->op == OINDEXMAP) { |
908 // map index has "lhs" bit set in a->etype. | 911 // map index has "lhs" bit set in a->etype. |
909 // make a copy so we can clear it on the rhs. | 912 // make a copy so we can clear it on the rhs. |
910 a = nod(OXXX, N, N); | 913 a = nod(OXXX, N, N); |
911 *a = *l; | 914 *a = *l; |
912 a->etype = 0; | 915 a->etype = 0; |
913 } | 916 } |
914 r = nod(OAS, l, nod(n->etype, a, n->right)); | 917 r = nod(OAS, l, nod(n->etype, a, n->right)); |
(...skipping 23 matching lines...) Expand all Loading... | |
938 */ | 941 */ |
939 et = n->left->type->etype; | 942 et = n->left->type->etype; |
940 if(iscomplex[et] && n->op == ODIV) { | 943 if(iscomplex[et] && n->op == ODIV) { |
941 t = n->type; | 944 t = n->type; |
942 n = mkcall("complex128div", types[TCOMPLEX128], init, | 945 n = mkcall("complex128div", types[TCOMPLEX128], init, |
943 conv(n->left, types[TCOMPLEX128]), | 946 conv(n->left, types[TCOMPLEX128]), |
944 conv(n->right, types[TCOMPLEX128])); | 947 conv(n->right, types[TCOMPLEX128])); |
945 n = conv(n, t); | 948 n = conv(n, t); |
946 goto ret; | 949 goto ret; |
947 } | 950 } |
951 // Nothing to do for float divisions. | |
952 if(isfloat[et]) | |
953 goto ret; | |
954 | |
955 // Try rewriting as shifts or magic multiplies. | |
956 walkdiv(&n, init); | |
957 | |
948 /* | 958 /* |
949 * rewrite div and mod into function calls | 959 * rewrite div and mod into function calls |
rsc
2012/11/26 16:28:34
rewrite 64-bit div and mod...
remyoudompheng
2012/11/26 21:18:55
Done.
| |
950 * on 32-bit architectures. | 960 * on 32-bit architectures. |
951 */ | 961 */ |
952 » » if(widthptr > 4 || (et != TUINT64 && et != TINT64)) | 962 » » switch(n->op) { |
953 » » » goto ret; | 963 » » case OMOD: |
954 » » if(et == TINT64) | 964 » » case ODIV: |
955 » » » strcpy(namebuf, "int64"); | 965 » » » if(widthptr > 4 || (et != TUINT64 && et != TINT64)) |
956 » » else | 966 » » » » goto ret; |
957 » » » strcpy(namebuf, "uint64"); | 967 » » » if(et == TINT64) |
958 » » if(n->op == ODIV) | 968 » » » » strcpy(namebuf, "int64"); |
959 » » » strcat(namebuf, "div"); | 969 » » » else |
960 » » else | 970 » » » » strcpy(namebuf, "uint64"); |
961 » » » strcat(namebuf, "mod"); | 971 » » » if(n->op == ODIV) |
962 » » n = mkcall(namebuf, n->type, init, | 972 » » » » strcat(namebuf, "div"); |
963 » » » conv(n->left, types[et]), conv(n->right, types[et])); | 973 » » » else |
974 » » » » strcat(namebuf, "mod"); | |
975 » » » n = mkcall(namebuf, n->type, init, | |
976 » » » » conv(n->left, types[et]), conv(n->right, types[e t])); | |
977 » » » break; | |
978 » » default: | |
979 » » » break; | |
980 » » } | |
964 goto ret; | 981 goto ret; |
965 | 982 |
966 case OINDEX: | 983 case OINDEX: |
967 walkexpr(&n->left, init); | 984 walkexpr(&n->left, init); |
985 // save the original node for bounds checking elision. | |
986 // If it was a ODIV/OMOD walk might rewrite it. | |
987 r = n->right; | |
968 walkexpr(&n->right, init); | 988 walkexpr(&n->right, init); |
969 | 989 |
970 // if range of type cannot exceed static array bound, | 990 // if range of type cannot exceed static array bound, |
971 // disable bounds check. | 991 // disable bounds check. |
972 if(n->bounded) | 992 if(n->bounded) |
973 goto ret; | 993 goto ret; |
974 t = n->left->type; | 994 t = n->left->type; |
975 if(t != T && isptr[t->etype]) | 995 if(t != T && isptr[t->etype]) |
976 t = t->type; | 996 t = t->type; |
977 if(isfixedarray(t)) { | 997 if(isfixedarray(t)) { |
978 » » » n->bounded = bounded(n->right, t->bound); | 998 » » » n->bounded = bounded(r, t->bound); |
979 if(debug['m'] && n->bounded && !isconst(n->right, CTINT) ) | 999 if(debug['m'] && n->bounded && !isconst(n->right, CTINT) ) |
980 warn("index bounds check elided"); | 1000 warn("index bounds check elided"); |
981 if(smallintconst(n->right) && !n->bounded) | 1001 if(smallintconst(n->right) && !n->bounded) |
982 yyerror("index out of bounds"); | 1002 yyerror("index out of bounds"); |
983 } else if(isconst(n->left, CTSTR)) { | 1003 } else if(isconst(n->left, CTSTR)) { |
984 » » » n->bounded = bounded(n->right, n->left->val.u.sval->len) ; | 1004 » » » n->bounded = bounded(r, n->left->val.u.sval->len); |
985 if(debug['m'] && n->bounded && !isconst(n->right, CTINT) ) | 1005 if(debug['m'] && n->bounded && !isconst(n->right, CTINT) ) |
986 warn("index bounds check elided"); | 1006 warn("index bounds check elided"); |
987 if(smallintconst(n->right)) { | 1007 if(smallintconst(n->right)) { |
988 if(!n->bounded) | 1008 if(!n->bounded) |
989 yyerror("index out of bounds"); | 1009 yyerror("index out of bounds"); |
990 else { | 1010 else { |
991 // replace "abc"[2] with 'b'. | 1011 // replace "abc"[2] with 'b'. |
992 // delayed until now because "abc"[2] is not | 1012 // delayed until now because "abc"[2] is not |
993 // an ideal constant. | 1013 // an ideal constant. |
994 v = mpgetfix(n->right->val.u.xval); | 1014 v = mpgetfix(n->right->val.u.xval); |
(...skipping 1861 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2856 ········ | 2876 ········ |
2857 // Remove rotate 0 and rotate w. | 2877 // Remove rotate 0 and rotate w. |
2858 s = mpgetfix(n->right->val.u.xval); | 2878 s = mpgetfix(n->right->val.u.xval); |
2859 if(s == 0 || s == w) | 2879 if(s == 0 || s == w) |
2860 n = n->left; | 2880 n = n->left; |
2861 | 2881 |
2862 *np = n; | 2882 *np = n; |
2863 return; | 2883 return; |
2864 } | 2884 } |
2865 | 2885 |
2886 /* | |
2887 * walkdiv rewrites division by a constant as less expensive | |
2888 * operations. | |
2889 */ | |
2890 static void | |
2891 walkdiv(Node **np, NodeList **init) | |
2892 { | |
2893 Node *n, *nl, *nr, *nc; | |
2894 Node *n1, *n2, *n3, *n4; | |
2895 int pow; // if >= 0, nr is 1<<n | |
rsc
2012/11/26 16:28:34
1<<pow?
remyoudompheng
2012/11/26 21:18:55
Done.
| |
2896 int s; // 1 if nr is negative. | |
2897 int w; | |
2898 Type *twide; | |
2899 Magic m; | |
2900 | |
2901 n = *np; | |
2902 if(n->right->op != OLITERAL) | |
2903 return; | |
2904 // nr is a constant. | |
2905 nl = cheapexpr(n->left, init); | |
2906 nr = n->right; | |
2907 | |
2908 // special cases of mod/div | |
2909 // by a constant | |
2910 w = nl->type->width*8; | |
2911 s = 0; | |
2912 pow = powtwo(nr); | |
2913 if(pow >= 1000) { | |
2914 // negative power of 2 | |
2915 s = 1; | |
2916 pow -= 1000; | |
2917 } | |
2918 | |
2919 if(pow+1 >= w) { | |
2920 // divisor too large. | |
2921 return; | |
2922 } | |
2923 if(pow < 0) { | |
2924 goto divbymul; | |
2925 } | |
2926 | |
2927 switch(pow) { | |
2928 case 0: | |
2929 if(n->op == OMOD) { | |
2930 // nl % 1 is zero. | |
2931 nodconst(n, n->type, 0); | |
2932 } else if(s) { | |
2933 // divide by -1 | |
2934 n->op = OMINUS; | |
2935 n->right = N; | |
2936 } else { | |
2937 // divide by 1 | |
2938 n = nl; | |
2939 } | |
2940 break; | |
2941 default: | |
2942 if(issigned[n->type->etype]) { | |
2943 if(n->op == OMOD) { | |
2944 // signed modulo 2^pow is like ANDing | |
2945 // with the last pow bits, but if nl < 0, | |
2946 // nl & (2^pow-1) is (nl+1)%2^pow - 1. | |
2947 nc = nod(OXXX, N, N); | |
2948 nodconst(nc, types[simtype[TUINT]], w-1); | |
2949 n1 = nod(ORSH, nl, nc); // n1 = -1 iff nl < 0. | |
2950 if(pow == 1) { | |
2951 typecheck(&n1, Erv); | |
2952 n1 = cheapexpr(n1, init); | |
2953 // n = (nl+ε)&1 -ε where ε=1 iff nl<0. | |
2954 n2 = nod(OSUB, nl, n1); | |
2955 nc = nod(OXXX, N, N); | |
2956 nodconst(nc, nl->type, 1); | |
2957 n3 = nod(OAND, n2, nc); | |
2958 n = nod(OADD, n3, n1); | |
2959 } else { | |
2960 // n = (nl+ε)&(nr-1) - ε where ε=2^pow-1 iff nl<0. | |
2961 nc = nod(OXXX, N, N); | |
2962 nodconst(nc, nl->type, (1LL<<pow)-1); | |
2963 n2 = nod(OAND, n1, nc); // n2 = 2^pow-1 iff nl<0. | |
2964 typecheck(&n2, Erv); | |
2965 n2 = cheapexpr(n2, init); | |
2966 | |
2967 n3 = nod(OADD, nl, n2); | |
2968 n4 = nod(OAND, n3, nc); | |
2969 n = nod(OSUB, n4, n2); | |
2970 } | |
2971 break; | |
2972 } else { | |
2973 // arithmetic right shift does not give the corr ect rounding. | |
2974 // if nl >= 0, nl >> n == nl / nr | |
2975 // if nl < 0, we want to add 2^n-1 first. | |
2976 nc = nod(OXXX, N, N); | |
2977 nodconst(nc, types[simtype[TUINT]], w-1); | |
2978 n1 = nod(ORSH, nl, nc); // n1 = -1 iff nl < 0. | |
2979 if(pow == 1) { | |
2980 // nl+1 is nl-(-1) | |
2981 n->left = nod(OSUB, nl, n1); | |
2982 } else { | |
2983 // Do a logical right right on -1 to kee p pow bits. | |
2984 nc = nod(OXXX, N, N); | |
2985 nodconst(nc, types[simtype[TUINT]], w-po w); | |
2986 n2 = nod(ORSH, conv(n1, tounsigned(nl->t ype)), nc); | |
2987 n->left = nod(OADD, nl, conv(n2, nl->typ e)); | |
2988 } | |
2989 // n = (nl + 2^pow-1) >> pow | |
2990 n->op = ORSH; | |
2991 nc = nod(OXXX, N, N); | |
2992 nodconst(nc, types[simtype[TUINT]], pow); | |
2993 n->right = nc; | |
2994 n->typecheck = 0; | |
2995 } | |
2996 if(s) | |
2997 n = nod(OMINUS, n, N); | |
2998 break; | |
2999 } | |
3000 nc = nod(OXXX, N, N); | |
3001 if(n->op == OMOD) { | |
3002 // n = nl & (nr-1) | |
3003 n->op = OAND; | |
3004 nodconst(nc, nl->type, mpgetfix(nr->val.u.xval)-1); | |
3005 } else { | |
3006 // n = nl >> pow | |
3007 n->op = ORSH; | |
3008 nodconst(nc, types[simtype[TUINT]], pow); | |
3009 } | |
3010 n->typecheck = 0; | |
3011 n->right = nc; | |
3012 break; | |
3013 } | |
3014 goto ret; | |
3015 | |
3016 divbymul: | |
3017 // try to do division by multiply by (2^w)/d | |
3018 // see hacker's delight chapter 10 | |
3019 // TODO: support 64-bit magic multiply here. | |
3020 m.w = w; | |
3021 if(issigned[nl->type->etype]) { | |
3022 m.sd = mpgetfix(nr->val.u.xval); | |
3023 smagic(&m); | |
3024 } else { | |
3025 m.ud = mpgetfix(nr->val.u.xval); | |
3026 umagic(&m); | |
3027 } | |
3028 if(m.bad) | |
3029 return; | |
3030 | |
3031 // We have a quick division method so use it | |
3032 // for modulo too. | |
3033 if(n->op == OMOD) | |
3034 goto longmod; | |
3035 | |
3036 switch(simtype[nl->type->etype]) { | |
3037 default: | |
3038 return; | |
3039 | |
3040 case TUINT8: | |
3041 case TUINT16: | |
3042 case TUINT32: | |
3043 // n1 = nl * magic >> w (HMUL) | |
3044 nc = nod(OXXX, N, N); | |
3045 nodconst(nc, nl->type, m.um); | |
3046 n1 = nod(OMUL, nl, nc); | |
3047 typecheck(&n1, Erv); | |
3048 n1->op = OHMUL; | |
3049 if(m.ua) { | |
3050 // Select a Go type with (at least) twice the width. | |
3051 switch(simtype[nl->type->etype]) { | |
3052 default: | |
3053 return; | |
3054 case TUINT8: | |
3055 case TUINT16: | |
3056 twide = types[TUINT32]; | |
3057 break; | |
3058 case TUINT32: | |
3059 twide = types[TUINT64]; | |
3060 break; | |
3061 case TINT8: | |
3062 case TINT16: | |
3063 twide = types[TINT32]; | |
3064 break; | |
3065 case TINT32: | |
3066 twide = types[TINT64]; | |
3067 break; | |
3068 } | |
3069 | |
3070 // add numerator (might overflow). | |
3071 // n2 = (n1 + nl) | |
3072 n2 = nod(OADD, conv(n1, twide), conv(nl, twide)); | |
3073 | |
3074 // shift by m.s | |
3075 nc = nod(OXXX, N, N); | |
3076 nodconst(nc, types[TUINT], m.s); | |
3077 n = conv(nod(ORSH, n2, nc), nl->type); | |
3078 } else { | |
3079 // n = n1 >> m.s | |
3080 nc = nod(OXXX, N, N); | |
3081 nodconst(nc, types[TUINT], m.s); | |
3082 n = nod(ORSH, n1, nc); | |
3083 } | |
3084 break; | |
3085 | |
3086 case TINT8: | |
3087 case TINT16: | |
3088 case TINT32: | |
3089 // n1 = nl * magic >> w | |
3090 nc = nod(OXXX, N, N); | |
3091 nodconst(nc, nl->type, m.sm); | |
3092 n1 = nod(OMUL, nl, nc); | |
3093 typecheck(&n1, Erv); | |
3094 n1->op = OHMUL; | |
3095 if(m.sm < 0) { | |
3096 // add the numerator. | |
3097 n1 = nod(OADD, n1, nl); | |
3098 } | |
3099 // shift by m.s | |
3100 nc = nod(OXXX, N, N); | |
3101 nodconst(nc, types[TUINT], m.s); | |
3102 n2 = conv(nod(ORSH, n1, nc), nl->type); | |
3103 // add 1 iff n1 is negative. | |
3104 nc = nod(OXXX, N, N); | |
3105 nodconst(nc, types[TUINT], w-1); | |
3106 n3 = nod(ORSH, nl, nc); // n4 = -1 iff n1 is negative. | |
3107 n = nod(OSUB, n2, n3); | |
3108 // apply sign. | |
3109 if(m.sd < 0) | |
3110 n = nod(OMINUS, n, N); | |
3111 break; | |
3112 } | |
3113 goto ret; | |
3114 | |
3115 longmod: | |
3116 // rewrite as A%B = A - (A/B*B). | |
3117 n1 = nod(ODIV, nl, nr); | |
3118 n2 = nod(OMUL, n1, nr); | |
3119 n = nod(OSUB, nl, n2); | |
3120 goto ret; | |
3121 | |
3122 ret: | |
3123 typecheck(&n, Erv); | |
3124 walkexpr(&n, init); | |
3125 *np = n; | |
3126 } | |
3127 | |
2866 // return 1 if integer n must be in range [0, max), 0 otherwise | 3128 // return 1 if integer n must be in range [0, max), 0 otherwise |
2867 static int | 3129 static int |
2868 bounded(Node *n, int64 max) | 3130 bounded(Node *n, int64 max) |
2869 { | 3131 { |
2870 int64 v; | 3132 int64 v; |
2871 int32 bits; | 3133 int32 bits; |
2872 int sign; | 3134 int sign; |
2873 | 3135 |
2874 if(n->type == T || !isint[n->type->etype]) | 3136 if(n->type == T || !isint[n->type->etype]) |
2875 return 0; | 3137 return 0; |
(...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2961 yyerror("tracked field must be in named struct type"); | 3223 yyerror("tracked field must be in named struct type"); |
2962 if(!exportname(field->sym->name)) | 3224 if(!exportname(field->sym->name)) |
2963 yyerror("tracked field must be exported (upper case)"); | 3225 yyerror("tracked field must be exported (upper case)"); |
2964 | 3226 |
2965 l = typ(0); | 3227 l = typ(0); |
2966 l->type = field; | 3228 l->type = field; |
2967 l->down = curfn->paramfld; | 3229 l->down = curfn->paramfld; |
2968 curfn->paramfld = l; | 3230 curfn->paramfld = l; |
2969 } | 3231 } |
2970 | 3232 |
OLD | NEW |