[M3devel] m3cc static chain stuff
Rodney M. Bates
rodney_bates at lcwb.coop
Fri May 28 02:50:51 CEST 2010
Jay K wrote:
> I also disfavor using gcc trampolines, for the same reason, they require executable stack, which is either unavailable, or probably quite slow (having to call mprotect).
>
>
> I had forgotten about them, and your reminding me helps me realize why we have to hack gcc a bit.
> Because it has a similar but inferior mechanism.
>
>
> One wonders about Ada though -- does it supported nested functions?
> I noticed it bears a striking resembles to Pascal/Modula-3. :)
Ada does have nested procedures, but it does not have variables or parameters
of procedure type at all, and it is these that need a closure/trampoline
mechanism, to hold a pointer to the "environment" of the runtime procedure
value.
Actually, Ada has a form of procedure parameters, but they are
"generic" parameters, which means "instantiation" (i.e., supplying
the actual procedure parameter) is done statically. This pretty
much always means the program unit with the procedure parameter
is copied at compile time and modified accordingly.
>
> And uses the same not great trampoline mechanism?
>
>
> If they could be moved to an "executable heap", and be made very debuggable, then I'd favor them.
> Those are two very big ifs.
>
>
> Why does m3gdb have to read trampolines? It has to extract a parameter from inside their code?
Yes, The environment (address of the activation record) is in the closure. If we used trampolines,
it would be in the trampoline instead. m3gdb has to read these when an m3gdb expression contains
a call on a parameter of procedure type, to correctly execute the call. It has to construct a
closure (or, again, it would be a trampoline, if we used them instead) to execute a call that
contains a nested procedure as an actual parameter.
> Ah.
> We could probably..if we really ever get here..which isn't all that likely, we could probably endeavor to format them like so:
>
> -- pointer to function --
> -- parameter/static chain whatever --
^ For a single function, there can be many values of this. Since this scheme
puts it right before the code of the nested procedure, there could only be one value of it. Every different
parameter (to some other procedure, usually) can have a different environment. So this won't work.
> trampoline points here => -- start of code --
> -- target dependent position independent not very optimized
> -- *constant* hand crafted code that fetchs the data from just before itself
>
>
> Would that address the m3gdb problem?
> Putting the data at fixed negative offsets from the trampoline code?
> Presumably putting it at fixed positive offsets is the problem -- you'd either round up the trampoline size
> to try to account for any target, of m3gdb would have to know where in the instruction stream per-target the data is written. Fixed negative offsets seem much better.
>
>
> Granted, crafting that code would be.. fun. :)
> Actually it would just be a call to a helper routine and the helper routine would fetch the return address and go from there..still gnarly.
>
>
> - Jay
>
>
> ----------------------------------------
>> From: hosking at cs.purdue.edu
>> Date: Thu, 27 May 2010 05:33:52 -0400
>> To: rodney_bates at lcwb.coop
>> CC: m3devel at elegosoft.com
>> Subject: Re: [M3devel] m3cc static chain stuff
>>
>> The trampoline mechanism is broken on many platforms because they disable executable stacks. I strongly favour retaining the current mechanisms.
>>
>> On 26 May 2010, at 19:14, Rodney M. Bates wrote:
>>
>>>
>>> Tony Hosking wrote:
>>>> We should endeavour to use the same functionality that the C compiler uses for its own nested functions, whereever possible. The only thing is that we also build closures, so the trampoline mechanisms should be avoided.
>>> I don't follow the last part of this argument. If we follow the first
>>> principle, we would just use trampolines, since they are the same
>>> mechanism the C compiler uses. And the only place they are not possible
>>> are places where they won't work for C either (e.g., a target where there
>>> is no place to construct code at runtime and have it be executable).
>>> The mere fact that we "also build closures" is not a necessity, just
>>> the reflection of the design decision to use the closure mechanism
>>> instead of trampolines.
>>>
>>> OTHO, despite all the positive things I have said here and in another post
>>> about trampolines, I don't favor using them, because they would make m3gdb
>>> support a lot more difficult. m3gdb needs to be able to both read and
>>> construct closures/trampolines, whichever. Closures are almost target-
>>> independent, except for the native word size, which is already easily
>>> available to m3gdb. Trampolines are going to be different for every
>>> instruction set, and the ones constructed by compiled code, at least,
>>> could even change with compiler version. m3gdb would need a _lot_ of
>>> highly target-dependent code to handle them.
>>>
>>>> Antony Hosking | Associate Professor | Computer Science | Purdue University
>>>> 305 N. University Street | West Lafayette | IN 47907 | USA
>>>> Office +1 765 494 6001 | Mobile +1 765 427 5484
>>>> On 26 May 2010, at 02:05, Jay K wrote:
>>>>> Can any of this be removed?
>>>>> Can we really not just use "unfold-nested-procs" like NT386?
>>>>> We'd just lose a little bit of optimization, in rare cases, stop paying a bad cost/benefit ratio?
>>>>> Part of this is actually *deoptimization*. If the compiler decides the values aren't used, then it should
>>>>> be allowed to remove them. That is normal. Wanting to unused stuff in the debugger isn't
>>>>> generally a considered the responsibility of optimizers.
>>>>>
>>>>> Therefore -- we could "unfold", remove our diffs, and gain some optimization and some deoptimization.
>>>>> Maybe I'll try without.
>>>>>
>>>>>
>>>>> diff -ur /src/orig/gcc-4.3.0/gcc/tree-nested.c /dev2/cm3/m3-sys/m3cc/gcc/gcc/tree-nested.c
>>>>> --- /src/orig/gcc-4.3.0/gcc/tree-nested.c 2008-02-15 09:36:43.000000000 -0800
>>>>> +++ /dev2/cm3/m3-sys/m3cc/gcc/gcc/tree-nested.c 2010-05-09 22:27:58.000000000 -0700
>>>>>
>>>>>
>>>>> -/* Build or return the RECORD_TYPE that describes the frame state that is
>>>>> - shared between INFO->CONTEXT and its nested functions. This record will
>>>>> - not be complete until finalize_nesting_tree; up until that point we'll
>>>>> +/* This must agree with the string defined by the same name in m3gdb, file
>>>>> + m3-util.c */
>>>>> +static const char * nonlocal_var_rec_name = "_nonlocal_var_rec";
>>>>> +
>>>>> +/* Build or return the RECORD_TYPE that describes the non-local frame struct
>>>>> + that is shared between INFO->CONTEXT and its nested functions. This record
>>>>> + will not be complete until finalize_nesting_tree; up until that point we'll
>>>>> be adding fields as necessary.
>>>>> We also build the DECL that represents this frame in the function. */
>>>>> @@ -209,7 +224,8 @@
>>>>> free (name);
>>>>> info->frame_type = type;
>>>>> - info->frame_decl = create_tmp_var_for (info, type, "FRAME");
>>>>> + info->frame_decl
>>>>> + = create_tmp_var_for (info, type, nonlocal_var_rec_name);
>>>>> /* ??? Always make it addressable for now, since it is meant to
>>>>> be pointed to by the static chain pointer. This pessimizes
>>>>> @@ -218,6 +234,8 @@
>>>>> reachable, but the true pessimization is to create the non-
>>>>> local frame structure in the first place. */
>>>>> TREE_ADDRESSABLE (info->frame_decl) = 1;
>>>>> + /* m3gdb needs to know about this variable. */
>>>>> + DECL_IGNORED_P (info->frame_decl) = 0; }
>>>>> return type;
>>>>> }
>>>>> @@ -290,6 +308,10 @@
>>>>> return *slot;
>>>>> }
>>>>> +/* This must agree with the string defined by the same name in m3gdb, file
>>>>> + m3_util.c */
>>>>> +static const char * static_link_var_name = "_static_link_var";
>>>>> +
>>>>> /* Build or return the variable that holds the static chain within
>>>>> INFO->CONTEXT. This variable may only be used within INFO->CONTEXT. */
>>>>> @@ -310,9 +332,14 @@
>>>>> Note also that it's represented as a parameter. This is more
>>>>> close to the truth, since the initial value does come from
>>>>> the caller. */
>>>>> - decl = build_decl (PARM_DECL, create_tmp_var_name ("CHAIN"), type);
>>>>> + decl = build_decl
>>>>> + (PARM_DECL, get_identifier (static_link_var_name), type);
>>>>> + TREE_CHAIN (decl) = NULL; /* Possibly redundant, but dbxout needs it. */
>>>>> DECL_ARTIFICIAL (decl) = 1;
>>>>> - DECL_IGNORED_P (decl) = 1;
>>>>> +
>>>>> + /* m3gdb needs to know about this variable. */
>>>>> + DECL_IGNORED_P (decl) = 0;
>>>>> +
>>>>> TREE_USED (decl) = 1;
>>>>> DECL_CONTEXT (decl) = info->context;
>>>>> DECL_ARG_TYPE (decl) = type;
>>>>> @@ -326,7 +353,11 @@
>>>>> return decl;
>>>>> }
>>>>> -/* Build or return the field within the non-local frame state that holds
>>>>> +/* This must agree with the string defined by the same name in m3gdb, file
>>>>> + m3_util.c */
>>>>> +static const char * static_link_copy_field_name = "_static_link_copy_field";
>>>>> +
>>>>> +/* Build or return the field within the non-local frame struct that holds
>>>>> the static chain for INFO->CONTEXT. This is the way to walk back up
>>>>> multiple nesting levels. */
>>>>> @@ -339,10 +370,12 @@
>>>>> tree type = build_pointer_type (get_frame_type (info->outer));
>>>>> field = make_node (FIELD_DECL);
>>>>> - DECL_NAME (field) = get_identifier ("__chain");
>>>>> + DECL_NAME (field) = get_identifier (static_link_copy_field_name);
>>>>> TREE_TYPE (field) = type;
>>>>> DECL_ALIGN (field) = TYPE_ALIGN (type);
>>>>> DECL_NONADDRESSABLE_P (field) = 1;
>>>>> + /* m3gdb should know about this field. */
>>>>> + DECL_IGNORED_P (field) = 0; insert_field_into_struct (get_frame_type (info), field);
>>>>> @@ -465,7 +498,7 @@
>>>>> return *slot;
>>>>> }
>>>>> -/* Build or return the field within the non-local frame state that holds
>>>>> +/* Build or return the field within the non-local frame struct that holds
>>>>> the non-local goto "jmp_buf". The buffer itself is maintained by the
>>>>> rtl middle-end as dynamic stack space is allocated. */
>>>>> @@ -1620,6 +1653,9 @@
>>>>> switch (TREE_CODE (t))
>>>>> {
>>>>> case ADDR_EXPR:
>>>>> + if (TREE_STATIC (t))
>>>>> + break;
>>>>> +
>>>>> /* Build
>>>>> T.1 = &CHAIN->tramp;
>>>>> T.2 = __builtin_adjust_trampoline (T.1);
>>>>> @@ -1714,6 +1750,22 @@
>>>>> }
>>>>> break;
>>>>> + case STATIC_CHAIN_EXPR:
>>>>> + decl = TREE_OPERAND (t, 0);
>>>>> + target_context = decl_function_context (decl);
>>>>> + if (target_context)
>>>>> + {
>>>>> + if (info->context == target_context)
>>>>> + {
>>>>> + /* Make sure frame_decl gets created. */
>>>>> + (void) get_frame_type (info);
>>>>> + }
>>>>> + *tp = get_static_chain (info, target_context, &wi->tsi);
>>>>> + }
>>>>> + else
>>>>> + *tp = null_pointer_node;
>>>>> + break;
>>>>> +
>>>>> case RETURN_EXPR:
>>>>> case GIMPLE_MODIFY_STMT:
>>>>> case WITH_SIZE_EXPR:
>>>>> @@ -1768,13 +1820,22 @@
>>>>> return NULL_TREE;
>>>>> }
>>>>> +static bool
>>>>> +debug_static_links (void)
>>>>> +{ return
>>>>> + write_symbols != NO_DEBUG
>>>>> + && debug_info_level != DINFO_LEVEL_NONE
>>>>> + && debug_info_level != DINFO_LEVEL_TERSE;
>>>>> +
>>>>> +} /* debug_static_links */
>>>>> +
>>>>> /* Walk the nesting tree starting with ROOT, depth first. Convert all
>>>>> trampolines and call expressions. On the way back up, determine if
>>>>> a nested function actually uses its static chain; if not, remember that. */
>>>>> static void
>>>>> convert_all_function_calls (struct nesting_info *root)
>>>>> -{
>>>>> +{
>>>>> do
>>>>> {
>>>>> if (root->inner)
>>>>> @@ -1784,7 +1845,10 @@
>>>>> walk_function (convert_call_expr, root);
>>>>> /* If the function does not use a static chain, then remember that. */
>>>>> - if (root->outer && !root->chain_decl && !root->chain_field)
>>>>> + if (root->outer && !root->chain_decl && !root->chain_field
>>>>> +/* REMOVE ME: */
>>>>> + /* && !debug_static_links () */
>>>>> + )
>>>>> DECL_NO_STATIC_CHAIN (root->context) = 1;
>>>>> else
>>>>> gcc_assert (!DECL_NO_STATIC_CHAIN (root->context));
>>>>> @@ -1806,6 +1870,21 @@
>>>>> tree context = root->context;
>>>>> struct function *sf;
>>>>> +/* REMOVEME: */
>>>>> + /* If this is a nested function and we are supporting debugging via
>>>>> + m3gdb, we always need a chain_decl, so m3gdb can find the static
>>>>> + chain, even if the programmer's code doesn't use it. */
>>>>> + if (false && root->outer && debug_static_links () )
>>>>> + { tree static_chain_decl, temp, stmt;
>>>>> + /* This is a desperate attempt to get later code generation to
>>>>> + store the static link. If it works, it'll be a miracle. */
>>>>> + static_chain_decl = get_chain_decl (root);
>>>>> + stmt = build_addr (static_chain_decl, root->context);
>>>>> + temp = create_tmp_var_for (root, TREE_TYPE (static_chain_decl), NULL);
>>>>> + stmt = build_gimple_modify_stmt (temp, static_chain_decl);
>>>>> + append_to_statement_list (stmt, &stmt_list);
>>>>> + }
>>>>> +
>>>>> /* If we created a non-local frame type or decl, we need to lay them
>>>>> out at this time. */
>>>>> if (root->frame_type)
>>>>> @@ -1912,7 +1991,7 @@
>>>>> proper BIND_EXPR. */
>>>>> if (root->new_local_var_chain)
>>>>> declare_vars (root->new_local_var_chain, DECL_SAVED_TREE (root->context),
>>>>> - false);
>>>>> + true);
>>>>> if (root->debug_var_chain)
>>>>> declare_vars (root->debug_var_chain, DECL_SAVED_TREE (root->context),
>>>>> true);
>>>>>
>>>>>
>>>>>
>>
More information about the M3devel
mailing list