Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PATCH] fix PL_nan_u from leaking in every translation object on Win32 VC #14382

Closed
p5pRT opened this issue Dec 31, 2014 · 10 comments
Closed

[PATCH] fix PL_nan_u from leaking in every translation object on Win32 VC #14382

p5pRT opened this issue Dec 31, 2014 · 10 comments

Comments

@p5pRT
Copy link

p5pRT commented Dec 31, 2014

Migrated from rt.perl.org#123528 (status was 'resolved')

Searchable as RT123528$

@p5pRT
Copy link
Author

p5pRT commented Dec 31, 2014

From @bulk88

Created by @bulk88

need # for ticket.

failed due to ineffiency former plan

# define NV_NAN (*(double*)"\x00\x00\x00\x00\x00\x00\xF8\x7F")

SECTION HEADER #3F
  .rdata name
  0 physical address
  0 virtual address
  9 size of raw data
  4CA1 file pointer to raw data (00004CA1 to 00004CA9)
  0 file pointer to relocation table
  0 file pointer to line numbers
  0 number of relocations
  0 number of line numbers
40301040 flags
  Initialized Data
  COMDAT; sym= "`string'"
(??_C@​_08KIGKMAA@​?$AA?$AA?$AA?$AA?$AA?$AA?x?$HP?$AA@​)
  4 byte align
  Read Only

RAW DATA #3F
  00000000​: 00 00 00 00 00 00 F8 7F 00 ......ø..

it string is officially 9 bytes long, it will be rounded to 12 or 16. I
dont think there is a way to chop of the terminating null char to bring
back its size to 8 and not wasting all that space.

This is what a real FP symbol looks like

SECTION HEADER #47
  .rdata name
  0 physical address
  0 virtual address
  8 size of raw data
  5595 file pointer to raw data (00005595 to 0000559C)
  0 file pointer to relocation table
  0 file pointer to line numbers
  0 number of relocations
  0 number of line numbers
40401040 flags
  Initialized Data
  COMDAT; sym= __real@​7fefffffffffffff
  8 byte align
  Read Only

RAW DATA #47
  00000000​: FF FF FF FF FF FF EF 7F ÿÿÿÿÿÿï.

If you want more info, contact me offlist about this patch.

Perl Info

Flags:
                    category=core
                    severity=low

Site configuration information for perl 5.21.7:

Configured by Owner at Sat Nov 22 21:54:54 2014.

Summary of my perl5 (revision 5 version 21 subversion 7) configuration:
                  Local Commit: 1bce52df028aabe28c20b2d97949e35c17ea811e
                  Ancestor: 7072da8afeba4c87ae623cd913e274396ffcf1cd
                  Platform:
                    osname=MSWin32, osvers=5.1,
archname=MSWin32-x86-multi-thread
                    uname=''
                    config_args='undef'
                    hint=recommended, useposix=true, d_sigaction=undef
                    useithreads=define, usemultiplicity=define
                    use64bitint=undef, use64bitall=undef, 
uselongdouble=undef
                    usemymalloc=n, bincompat5005=undef
                  Compiler:
                    cc='cl', ccflags ='-nologo -GF -W3 -O1 -MD -Zi -DNDEBUG
-G7 -GL
-DWIN32 -D_CONSOLE -DNO_STRICT  -DPERL_TEXTMODE_SCRIPTS
-DPERL_HASH_FUNC_ONE_AT_A_TIME -DNO_MATHOMS -DPERL_IMPLICIT_CONTEXT
-DPERL_IMPLICIT_SYS -DUSE_PERLIO -D_USE_32BIT_TIME_T',
                    optimize='-O1 -MD -Zi -DNDEBUG -G7 -GL',
                    cppflags='-DWIN32'
                    ccversion='13.10.6030', gccversion='', gccosandvers=''
                    intsize=4, longsize=4, ptrsize=4, doublesize=8,
byteorder=1234,
doublekind=3
                    d_longlong=undef, longlongsize=8, d_longdbl=define,
longdblsize=8,
longdblkind=0
                    ivtype='long', ivsize=4, nvtype='double', nvsize=8,
Off_t='__int64',
lseeksize=8
                    alignbytes=8, prototype=define
                  Linker and Libraries:
                    ld='link', ldflags ='-nologo -nodefaultlib -debug
-opt:ref,icf
-ltcg  -libpath:"c:\perl521\lib\CORE"  -machine:x86'
                    libpth="C:\Program Files\Microsoft Visual Studio .NET
2003\VC7\lib"
                    libs=oldnames.lib kernel32.lib user32.lib gdi32.lib
winspool.lib
comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib
netapi32.lib uuid.lib ws2_32.lib mpr.lib winmm.lib  version.lib
odbc32.lib odbccp32.lib comctl32.lib msvcrt.lib
                    perllibs=oldnames.lib kernel32.lib user32.lib gdi32.lib
winspool.lib  comdlg32.lib advapi32.lib shell32.lib ole32.lib
oleaut32.lib  netapi32.lib uuid.lib ws2_32.lib mpr.lib winmm.lib
version.lib odbc32.lib odbccp32.lib comctl32.lib msvcrt.lib
                    libc=msvcrt.lib, so=dll, useshrplib=true,
libperl=perl521.lib
                    gnulibc_version=''
                  Dynamic Linking:
                    dlsrc=dl_win32.xs, dlext=dll, d_dlsymun=undef,
ccdlflags=' '
                    cccdlflags=' ', lddlflags='-dll -nologo -nodefaultlib
-debug
-opt:ref,icf -ltcg  -libpath:"c:\perl521\lib\CORE"  -machine:x86'

Locally applied patches:
                    ce7a4d57d0acca9f39a84d36d708c4505dfe45ca
                    ca0b263f4b167ddf97416f657d79ab5bd3344357
                    08919bf863666074243240abbd19cd1a74cc7b74
                    b8a043377dbf39548709b107a11e5cc2714c0e9a
                    efa855eb5cffb7739616c295dd968d1510efeeb0
                    1d47d0b810e26d9a2f9101fb813bd5b3dd332cc9
                    3faca062ddb056db54f73fa55b0a9d473675dd33
                    0b3e905bda3e75ad948a1213f620656b60807393
                    1b1efc719fde05d215e5a13fb38c03e12a3aab08
                    1bce52df028aabe28c20b2d97949e35c17ea811e


@INC for perl 5.21.7:
                    ..\lib
                    C:/perl521/srcnewb4opt/lib
                    .


Environment for perl 5.21.7:
                    HOME (unset)
                    LANG (unset)
                    LANGUAGE (unset)
                    LD_LIBRARY_PATH (unset)
                    LOGDIR (unset)
                    PATH=C:\WINDOWS\system32;C:\Program Files\Microsoft
Visual
Studio
.NET 2003\Vc7\bin;C:\Program Files\Microsoft Visual Studio .NET
2003\Common7\IDE;C:\WINDOWS;C:\Program Files\Git\cmd;C:\Program
Files\Microsoft Visual Studio .NET 2003\Common7\Tools\bin;C:\perl\bin
                    PERL_BADLANG (unset)
                    PERL_JSON_BACKEND=Cpanel::JSON::XS
                    PERL_YAML_BACKEND=YAML
                    SHELL (unset)




















@p5pRT
Copy link
Author

p5pRT commented Dec 31, 2014

From @bulk88

patch attached

@p5pRT
Copy link
Author

p5pRT commented Dec 31, 2014

From @bulk88

0001-fix-PL_nan_u-from-leaking-in-every-translation-objec.patch
From e59bc6ffbd41e63995f1ff31c529c92a70b36624 Mon Sep 17 00:00:00 2001
From: Daniel Dragan <bulk88@hotmail.com>
Date: Wed, 31 Dec 2014 07:44:08 -0500
Subject: [PATCH] fix PL_nan_u from leaking in every translation object on
 Win32 VC

A static const data symbol will not be deduped with its potentially
or actually identical static const data symbols by VC, even with LTCG/WPO.
This caused 43 unique copies of PL_nan_u, to appear in perl521.dll, Just
one copy , was used by code (S_my_atof_infnan and S_sv_setnv from
numeric.c). This bloat has to go. I also bet PL_nan_u will be appearing in
XS DLLs. To fix this use a unsigned __int64 with the right bitpattern in it
Selectany ("weak" in unix) will fix all problems of DOINIT, and is this an
XS module dynamically shared or static link. PL_nan_u should not be exported
on Win32 accross DLL boundaries. It isn't worth the overhead to fetch it
(2 32bit ptrs, 1 short, name of function call/data export, "\x00", plus 1 extra
machine instruction every time you want to fetch it)

Also JHI's HUGE_VAL solution can't constant fold. HUGE_VAL is imported
across DLL boundaries. I found a different C constant expression to create
infinity on VC. Now -Inf will be the actual value, it will not be runtime
computed by doing
"PTR_REG = *__imp__HUGE_VAL; FP_REG = *PTR_REG; fchngsign(FP_REG);"

perl521.dll vc 2003 section sizes in bytes
after text 0xc8633  .rdata 0x4852c .data 0x1738
beforetext 0xc8623  .rdata 0x485bc .data 0x1738

See ticket  [perl #123528]  for details.
---
 gv.c          |    2 +-
 perl.h        |    8 --------
 win32/win32.h |   26 ++++++++++++++++++++++++++
 3 files changed, 27 insertions(+), 9 deletions(-)

diff --git a/gv.c b/gv.c
index 8880aee..5001ebd 100644
--- a/gv.c
+++ b/gv.c
@@ -39,7 +39,7 @@ Perl stores its global variables.
 #include "feature.h"
 
 static const char S_autoload[] = "AUTOLOAD";
-static const STRLEN S_autolen = sizeof(S_autoload)-1;
+#define S_autolen (sizeof("AUTOLOAD")-1)
 
 SV *
 Perl_gv_add_by_type_p(pTHX_ GV *gv, gv_add_type type)
diff --git a/perl.h b/perl.h
index 55b8011..b287d63 100644
--- a/perl.h
+++ b/perl.h
@@ -4277,14 +4277,6 @@ START_EXTERN_C
 END_EXTERN_C
 #endif
 
-#ifdef WIN32
-#  if !defined(NV_INF) && defined(HUGE_VAL)
-#    define NV_INF HUGE_VAL
-#  endif
-/* For WIN32 the best NV_NAN is the __PL_nan_u trick, see below.
- * There is no supported way of getting the NAN across all the crts. */
-#endif
-
 /* If you are thinking of using HUGE_VAL for infinity, or using
  * <math.h> functions to generate NV_INF (e.g. exp(1e9), log(-1.0)),
  * stop.  Neither will work portably: HUGE_VAL can be just DBL_MAX,
diff --git a/win32/win32.h b/win32/win32.h
index 53f0500..57393a2 100644
--- a/win32/win32.h
+++ b/win32/win32.h
@@ -262,6 +262,32 @@ typedef unsigned short	mode_t;
 
 #define HAS_ANONFIELDS
 
+#  pragma warning(push)
+#  pragma warning(disable:4756)
+#  pragma warning(disable:4056)
+PERL_STATIC_INLINE
+double S_Infinity() {
+    /* this is a real C literal which can get further constant folded
+       unlike using HUGE_VAL/_HUGE which are data symbol imports from the CRT
+       and therefore can not by folded by VC, an example of constant
+       folding INF is creating -INF */
+    return (DBL_MAX+DBL_MAX);
+}
+#  pragma warning(pop)
+#  define NV_INF S_Infinity()
+
+/* selectany allows duplicate and unused data symbols to be removed by
+   VC linker, if this were static, each translation unit will have its own,
+   usually unused __PL_nan_u, if this were plain extern it will cause link
+   to fail due to multiple definitions, since we dont know if we are being
+   compiled as static or DLL XS, selectany simply always works, the cost of
+   importing __PL_nan_u across DLL boundaries in size in the importing DLL
+   will be more than the 8 bytes it will take up being in each XS DLL if
+   that DLL actually uses __PL_nan_u */
+extern const __declspec(selectany) union { unsigned __int64 __q; double __d; }
+__PL_nan_u = { 0x7FF8000000000000UI64 }; /* TODO test on VC6 */
+#  define NV_NAN ((NV)__PL_nan_u.__d)
+
 #endif /* _MSC_VER */
 
 #ifdef __MINGW32__		/* Minimal Gnu-Win32 */
-- 
1.7.9.msysgit.0

@p5pRT
Copy link
Author

p5pRT commented Dec 31, 2014

From @bulk88

On Wed Dec 31 04​:46​:08 2014, bulk88 wrote​:

patch attached

More background and a revised patch with vc6 tweaks attached.

PL_nan before

COFF SYMBOL TABLE
000 0000000F DEBUG notype Filename | .file
  ..\av.c
002 005F178E ABS notype Static | @​comp.id
003 00000001 ABS notype Static | @​feat.00
004 00000000 SECT1 notype Static | .drectve
  Section length 2C, #relocs 0, #linenums 0, checksum 0
006 00000000 SECT2 notype Static | .debug$S
  Section length 1BF2, #relocs 6, #linenums 0, checksum 0
008 00000000 SECT3 notype Static | .rdata
  Section length 4, #relocs 0, #linenums 0, checksum B40BBE3
00A 00000000 SECT3 notype Static | ___PL_nan_u

SECTION HEADER #3
  .rdata name
  0 physical address
  0 virtual address
  4 size of raw data
  27AE file pointer to raw data (000027AE to 000027B1)
  0 file pointer to relocation table
  0 file pointer to line numbers
  0 number of relocations
  0 number of line numbers
40300040 flags
  Initialized Data
  4 byte align
  Read Only

RAW DATA #3
  00000000​: 00 00 C0 7F ..À.

Notice there is no "COMDAT;" flag on the old PL_nan_u like on the string and the real (not type puned nan) FP double constant above.

On the new PL_nan_u there is a COMDAT flag

COFF SYMBOL TABLE
000 0000000F DEBUG notype Filename | .file
  ..\av.c
002 005F178E ABS notype Static | @​comp.id
003 00000001 ABS notype Static | @​feat.00
004 00000000 SECT1 notype Static | .drectve
  Section length 2C, #relocs 0, #linenums 0, checksum 0
006 00000000 SECT2 notype Static | .debug$S
  Section length 1BB2, #relocs 4, #linenums 0, checksum 0
008 00000000 SECT3 notype Static | .rdata
  Section length 8, #relocs 0, #linenums 0, checksum 1CDF0718, selection 2 (pick any)
00A 00000000 SECT3 notype External | ___PL_nan_u

SECTION HEADER #3
  .rdata name
  0 physical address
  0 virtual address
  8 size of raw data
  2782 file pointer to raw data (00002782 to 00002789)
  0 file pointer to relocation table
  0 file pointer to line numbers
  0 number of relocations
  0 number of line numbers
40401040 flags
  Initialized Data
  COMDAT; sym= ___PL_nan_u
  8 byte align
  Read Only

RAW DATA #3
  00000000​: 00 00 00 00 00 00 F8 7F ......ø.

--
bulk88 ~ bulk88 at hotmail.com

@p5pRT
Copy link
Author

p5pRT commented Dec 31, 2014

From @bulk88

0001-fix-PL_nan_u-from-leaking-in-every-translation-objec.patch
From 4d8db77db2dca3753df6ea9095ef8f12d9e8d2ba Mon Sep 17 00:00:00 2001
From: Daniel Dragan <bulk88@hotmail.com>
Date: Wed, 31 Dec 2014 15:58:07 -0500
Subject: [PATCH] fix PL_nan_u from leaking in every translation object on
 Win32 VC

A static const data symbol will not be deduped with its potentially
or actually identical static const data symbols by VC, even with LTCG/WPO.
This caused 43 unique copies of PL_nan_u, to appear in perl521.dll, Just
one copy was used by code (S_my_atof_infnan and S_sv_setnv from
numeric.c). This bloat has to go. I also bet PL_nan_u will be appearing in
XS DLLs. To fix this use a unsigned __int64 with the right bitpattern in it
Selectany ("weak" in unix) will fix all problems of DOINIT, and whether
this an XS module dynamically shared or static link. PL_nan_u should not
be exported on Win32 accross DLL boundaries. It isn't worth the overhead
to fetch it (2 32bit ptrs, 1 short, name of function call/data export, "\x00", plus 1
extra machine instruction every time you want to fetch it).

Also the HUGE_VAL solution can't constant fold. HUGE_VAL is imported
across DLL boundaries. I found a different C constant expression to create
infinity on VC. Now -Inf will be the actual value, it will not be runtime
computed by doing
"PTR_REG = *__imp__HUGE_VAL; FP_REG = *PTR_REG; fchngsign(FP_REG);".
NV_INF remained as a FP expression instead of becoming "PL_inf_u" so that
if in XS modules, a constant expression evaluted to FP INF
(symbol __real@7ff0000000000000), then it will be deduped together,
with any NV_INF usage. With a separate PL_inf_u, symbol PL_inf_u and
__real@7ff0000000000000 wont be deduped by current VCs (maybe in future
VCs they would be).

perl521.dll vc 2003 section sizes in bytes
after  text 0xc8633  .rdata 0x4852c .data 0x1738
before text 0xc8623  .rdata 0x485bc .data 0x1738

VC 6 but not 2003 ignored the warning disable around S_Infinity, and in
numeric.c and sv.c whereever NV_INF was used. VC 6 in -O1 reported at the
caller inline site warning 4756 for the caller and S_Infinity. So
unfortuantly it has to be done manually.

S_autolen was turned a macro since the static var is inefficient. Refering
to a static in the binary takes takes 5 bytes of machine code on x86,
loading the constant is 2 or 5 bytes. Also the overhead of the 4/8 bytes in
the binary for static storage.

See ticket [perl #123528] for details.
---
 gv.c          |    2 +-
 numeric.c     |    8 ++++++++
 perl.h        |    8 --------
 sv.c          |    7 +++++++
 win32/win32.h |   25 +++++++++++++++++++++++++
 5 files changed, 41 insertions(+), 9 deletions(-)

diff --git a/gv.c b/gv.c
index 8880aee..5001ebd 100644
--- a/gv.c
+++ b/gv.c
@@ -39,7 +39,7 @@ Perl stores its global variables.
 #include "feature.h"
 
 static const char S_autoload[] = "AUTOLOAD";
-static const STRLEN S_autolen = sizeof(S_autoload)-1;
+#define S_autolen (sizeof("AUTOLOAD")-1)
 
 SV *
 Perl_gv_add_by_type_p(pTHX_ GV *gv, gv_add_type type)
diff --git a/numeric.c b/numeric.c
index 9e05d55..ac69f45 100644
--- a/numeric.c
+++ b/numeric.c
@@ -1109,6 +1109,11 @@ Perl_my_atof(pTHX_ const char* s)
     return x;
 }
 
+
+#ifdef USING_MSVC6
+#  pragma warning(push)
+#  pragma warning(disable:4756;disable:4056)
+#endif
 static char*
 S_my_atof_infnan(const char* s, bool negative, const char* send, NV* value)
 {
@@ -1180,6 +1185,9 @@ S_my_atof_infnan(const char* s, bool negative, const char* send, NV* value)
     }
     return NULL;
 }
+#ifdef USING_MSVC6
+#  pragma warning(pop)
+#endif
 
 char*
 Perl_my_atof2(pTHX_ const char* orig, NV* value)
diff --git a/perl.h b/perl.h
index 55b8011..b287d63 100644
--- a/perl.h
+++ b/perl.h
@@ -4277,14 +4277,6 @@ START_EXTERN_C
 END_EXTERN_C
 #endif
 
-#ifdef WIN32
-#  if !defined(NV_INF) && defined(HUGE_VAL)
-#    define NV_INF HUGE_VAL
-#  endif
-/* For WIN32 the best NV_NAN is the __PL_nan_u trick, see below.
- * There is no supported way of getting the NAN across all the crts. */
-#endif
-
 /* If you are thinking of using HUGE_VAL for infinity, or using
  * <math.h> functions to generate NV_INF (e.g. exp(1e9), log(-1.0)),
  * stop.  Neither will work portably: HUGE_VAL can be just DBL_MAX,
diff --git a/sv.c b/sv.c
index 76cf403..76b3ef6 100644
--- a/sv.c
+++ b/sv.c
@@ -2145,6 +2145,10 @@ S_sv_2iuv_non_preserve(pTHX_ SV *const sv
 
 /* If numtype is infnan, set the NV of the sv accordingly.
  * If numtype is anything else, try setting the NV using Atof(PV). */
+#ifdef USING_MSVC6
+#  pragma warning(push)
+#  pragma warning(disable:4756;disable:4056)
+#endif
 static void
 S_sv_setnv(pTHX_ SV* sv, int numtype)
 {
@@ -2169,6 +2173,9 @@ S_sv_setnv(pTHX_ SV* sv, int numtype)
             SvPOK_on(sv); /* PV is okay, though. */
     }
 }
+#ifdef USING_MSVC6
+#  pragma warning(pop)
+#endif
 
 STATIC bool
 S_sv_2iuv_common(pTHX_ SV *const sv)
diff --git a/win32/win32.h b/win32/win32.h
index 53f0500..dfefe77 100644
--- a/win32/win32.h
+++ b/win32/win32.h
@@ -262,6 +262,31 @@ typedef unsigned short	mode_t;
 
 #define HAS_ANONFIELDS
 
+#  pragma warning(push)
+#  pragma warning(disable:4756;disable:4056)
+PERL_STATIC_INLINE
+double S_Infinity() {
+    /* this is a real C literal which can get further constant folded
+       unlike using HUGE_VAL/_HUGE which are data symbol imports from the CRT
+       and therefore can not by folded by VC, an example of constant
+       folding INF is creating -INF */
+    return (DBL_MAX+DBL_MAX);
+}
+#  pragma warning(pop)
+#  define NV_INF S_Infinity()
+
+/* selectany allows duplicate and unused data symbols to be removed by
+   VC linker, if this were static, each translation unit will have its own,
+   usually unused __PL_nan_u, if this were plain extern it will cause link
+   to fail due to multiple definitions, since we dont know if we are being
+   compiled as static or DLL XS, selectany simply always works, the cost of
+   importing __PL_nan_u across DLL boundaries in size in the importing DLL
+   will be more than the 8 bytes it will take up being in each XS DLL if
+   that DLL actually uses __PL_nan_u */
+extern const __declspec(selectany) union { unsigned __int64 __q; double __d; }
+__PL_nan_u = { 0x7FF8000000000000UI64 };
+#  define NV_NAN ((NV)__PL_nan_u.__d)
+
 #endif /* _MSC_VER */
 
 #ifdef __MINGW32__		/* Minimal Gnu-Win32 */
-- 
1.7.9.msysgit.0

@p5pRT
Copy link
Author

p5pRT commented Feb 4, 2015

From @bulk88

On Wed Dec 31 04​:42​:34 2014, bulk88 wrote​:

This is a bug report for perl from bulk88@​hotmail.com,
generated with the help of perlbug 1.40 running under perl 5.21.7.

-----------------------------------------------------------------
[Please describe your issue here]

need # for ticket.

Bump.

--
bulk88 ~ bulk88 at hotmail.com

@p5pRT
Copy link
Author

p5pRT commented Mar 16, 2015

From @bulk88

On Wed Feb 04 01​:33​:49 2015, bulk88 wrote​:

On Wed Dec 31 04​:42​:34 2014, bulk88 wrote​:

This is a bug report for perl from bulk88@​hotmail.com,
generated with the help of perlbug 1.40 running under perl 5.21.7.

-----------------------------------------------------------------
[Please describe your issue here]

need # for ticket.

Bump.
Bump.

--
bulk88 ~ bulk88 at hotmail.com

@p5pRT
Copy link
Author

p5pRT commented Mar 16, 2015

From @tonycoz

On Mon Mar 16 01​:54​:08 2015, bulk88 wrote​:

On Wed Feb 04 01​:33​:49 2015, bulk88 wrote​:

On Wed Dec 31 04​:42​:34 2014, bulk88 wrote​:

This is a bug report for perl from bulk88@​hotmail.com,
generated with the help of perlbug 1.40 running under perl 5.21.7.

-----------------------------------------------------------------
[Please describe your issue here]

need # for ticket.

Bump.
Bump.

This was applied as 3c81f0b by Jarkko.

Tony

@p5pRT
Copy link
Author

p5pRT commented Mar 16, 2015

The RT System itself - Status changed from 'new' to 'open'

@p5pRT
Copy link
Author

p5pRT commented Mar 16, 2015

@tonycoz - Status changed from 'open' to 'resolved'

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant