Re: [Dwarf-Discuss] [EXTERNAL] - RE: Multiple floating point types with the same size but different encodings

2022-01-25 Thread Jakub Jelinek via Dwarf-Discuss
On Mon, Jan 24, 2022 at 09:43:07PM -0500, John Reagan wrote:
> Yes, on OpenVMS we have 2 32-bit floats (VAX f_float, IEEE s_float), 3
> 64-bit floats (VAX d_float, VAX g_float, IEEE t_float), and 1 128-bit
> float (IEEE x_float).  We had a 2nd 128-bit float back on the VAX but we
> don't support that anymore.
> 
> Our current encoding on OpenVMS Itanium:
> 
> DW_ATE_Float: s_float (size 4), t_float (byte size 8), x_float (byte
> size 16)
> 
> DW_ATE_complex_float: s_float complex, t_float complex, x_float complex
> 
> DW_ATE_HP_VAX_float [0x88]: f_float (byte size 4), g_float (byte size 8)
> 
> DW_ATE_HP_VAX_float_d [0x89]: d_float (byte size 8)
> 
> DW_ATE_VAX_complex_float [0x8f]: f_float complex, g_float complex
> 
> DW_ATE_VAX_complex_float [0x90]: d_float complex
> 
> For choice, I'd guess that Ron might have more history as the comments
> on the code also say that HP-UX and NSK used the same codes too.  So I
> don't know if OpenVMS was the first user or if OpenVMS inherited it.

If s_float and f_float or t_float and g_float coexist on the same platform
in the same ABI, I'm afraid DW_AT_precision to distinguish between them,
IEEE single has 24-bit significand precision (1 bit implied) and
it seems s_float does too, and similarly IEEE double has 53-bit precision
(1 bit implied) and it seems g_gloat does too (d_float has 56-bit
precision).
So we'd need also exponent bias (or minimum or maximum exponent)
to differentiate between them.

Jakub

___
Dwarf-Discuss mailing list
Dwarf-Discuss@lists.dwarfstd.org
http://lists.dwarfstd.org/listinfo.cgi/dwarf-discuss-dwarfstd.org


Re: [Dwarf-Discuss] [EXTERNAL] - RE: Multiple floating point types with the same size but different encodings

2022-01-25 Thread Paul Robinson via Dwarf-Discuss
John Reagan tells me his message didn't go to the list,
but Jakub quotes it in his reply.  My comments below.

> -Original Message-
> From: Jakub Jelinek 
> Sent: Tuesday, January 25, 2022 7:10 AM
> To: John Reagan 
> Cc: Robinson, Paul ; ja...@redhat.com; dwarf-
> disc...@lists.dwarfstd.org
> Subject: Re: [EXTERNAL] - RE: [Dwarf-Discuss] Multiple floating point
> types with the same size but different encodings
> 
> On Mon, Jan 24, 2022 at 09:43:07PM -0500, John Reagan wrote:
> > Yes, on OpenVMS we have 2 32-bit floats (VAX f_float, IEEE s_float), 3
> > 64-bit floats (VAX d_float, VAX g_float, IEEE t_float), and 1 128-bit
> > float (IEEE x_float).  We had a 2nd 128-bit float back on the VAX but we
> > don't support that anymore.
> >
> > Our current encoding on OpenVMS Itanium:
> >
> > DW_ATE_Float: s_float (size 4), t_float (byte size 8), x_float (byte
> > size 16)
> >
> > DW_ATE_complex_float: s_float complex, t_float complex, x_float complex
> >
> > DW_ATE_HP_VAX_float [0x88]: f_float (byte size 4), g_float (byte size 8)
> >
> > DW_ATE_HP_VAX_float_d [0x89]: d_float (byte size 8)
> >
> > DW_ATE_VAX_complex_float [0x8f]: f_float complex, g_float complex
> >
> > DW_ATE_VAX_complex_float [0x90]: d_float complex
> >
> > For choice, I'd guess that Ron might have more history as the comments
> > on the code also say that HP-UX and NSK used the same codes too.  So I
> > don't know if OpenVMS was the first user or if OpenVMS inherited it.

I'd guess that HP-UX got there first, and the VAX-specific ones were added
for OpenVMS later.  NSK had a legacy floating-point format but I don't recall
how we described it.

> 
> If s_float and f_float or t_float and g_float coexist on the same platform
> in the same ABI, I'm afraid DW_AT_precision to distinguish between them,

Did you mean, DW_AT_precision isn't enough to distinguish between them?

> IEEE single has 24-bit significand precision (1 bit implied) and
> it seems s_float does too, and similarly IEEE double has 53-bit precision
> (1 bit implied) and it seems g_gloat does too (d_float has 56-bit
> precision).
> So we'd need also exponent bias (or minimum or maximum exponent)
> to differentiate between them.
> 
>   Jakub

It sounds like ATE codes would be better all around.  We could have a
pile of attributes that parameterize all the things, but that seems
overly general.
--paulr

___
Dwarf-Discuss mailing list
Dwarf-Discuss@lists.dwarfstd.org
http://lists.dwarfstd.org/listinfo.cgi/dwarf-discuss-dwarfstd.org


Re: [Dwarf-Discuss] [EXTERNAL] - RE: Multiple floating point types with the same size but different encodings

2022-01-25 Thread Jakub Jelinek via Dwarf-Discuss
On Tue, Jan 25, 2022 at 01:55:40PM +, paul.robin...@sony.com wrote:
> > If s_float and f_float or t_float and g_float coexist on the same platform
> > in the same ABI, I'm afraid DW_AT_precision to distinguish between them,
> 
> Did you mean, DW_AT_precision isn't enough to distinguish between them?

Yes.  My understanding is that e.g. IEEE single and VAX f_float both use
the same number of bits for exponent and mantissa, but the difference is that
f_float uses a different exponent bias and doesn't support NaNs/Infinities.
So when describing it through the C/C++ float.h macros,
*_MANT_DIG is the same, but they differ in *_MIN_EXP and *_MAX_EXP.

> > IEEE single has 24-bit significand precision (1 bit implied) and
> > it seems s_float does too, and similarly IEEE double has 53-bit precision
> > (1 bit implied) and it seems g_gloat does too (d_float has 56-bit
> > precision).
> > So we'd need also exponent bias (or minimum or maximum exponent)
> > to differentiate between them.
> 
> It sounds like ATE codes would be better all around.  We could have a
> pile of attributes that parameterize all the things, but that seems
> overly general.

For ATE codes, the problem is with standardization if we wanted
to standardize it in some way for DWARF6.
The current DW_ATE_{,complex_}float is way too unspecific and historically
can be about various formats.
So, we'd need something like DW_ATE_{,complex_}ieee754_float
(or ieee754_binary_float?)
which would depending on DW_AT_byte_size be binary{16,32,64,128,256}
format, and then add DW_ATE_* values for the floating point formats
known to us, which would be
vax_f_float, vax_g_float, vax_d_float (what about vax_h_float?)
bfloat16
Intel extended precision
IBM extended (double double)
what else?
Anyway, it might be possible as can be seen in the DW_ATE_HP_*
extensions to reuse the same DW_ATE_* code for multiple different
formats as long as they are guaranteed to have different DW_AT_byte_size.

For DW_AT_precision/DW_AT_min_exponent/DW_AT_max_exponent we would
just define them the same way as C/C++ does define corresponding
macros, e.g. https://en.cppreference.com/w/c/types/limits
(though of course, we can only reasonably use properties that are
expressible as small integral values or booleans, can't have
attributes matching to say FLT_MAX etc., which need some floating point
values).  All could be optional and the producers would need to use them
only if without those attributes it would be ambiguous what it is.

Jakub

___
Dwarf-Discuss mailing list
Dwarf-Discuss@lists.dwarfstd.org
http://lists.dwarfstd.org/listinfo.cgi/dwarf-discuss-dwarfstd.org


Re: [Dwarf-Discuss] [EXTERNAL] - RE: Multiple floating point types with the same size but different encodings

2022-01-25 Thread Ron Brender via Dwarf-Discuss
For the sake of history, I don't recall much of the technical issues, but I
do recall working with Paul way back when (we were both with HP at the
time) to come up with a single common HP-wide list of base types and codes
for all the floating-point types in use across HP.

I support Paul's suggestion that the best strategy is to simply enumerate
as DW_ATE codes all the "interesting" representations in use. This
enumeration can easily be extended if more are needed. A formal
parameterized model for floating point would force consumers to do a lot of
detective work to conclude that, for example, "Oh yeah, this is just the
IEEE 32-bit type". Alternatively, a dispatch on a DW_ATE code makes it easy
to support those, and only those, that are of interest for each
implementation

One issue that bears discussion is whether the existing types should be
considered "generic" (any floating point with a given size) and supplement
those with new codes that are very specific to actual implementations?

Ron

On Tue, Jan 25, 2022 at 8:55 AM Paul Robinson via Dwarf-Discuss <
dwarf-discuss@lists.dwarfstd.org> wrote:

> John Reagan tells me his message didn't go to the list,
> but Jakub quotes it in his reply.  My comments below.
>
> > -Original Message-
> > From: Jakub Jelinek 
> > Sent: Tuesday, January 25, 2022 7:10 AM
> > To: John Reagan 
> > Cc: Robinson, Paul ; ja...@redhat.com; dwarf-
> > disc...@lists.dwarfstd.org
> > Subject: Re: [EXTERNAL] - RE: [Dwarf-Discuss] Multiple floating point
> > types with the same size but different encodings
> >
> > On Mon, Jan 24, 2022 at 09:43:07PM -0500, John Reagan wrote:
> > > Yes, on OpenVMS we have 2 32-bit floats (VAX f_float, IEEE s_float), 3
> > > 64-bit floats (VAX d_float, VAX g_float, IEEE t_float), and 1 128-bit
> > > float (IEEE x_float).  We had a 2nd 128-bit float back on the VAX but
> we
> > > don't support that anymore.
> > >
> > > Our current encoding on OpenVMS Itanium:
> > >
> > > DW_ATE_Float: s_float (size 4), t_float (byte size 8), x_float (byte
> > > size 16)
> > >
> > > DW_ATE_complex_float: s_float complex, t_float complex, x_float complex
> > >
> > > DW_ATE_HP_VAX_float [0x88]: f_float (byte size 4), g_float (byte size
> 8)
> > >
> > > DW_ATE_HP_VAX_float_d [0x89]: d_float (byte size 8)
> > >
> > > DW_ATE_VAX_complex_float [0x8f]: f_float complex, g_float complex
> > >
> > > DW_ATE_VAX_complex_float [0x90]: d_float complex
> > >
> > > For choice, I'd guess that Ron might have more history as the comments
> > > on the code also say that HP-UX and NSK used the same codes too.  So I
> > > don't know if OpenVMS was the first user or if OpenVMS inherited it.
>
> I'd guess that HP-UX got there first, and the VAX-specific ones were added
> for OpenVMS later.  NSK had a legacy floating-point format but I don't
> recall
> how we described it.
>
> >
> > If s_float and f_float or t_float and g_float coexist on the same
> platform
> > in the same ABI, I'm afraid DW_AT_precision to distinguish between them,
>
> Did you mean, DW_AT_precision isn't enough to distinguish between them?
>
> > IEEE single has 24-bit significand precision (1 bit implied) and
> > it seems s_float does too, and similarly IEEE double has 53-bit precision
> > (1 bit implied) and it seems g_gloat does too (d_float has 56-bit
> > precision).
> > So we'd need also exponent bias (or minimum or maximum exponent)
> > to differentiate between them.
> >
> >   Jakub
>
> It sounds like ATE codes would be better all around.  We could have a
> pile of attributes that parameterize all the things, but that seems
> overly general.
> --paulr
>
> ___
> Dwarf-Discuss mailing list
> Dwarf-Discuss@lists.dwarfstd.org
> http://lists.dwarfstd.org/listinfo.cgi/dwarf-discuss-dwarfstd.org
>
___
Dwarf-Discuss mailing list
Dwarf-Discuss@lists.dwarfstd.org
http://lists.dwarfstd.org/listinfo.cgi/dwarf-discuss-dwarfstd.org


Re: [Dwarf-Discuss] [EXTERNAL] - RE: Multiple floating point types with the same size but different encodings

2022-01-25 Thread Todd Allen via Dwarf-Discuss
> 
> For ATE codes, the problem is with standardization if we wanted
> to standardize it in some way for DWARF6.
> The current DW_ATE_{,complex_}float is way too unspecific and historically
> can be about various formats.
> So, we'd need something like DW_ATE_{,complex_}ieee754_float
> (or ieee754_binary_float?)
> which would depending on DW_AT_byte_size be binary{16,32,64,128,256}
> format, and then add DW_ATE_* values for the floating point formats
> known to us, which would be
> vax_f_float, vax_g_float, vax_d_float (what about vax_h_float?)
> bfloat16
> Intel extended precision
> IBM extended (double double)
> what else?
> Anyway, it might be possible as can be seen in the DW_ATE_HP_*
> extensions to reuse the same DW_ATE_* code for multiple different
> formats as long as they are guaranteed to have different DW_AT_byte_size.
> 
> For DW_AT_precision/DW_AT_min_exponent/DW_AT_max_exponent we would
> just define them the same way as C/C++ does define corresponding
> macros, e.g. https://en.cppreference.com/w/c/types/limits
> (though of course, we can only reasonably use properties that are
> expressible as small integral values or booleans, can't have
> attributes matching to say FLT_MAX etc., which need some floating point
> values).  All could be optional and the producers would need to use them
> only if without those attributes it would be ambiguous what it is.
> 

I suspect you'd end up needing more attributes to completely pin down a
floating-point type.  Consider the i86/i87 FPU 80-bit floating-point type.  It
had a single bit which was the integer part, in additional to the traditional
fractional bits of the mantissa.  So determining the number of bits in the
exponent is not simply (bit_size - mantissa_bits - sign_bit).  Also, you'd need
to indicate lack of support for inf/nan, unless you were expecting that to be
deduced from the min_exponent/max_exponent.

The encodings do seem like a cleaner approach, as Ron argued: You either
recognize the enumerated value, probably because your hardware supports it
natively, and you don't care all that much about all the persnickety bits; or
you reject the type.  I suppose you might choose to support a non-native
floating-point type, but I suspect you'd need to have a priori knowledge of all
the details anyway.

If we do end up promoting any of these architecture-specific types into the
standard, perhaps we should provide some of the implementation details about
what they mean.  We could do that by referencing other documents, but it seems
reasonable to include a table containing the number of bits for each field, and
a mention of any major peculiarities (e.g. skewed bias, inf/nan unsupported,
etc.).

-- 
Todd Allen
Concurrent Real-Time
___
Dwarf-Discuss mailing list
Dwarf-Discuss@lists.dwarfstd.org
http://lists.dwarfstd.org/listinfo.cgi/dwarf-discuss-dwarfstd.org


Re: [Dwarf-Discuss] lambda (& other anonymous type) identification/naming

2022-01-25 Thread David Blaikie via Dwarf-Discuss
On Mon, Jan 24, 2022 at 5:37 PM Adrian Prantl  wrote:

>
>
> On Jan 23, 2022, at 2:53 PM, David Blaikie  wrote:
>
> A rather common "quality of implementation" issue seems to be lambda
> naming.
>
> I came across this due to non-canonicalization of lambda names in template
> parameters depending on how a source file is named in Clang, and GCC's seem
> to be very ambiguous:
>
> $ cat tmp/lambda.h
> template
> void f1(T) { }
> static int i = (f1([]{}), 1);
> static int j = (f1([]{}), 2);
> void f1() {
>   f1([]{});
>   f1([]{});
> }
> $ cat tmp/lambda.cpp
> #ifdef I_PATH
> #include 
> #else
> #include "lambda.h"
> #endif
> $ clang++-tot tmp/lambda.cpp -g -c -I. -DI_PATH && llvm-dwarfdump-tot
> lambda.o | grep "f1<"
> DW_AT_name  ("*f1<*(lambda at ./tmp/lambda.h:3:20)>")
> DW_AT_name  ("*f1<*(lambda at ./tmp/lambda.h:4:20)>")
> DW_AT_name  ("*f1<*(lambda at ./tmp/lambda.h:6:6)>")
> DW_AT_name  ("*f1<*(lambda at ./tmp/lambda.h:7:6)>")
> $ clang++-tot tmp/lambda.cpp -g -c && llvm-dwarfdump-tot lambda.o | grep
> "f1<"
> DW_AT_name  ("*f1<*(lambda at tmp/lambda.h:3:20)>")
> DW_AT_name  ("*f1<*(lambda at tmp/lambda.h:4:20)>")
> DW_AT_name  ("*f1<*(lambda at tmp/lambda.h:6:6)>")
> DW_AT_name  ("*f1<*(lambda at tmp/lambda.h:7:6)>")
> $ g++-tot tmp/lambda.cpp -g -c -I. && llvm-dwarfdump-tot lambda.o | grep
> "f1<"
> DW_AT_name  ("*f1<*f1():: >")
> DW_AT_name  ("*f1<*f1():: >")
> DW_AT_name  ("*f1<* >")
>
> DW_AT_name  ("*f1<* >")
>
> (I came across this in the context of my simplified template names work -
> rebuilding names from the DW_TAG description of the template parameters -
> and while I'm not rebuilding names that have lambda parameters (keep
> encoding the full string instead). The issue is if some other type
> depending on a type with a lambda parameter - but then multiple uses of
> that inner type exist, from different translation units (using type units)
> with different ways of naming the same file - so then the expected name has
> one spelling, but the actual spelling is different due to the "./")
>
> But all this said - it'd be good to figure out a reliable naming - the
> naming we have here, while usable for humans (pointing to surce files, etc)
> - they don't reliably give unique names for each lambda/template
> instantiation which would make it difficult for a consumer to know if two
> entities are the same (important for types - is some function parameter the
> same type as another type?)
>
> While it's expected cross-producer (eg: trying to be compatible with GCC
> and Clang debug info) you have to do some fuzzy matching (eg: "f1" or
> "f1" at the most basic - there are more complicated cases) - this
> one's not possible with the data available.
>
> The source file/line/column is insufficient to uniquely identify a lambda
> (multiple lambdas stamped out by a macro would get all the same
> file/line/col) and valid code (albeit unlikely) that writes the same
> definition in multiple places could make the same lambda have different
> names.
>
> We should probably use something more like the way various ABI manglings
> do to identify these entities.
>
> But we should probably also do this for other unnamed types that have
> linkage (need to/would benefit from being matched up between two CUs), even
> not lambdas.
>
> FWIW, at least the llvm-cxxfilt demanglings of clang's manglings for these
> symbols is:
>
>  void f1<$_0>($_0)
>  f1<$_1>($_1)
>  void f1(f1()::$_2)
>  void f1(f1()::$_3)
>
> Should we use that instead?
>
>
> The only other information that the current human-readable DWARF name
> carries is the file+line and that is fully redundant with DW_AT_file/line,
> so the above scheme seem reasonable to me. Poorly symbolicated backtraces
> would be worse in this scheme, so I'm expecting most pushback from users
> who rely on a tool that just prints the human readable name with no source
> info.
>

Yeah - you can always pull the file/line/col from the DW_AT_decl_* anyway,
so encoding it in the type name does seem redundant and inefficient indeed
(beyond/independent of the correctness issues).

> GCC's mangling's different (in these examples that's OK, since they're all
> internal linkage):
>
>  void f1(f1()::'lambda0'())
>  void f1(f1()::'lambda'())
>
> If I add an example like this:
>
> inline auto f1() { return []{}; }
>
> and instantiate the template with the result of f1:
>
>  void f1(f2()::'lambda'())
>
> GCC:
>
>  void f1(f2()::'lambda'())
>
> So they consistently use the same mangling - we could use the same naming
> for template parameters?
>
> How should we communicate this sort of identity for unnamed types in the
> DIEs describing the types themselves (not just the string of a template
> name of a type instantiated with the unnamed type) so the unnamed

Re: [Dwarf-Discuss] lambda (& other anonymous type) identification/naming

2022-01-25 Thread Adrian Prantl via Dwarf-Discuss


> On Jan 23, 2022, at 2:53 PM, David Blaikie  wrote:
> 
> A rather common "quality of implementation" issue seems to be lambda naming.
> 
> I came across this due to non-canonicalization of lambda names in template 
> parameters depending on how a source file is named in Clang, and GCC's seem 
> to be very ambiguous:
> 
> $ cat tmp/lambda.h
> template
> void f1(T) { }
> static int i = (f1([]{}), 1);
> static int j = (f1([]{}), 2);
> void f1() {
>   f1([]{});
>   f1([]{});
> }
> $ cat tmp/lambda.cpp
> #ifdef I_PATH
> #include 
> #else
> #include "lambda.h"
> #endif
> $ clang++-tot tmp/lambda.cpp -g -c -I. -DI_PATH && llvm-dwarfdump-tot 
> lambda.o | grep "f1<"
> DW_AT_name  ("f1<(lambda at ./tmp/lambda.h:3:20)>")
> DW_AT_name  ("f1<(lambda at ./tmp/lambda.h:4:20)>")
> DW_AT_name  ("f1<(lambda at ./tmp/lambda.h:6:6)>")
> DW_AT_name  ("f1<(lambda at ./tmp/lambda.h:7:6)>")
> $ clang++-tot tmp/lambda.cpp -g -c && llvm-dwarfdump-tot lambda.o | grep "f1<"
> DW_AT_name  ("f1<(lambda at tmp/lambda.h:3:20)>")
> DW_AT_name  ("f1<(lambda at tmp/lambda.h:4:20)>")
> DW_AT_name  ("f1<(lambda at tmp/lambda.h:6:6)>")
> DW_AT_name  ("f1<(lambda at tmp/lambda.h:7:6)>")
> $ g++-tot tmp/lambda.cpp -g -c -I. && llvm-dwarfdump-tot lambda.o | grep "f1<"
> DW_AT_name  ("f1 >")
> DW_AT_name  ("f1 >")
> DW_AT_name  ("f1< >")
> DW_AT_name  ("f1< >")
> 
> (I came across this in the context of my simplified template names work - 
> rebuilding names from the DW_TAG description of the template parameters - and 
> while I'm not rebuilding names that have lambda parameters (keep encoding the 
> full string instead). The issue is if some other type depending on a type 
> with a lambda parameter - but then multiple uses of that inner type exist, 
> from different translation units (using type units) with different ways of 
> naming the same file - so then the expected name has one spelling, but the 
> actual spelling is different due to the "./")
> 
> But all this said - it'd be good to figure out a reliable naming - the naming 
> we have here, while usable for humans (pointing to surce files, etc) - they 
> don't reliably give unique names for each lambda/template instantiation which 
> would make it difficult for a consumer to know if two entities are the same 
> (important for types - is some function parameter the same type as another 
> type?)
> 
> While it's expected cross-producer (eg: trying to be compatible with GCC and 
> Clang debug info) you have to do some fuzzy matching (eg: "f1" or 
> "f1" at the most basic - there are more complicated cases) - this 
> one's not possible with the data available.
> 
> The source file/line/column is insufficient to uniquely identify a lambda 
> (multiple lambdas stamped out by a macro would get all the same 
> file/line/col) and valid code (albeit unlikely) that writes the same 
> definition in multiple places could make the same lambda have different names.
> 
> We should probably use something more like the way various ABI manglings do 
> to identify these entities.
> 
> But we should probably also do this for other unnamed types that have linkage 
> (need to/would benefit from being matched up between two CUs), even not 
> lambdas.
> 
> FWIW, at least the llvm-cxxfilt demanglings of clang's manglings for these 
> symbols is:
> 
>  void f1<$_0>($_0)
>  f1<$_1>($_1)
>  void f1(f1()::$_2)
>  void f1(f1()::$_3)
> 
> Should we use that instead?

The only other information that the current human-readable DWARF name carries 
is the file+line and that is fully redundant with DW_AT_file/line, so the above 
scheme seem reasonable to me. Poorly symbolicated backtraces would be worse in 
this scheme, so I'm expecting most pushback from users who rely on a tool that 
just prints the human readable name with no source info.

> 
> GCC's mangling's different (in these examples that's OK, since they're all 
> internal linkage):
> 
>  void f1(f1()::'lambda0'())
>  void f1(f1()::'lambda'())
> 
> If I add an example like this:
> 
> inline auto f1() { return []{}; }
> 
> and instantiate the template with the result of f1:
> 
>  void f1(f2()::'lambda'())
> 
> GCC:
> 
>  void f1(f2()::'lambda'()) 
> 
> So they consistently use the same mangling - we could use the same naming for 
> template parameters?
> 
> How should we communicate this sort of identity for unnamed types in the DIEs 
> describing the types themselves (not just the string of a template name of a 
> type instantiated with the unnamed type) so the unnamed type can be matched 
> up between translation units.
> 
> eg, if I have these two translation units:
> // header
> inline auto f1() { struct { } local; return local; }
> // unit 1:
> #include "header"
> auto f2(decltype(f1())) { }
> // unit 2:
> #include "header