Patch for setting DW_AT_const_value in identity declaration VAR_DECLs
Jose E. Marchesi
jemarch@gnu.org
Tue Sep 22 13:26:34 GMT 2026
Hi again.
I forgot to mention variables! :D
I think ideally all the identity declarations might be emitted as
DW_AT_const_value, because they are really constant once they get
initialized.
Now, will we need DW_AT_variable as well? The answer is yes.
But first I have to explain how the concept of "variable" is realized in
Algol 68.
The identity declaration is generally:
AMODE identifier = VALUE
Where AMODE is any mode. And any mode means _any_ mode in an
orthogonal language, no exceptions. So AMODE might be an integral
mode, a real number mode, a boolean mode, a string mode, a struct mode,
an union mode, a procedure mode, etc.
One of the kind of modes is the "name", or reference. These are of mode
`ref AMODE' and the values are references to other values. You can
think of them as sort of "pointers" (but not quite exactly, see below).
So for example, `ref int' is the mode of values which are references to
int. Now, what VALUE do you assign to a reference? Well, "names" or
reference values are conceptually storage, and you have of two kinds:
AMODE ID VALUE
------- --- -------
ref int foo = loc int; { `loc int' yields a ref to int value
on the stack. }
ref int foo = heap int; { `heap int' yields a ref to int value
allocated on the heap. }
loc and gen are "generators".
The assignation (assignment) construct looks like:
LHS := RHS
where LHS must be of some `ref AMODE' and the RHS must be of some AMODE.
Its side effect is to make the reference value at the LHS to refer to a
copy of the value at the RHS.
Now, if we would implement mutability using just identity declarations,
we would be continuously doing indirect addressing non-stop. Like for
example, to maintain a counter:
begin
ref int counter = loc int;
counter := 0; { ref int := int }
do
{ ... do something with counter ... }
od
end
There `counter' has mode `ref int' and is a VAR_DECL that is really a
constant, as already discussed. GCC will likely realize that VAR_DECL
as an automatic, that contains an *int. Therefore every access to
`counter' would require indirection, and thats sort of silly.
For this reason, and also to provide some syntactic sugar, in Algol 68
you have a "variable declaration" construct that looks like:
int counter;
Where the declared `counter' has mode `ref int', not int.
which is the equivalent to:
int counter = loc int;
and to:
loc int counter;
You can also do:
heap int counter;
which is the equivalent to:
int counter = heap int;
So, language wise, there is absolutely not difference between a counter
defined as `int counter = loc counter' and `int counter'. But
implementation wise, we compile variable declarations into VAR_DECLs
that are not constant, to avoid indirect addressing.
So, this code is totally equivalent to the previous version language
wise:
begin
int counter;
counter := 0; { ref int := int }
do
{ ... do something with counter ... }
od
end
But this time, `counter' results in a VAR_DECL with type int (and not
*int), not constant, and can be accessed using direct addressing.
For these we want to geneate DW_AT_variable values I guess..
> Hello Mark!
>
>> Hi Jose,
>>
>> On Tue, Sep 22, 2026 at 01:14:54AM +0200, Jose E. Marchesi wrote:
>>> This is the patch for getting the DW_AT_const_value in identity
>>> declaration DIEs in DWARF.
>>>
>>> For the small test program:
>>>
>>> begin
>>> int var := 10;
>>> int ide = 20;
>>> skip
>>> end
>>>
>>> It generates the DWARF:
>>>
>>> <4><7f>: Abbrev Number: 5 (DW_TAG_variable)
>>> <80> DW_AT_name : ide
>>> <84> DW_AT_decl_file : 2
>>> <85> DW_AT_decl_line : 3
>>> <86> DW_AT_decl_column : 11
>>> <87> DW_AT_type : <0x9e>
>>> <8b> DW_AT_const_value : 1
>>> <4><8c>: Abbrev Number: 6 (DW_TAG_variable)
>>> <8d> DW_AT_name : var
>>> <91> DW_AT_decl_file : 2
>>> <92> DW_AT_decl_line : 2
>>> <93> DW_AT_decl_column : 11
>>> <94> DW_AT_type : <0x9e>
>>> <98> DW_AT_location : 2 byte block: 91 6c (DW_OP_fbreg: -20)
>>
>> Is that really how you want to represent constant values?
>> DW_AT_const_value gives the constant value of a variable. But here it
>> seems to be used as a flag whether the value is constant? (I would
>> have expected it to say 20.) There is DW_TAG_constant, which is used
>> for named constants.
>
> Ok this requires a bit of explanation. We are having a hard time to
> decide which DWARF would be best for the language's language constructs.
> Algol 68 is hard :') [BTW we will be discussing about this in the GNU
> Algol 68 BoF in Cauldron, Sunday at 9AM. It would be great if you could
> pass by and give us some advise. We would really benefit from the input
> someone who actually knows about DWARF.]
>
> Allright, so in Algol 68 the usual concepts of "constant", "variable",
> "pointer", parameter passing, etc, are generalized in a single concept:
> the "identity declaration", in which some value gets ascribed to an
> identifier, like in:
>
>
> int foo = 10;
>
> After that, the identifier `foo' could be literally substituted by its
> ascribed value (10) and everything would be ok. Note that the mode
> (type) of the so-defined identifier may be any mode, including multiples
> (arrays), structs, etc, like:
>
>
> mode Person = struct (string name, int age,
> struct (string street, city, country) addr);
>
> Person me = ("Jose Marchesi", 46, ("C/Foo", "Frankfurt", "Germany"));
>
> The ascribed value doens't have to be a denotation (literal), but it
> could literally anything, like in:
>
> Person me = if is_monday then jose else mrhyde fi;
>
> Once ascribed, there is simply no way to modify the value ascribed to
> the identifier in the identity declaration. This is assured by a) the
> value ascribed is actually a copy of the value that appears at the right
> in the identity declaration, and b) assignation (assignment) cannot be
> used to change an ascribed value. An identity declaration is the way to
> use a "constant" in Algol 68.
>
>
> Now, the "scope" (range in C terms) of an identifier declared in an
> identity declaration is the range (scope in C terms) in which it is
> declared, including _before_ the identity declaration, like in:
>
> begin
> { foo is accessible here, its constant value is 0. }
> int foo = 10;
> { foo is accessible here, its constant value is 10. }
> end
>
> Note { and } are comment delimiters.
>
> When the front-end finds the above code, it lowers it to a GENERIC like:
>
> void __algol68_main ()
> {
> STANDARD__prelude ();
> POSIX__prelude ();
> {
> void stop;
>
> {
> signed int foo = 0;
>
> /* foo is accessible here and its value is 0 */
> foo = 10;
> /* foo is accessible here and its value is 10 */
> }
> stop:, {};
> }
> POSIX__postlude ();
> STANDARD__postlude ();
> }
>
> In GENERIC terms, the front-end creates a VAR_DECL for foo, sets its
> DECL_INITIAL to zero, and then emits a MODIFY_EXPR to set it to 10 at
> the point in the program in which the identity declaration happens.
>
> Setting DECL_INITIAL to 10 wouldn't work. First, because it would lead
> to:
>
> void __algol68_main ()
> {
> STANDARD__prelude ();
> POSIX__prelude ();
> {
> void stop;
>
> {
> signed int foo = 10;
>
> /* foo is accessible here and its value is 10 (WRONG) */
> /* Identity declaration is here. */
> /* foo is accessible here and its value is 10 */
> }
> stop:, {};
> }
> POSIX__postlude ();
> STANDARD__postlude ();
> }
>
> And worse, because the following code would miscompile, since the
> initializations may happen at any order, and at the beginning of the
> block:
>
> { Make sure structs get copied when ascribed. }
> begin
> mode Bar = struct (int j, real r);
> mode Foo = struct (int i, Bar bar);
>
> Foo f1 := (10, (20, 3.14));
> Foo f2 = f1;
>
> j of bar of f1 := 200;
> assert (j of bar of f1 = 200);
> assert (j of bar of f2 = 20)
> end
>
> So this is the reason why we cannot mark the VAR_DECL resulting from
> identity declarations as DECL_CONSTANT and TREE_READ_ONLY: we need to do
> the MODIFY_EXPR at the right place.
>
> Yesterday I was brainsorming a bit with cuper and faust, and we thought
> about ways of doing this better, but none works:
>
> - First, we thought about doing something like:
>
>
> begin
> int foo = 10;
> { XXX }
> int bar = 20;
> { YYY }
> end
>
> to the generic:
>
> {
> {
> int foo = 10; VAR_DECL with constant
> /* XXX */
>
> {
> int bar = 20; VAR DECL with constant
> /* YYY */
> }
> }
> }
>
> i.e. to inroduce a block for each declaration, so we can put the
> ascribed value in DECL_INIT. This of course wouldn't work because foo
> and bar wouldn't be accessible before they are declared.
>
> - Then we went more crazy and thought about:
>
> begin
> { PRE }
> int foo = 10;
> { POST }
> end
>
> to be lowered to the GENERIC:
>
>
> {
> int foo' = 0; VAR_DECL non-constant
>
> /* PRE uses foo' for identifier foo */
> {
> int foo = 10; VAR_DECL constant
> /* POST uses foo for identifier foo */
> }
> }
>
> but this is not satisfactory, because foo' is suppose to be constant
> too.
>
> So I guess this all boils down to... what would be the best way to
> denote all this in DWARF? :)
>
>
>>
>> Cheers,
>>
>> Mark
>>
>>> diff --git a/gcc/algol68/a68-lang.cc b/gcc/algol68/a68-lang.cc
>>> index 6c3e607cb0c..042002eb1b3 100644
>>> --- a/gcc/algol68/a68-lang.cc
>>> +++ b/gcc/algol68/a68-lang.cc
>>> @@ -31,6 +31,7 @@
>>> #include "machmode.h"
>>> #include "stor-layout.h" /* For layout_type */
>>> #include "vec.h"
>>> +#include "dwarf2.h"
>>>
>>> #include "a68.h"
>>>
>>> @@ -50,6 +51,7 @@ struct GTY(()) lang_type
>>> struct GTY(()) lang_decl
>>> {
>>> NODE_T * node;
>>> + bool is_var;
>>> };
>>>
>>> /* Language-specific identifier information. This must include a
>>> @@ -100,7 +102,7 @@ a68_build_lang_type (MOID_T *moid)
>>> /* Allocate and return a lang specific structure for decl tree nodes. */
>>>
>>> struct lang_decl *
>>> -a68_build_lang_decl (NODE_T *node)
>>> +a68_build_lang_decl (NODE_T *node, bool is_var)
>>> {
>>> tree cdecl = CDECL (node);
>>> struct lang_decl *ld = cdecl ? DECL_LANG_SPECIFIC (cdecl) : NULL;
>>> @@ -109,9 +111,29 @@ a68_build_lang_decl (NODE_T *node)
>>> ld = (struct lang_decl *) ggc_cleared_alloc <struct lang_decl> ();
>>> if (ld->node == NULL)
>>> ld->node = node;
>>> + ld->is_var = is_var;
>>> return ld;
>>> }
>>>
>>> +/* Get the front-end is_var attribute associated with the given DECL. */
>>> +
>>> +bool
>>> +a68_decl_is_var (const_tree decl)
>>> +{
>>> + gcc_assert (VAR_P (decl));
>>> + return DECL_LANG_SPECIFIC (decl) && DECL_LANG_SPECIFIC (decl)->is_var;
>>> +}
>>> +
>>> +/* Set the front-end is_var attribute associated with DECL to the given
>>> + IS_VAR. */
>>> +
>>> +void
>>> +a68_decl_set_is_var (tree decl, bool is_var)
>>> +{
>>> + gcc_assert (VAR_P (decl));
>>> + DECL_LANG_SPECIFIC (decl)->is_var = is_var;
>>> +}
>>> +
>>> /* Get the front-end mode associated with the given TYPE. If no mode is
>>> associated then this function returns NO_MODE. */
>>>
>>> @@ -436,6 +458,25 @@ a68_getdecls (void)
>>> #undef LANG_HOOKS_GETDECLS
>>> #define LANG_HOOKS_GETDECLS a68_getdecls
>>>
>>> +/* Return -1 if dwarf ATTR shouldn't be added for DECL, or the attribute
>>> + value otherwise. */
>>> +int
>>> +a68_decl_dwarf_attribute (const_tree decl, int attr)
>>> +{
>>> + if (decl == NULL_TREE)
>>> + return -1;
>>> +
>>> + if (attr == DW_AT_const_value
>>> + && VAR_P (decl)
>>> + && !a68_decl_is_var (decl))
>>> + return 1;
>>> +
>>> + return -1;
>>> +}
>>> +
>>> +#undef LANG_HOOKS_DECL_DWARF_ATTRIBUTE
>>> +#define LANG_HOOKS_DECL_DWARF_ATTRIBUTE a68_decl_dwarf_attribute
>>> +
>>> /* Return A68_GLOBAL_CONTEXT, but create it first if need be. */
>>>
>>> static tree
>>> diff --git a/gcc/algol68/a68-low-moids.cc b/gcc/algol68/a68-low-moids.cc
>>> index ddd87671190..1ee786f401e 100644
>>> --- a/gcc/algol68/a68-low-moids.cc
>>> +++ b/gcc/algol68/a68-low-moids.cc
>>> @@ -132,7 +132,7 @@ chain_struct_fields (PACK_T *fields, tree context)
>>>
>>> /* Associate the tree field declaration and the front end node. */
>>> DECL_LANG_SPECIFIC (field_decl) =
>>> - (NODE (field_mode) ? a68_build_lang_decl (NODE (field_mode)) : NULL);
>>> + (NODE (field_mode) ? a68_build_lang_decl (NODE (field_mode), false /* is_var */) : NULL);
>>>
>>> /* Chain the field declaration in its containing context. */
>>> DECL_FIELD_CONTEXT (field_decl) = context;
>>> diff --git a/gcc/algol68/a68-low.cc b/gcc/algol68/a68-low.cc
>>> index 8fc112cd76f..99b25a32cea 100644
>>> --- a/gcc/algol68/a68-low.cc
>>> +++ b/gcc/algol68/a68-low.cc
>>> @@ -438,6 +438,7 @@ a68_make_anonymous_routine_decl (MOID_T *mode)
>>> FUNCTION_DECL,
>>> NULL_TREE /* name, set below. */,
>>> func_type);
>>> +
>>> char *name = xasprintf ("routine%d", DECL_UID (func_decl));
>>> DECL_NAME (func_decl) = a68_get_mangled_identifier (name);
>>> free (name);
>>> @@ -470,6 +471,9 @@ a68_make_proc_identity_declaration_decl (NODE_T *identifier,
>>> NULL_TREE, /* name, set below. */
>>> func_type);
>>>
>>> + DECL_LANG_SPECIFIC (func_decl)
>>> + = a68_build_lang_decl (identifier, false);
>>> +
>>> if (public_range)
>>> {
>>> bool publicized = PUBLICIZED (TAX (identifier));
>>> @@ -533,6 +537,9 @@ a68_make_identity_declaration_decl (NODE_T *identifier,
>>> NULL_TREE, /* name, set below. */
>>> type);
>>>
>>> + DECL_LANG_SPECIFIC (decl)
>>> + = a68_build_lang_decl (identifier, false /* is_var */);
>>> +
>>> if (public_range)
>>> {
>>> bool publicized = PUBLICIZED (TAX (identifier));
>>> @@ -593,6 +600,9 @@ a68_make_variable_declaration_decl (NODE_T *identifier,
>>> NULL_TREE, /* name, set below. */
>>> type);
>>>
>>> + DECL_LANG_SPECIFIC (decl)
>>> + = a68_build_lang_decl (identifier, true /* is_var */);
>>> +
>>> if (public_range)
>>> {
>>> bool publicized = PUBLICIZED (TAX (identifier));
>>> diff --git a/gcc/algol68/a68.h b/gcc/algol68/a68.h
>>> index 59f23e4e779..8fcbde97a75 100644
>>> --- a/gcc/algol68/a68.h
>>> +++ b/gcc/algol68/a68.h
>>> @@ -301,8 +301,10 @@ uint32_t *a68_u8_to_u32 (const uint8_t *s, size_t n, uint32_t *resultbuf, size_t
>>> #define a68_word_real_type A68_GLOBAL_TREES[ATI_WORD_REAL_TYPE]
>>>
>>> struct lang_type *a68_build_lang_type (MOID_T *moid);
>>> -struct lang_decl *a68_build_lang_decl (NODE_T *node);
>>> +struct lang_decl *a68_build_lang_decl (NODE_T *node, bool is_var);
>>> MOID_T *a68_type_moid (tree type);
>>> +bool a68_decl_is_var (const_tree decl);
>>> +void a68_decl_set_is_var (tree decl, bool is_var);
>>>
>>> /* a68-diagnostics.cc */
>>>
>>> diff --git a/gcc/dwarf2out.cc b/gcc/dwarf2out.cc
>>> index d170e18c740..49d9778a858 100644
>>> --- a/gcc/dwarf2out.cc
>>> +++ b/gcc/dwarf2out.cc
>>> @@ -5655,6 +5655,16 @@ is_dlang (void)
>>> return lang == DW_LANG_D;
>>> }
>>>
>>> +/* Return TRUE if the language is Algol 68. */
>>> +
>>> +static inline bool
>>> +is_algol68 (void)
>>> +{
>>> + unsigned int lang = get_AT_unsigned (comp_unit_die (), DW_AT_language);
>>> +
>>> + return lang == DW_LANG_Algol68;
>>> +}
>>> +
>>> /* Remove the specified attribute if present. Return TRUE if removal
>>> was successful. */
>>>
>>> @@ -27728,7 +27738,19 @@ gen_decl_die (tree decl, tree origin, struct vlr_context *ctx,
>>> true /* Emit name attribute. */,
>>> context_die);
>>> else
>>> - gen_variable_die (decl, origin, context_die);
>>> + {
>>> + gen_variable_die (decl, origin, context_die);
>>> +
>>> + if (is_algol68 ()
>>> + && lang_hooks.decls.decl_dwarf_attribute (decl,
>>> + DW_AT_const_value) == 1)
>>> + {
>>> + dw_die_ref die = lookup_decl_die (decl);
>>> + gcc_assert (die != NULL);
>>> + if (!get_AT (die, DW_AT_const_value))
>>> + add_AT_unsigned (die, DW_AT_const_value, 1);
>>> + }
>>> + }
>>> break;
>>>
>>> case FIELD_DECL:
More information about the Algol68
mailing list