From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-wm1-x32d.google.com (mail-wm1-x32d.google.com [IPv6:2a00:1450:4864:20::32d]) by sourceware.org (Postfix) with ESMTPS id 56EB7385E021 for ; Sat, 28 May 2022 15:34:11 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 56EB7385E021 Received: by mail-wm1-x32d.google.com with SMTP id 129-20020a1c0287000000b003974edd7c56so2700943wmc.2 for ; Sat, 28 May 2022 08:34:11 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:references:date:in-reply-to :message-id:user-agent:mime-version; bh=kyZ+Vl9iKBh0V14X8OM/oH9pQCzVbG+32xxDIvgthdY=; b=eEGQJ8eEJ48LFzTkMf8XzBETmGYhobSSFtjaxz2iVp3VnXS1V6IeAYmvgo9gFXrixI NnuE0GEO3jxFHAaZ9UxWY+QgwP7eUSVSTyE3G8/g49jl0/NZYj7P1GSS1XwUMiYlkKPK oXXZ2eXfnp+ADhgsyShjdlaMs7S8yzCZQd4Lkkr3NPiDljP2KWOPk6zkFThNknJafotT kXCbUcwxQDc5Ll8KT7sxy5qVzyTrvWrwVwgIOGF7qaW56eOEE4MQZI3xp8ohyFITl9eb U/CbcdTRBuE20kHSJtlhspbU3kUCc9ihCkxvHEIj7rk6WWy9TGWvFygrE7vgZxra4KWk 54YA== X-Gm-Message-State: AOAM532kV6aaBtd+Vxyb4F7P+cqf9k830bTZ3xfTQRLWkCZN418pHodp hwDXZvFDw7LAJaXwsq8v3UB/NFZJYOo= X-Google-Smtp-Source: ABdhPJwqwZS721Ujstyxxdv/ECG0Dag5iVwglbcP6ky0T3TvSV2LOVpPM6HMe7e0lcqybUywffxo5A== X-Received: by 2002:a05:600c:4787:b0:397:4891:2c3a with SMTP id k7-20020a05600c478700b0039748912c3amr11959413wmo.37.1653752049792; Sat, 28 May 2022 08:34:09 -0700 (PDT) Received: from lancelot ([195.147.220.46]) by smtp.gmail.com with ESMTPSA id i5-20020adfefc5000000b0020d00174eabsm4402621wrp.94.2022.05.28.08.34.08 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 28 May 2022 08:34:09 -0700 (PDT) Received: from gaius by lancelot with local (Exim 4.94.2) (envelope-from ) id 1nuyS8-008N6X-4m; Sat, 28 May 2022 16:34:08 +0100 From: Gaius Mulley To: Richard Biener Cc: GCC Patches Subject: Re: [PATCH] Modula-2: merge proposal/review: 1/9 01.patch-set-01 References: <871qwnvgej.fsf@debian> <87k0abgciu.fsf@debian> <87ee0hmlxq.fsf@debian> Date: Sat, 28 May 2022 16:34:08 +0100 In-Reply-To: (Richard Biener's message of "Fri, 27 May 2022 09:40:54 +0200") Message-ID: <87wne51xjj.fsf@debian> User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/27.1 (gnu/linux) MIME-Version: 1.0 Content-Type: text/plain X-Spam-Status: No, score=-1.2 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_ENVFROM_END_DIGIT, FREEMAIL_FROM, KAM_NUMSUBJECT, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP, T_SCC_BODY_TEXT_LINE autolearn=no autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sat, 28 May 2022 15:34:14 -0000 Richard Biener writes: > On Wed, May 25, 2022 at 9:50 PM Gaius Mulley wrote: >> >> Richard Biener writes: >> >> > So is there a reason to have the 'scaffold' separate from the object >> > of hello.mod? >> >> Perhaps the major advantage is flexibility? But no we can by default >> produce the scaffold within the object of hello.mod (and give users the >> ability to disable scaffold generation should they wish to implement >> their own). >> >> > Is there more than a 1:1 relation between a .mod and the 'scaffold'? >> >> yes there is a 1:1 relation between a .mod and the scaffold. Although >> the caveat is that the compiler would need to parse every .def and .mod >> imports from the application universe. Not a major change as gm2 has >> the ability to do whole program (application) compiling, so a minor set >> of changes to ensure that upon compiling the program module that it also >> parses every .def/.mod. >> >> > Why are multiple tools involved here - can the m2 frontend not parse >> > imports, reorder runtime modules and generate the 'scaffold' as >> > GENERIC IL as part of the translation unit of the .mod file? >> > Indirection through emitting C++ source code makes the process a bit >> > awkward IMHO. >> >> indeed the m2 front end can parse imports, reorder and generate the >> scaffold. >> >> > Unfortunately I have no m2 installation around to look at how complex >> > the 'scaffold' is. >> >> the scaffold is really simple for example here it is for hello.mod: >> >> $ gm2 -c -g -fmakelist hello.mod >> $ cat hello.lst >> Storage >> SYSTEM >> M2RTS >> RTExceptions >> # libc 11 /home/gaius/opt/lib/gcc/x86_64-pc-linux-gnu/13.0.0/m2/m2pim/libc.def FOR 'C' >> # SYSTEM 11 /home/gaius/opt/lib/gcc/x86_64-pc-linux-gnu/13.0.0/m2/m2pim/SYSTEM.mod >> # StrIO 11 /home/gaius/opt/lib/gcc/x86_64-pc-linux-gnu/13.0.0/m2/m2pim/StrIO.mod >> # StrLib 10 /home/gaius/opt/lib/gcc/x86_64-pc-linux-gnu/13.0.0/m2/m2pim/StrLib.mod >> # ASCII 10 /home/gaius/opt/lib/gcc/x86_64-pc-linux-gnu/13.0.0/m2/m2pim/ASCII.mod >> # NumberIO 10 /home/gaius/opt/lib/gcc/x86_64-pc-linux-gnu/13.0.0/m2/m2pim/NumberIO.mod >> # Indexing 10 /home/gaius/opt/lib/gcc/x86_64-pc-linux-gnu/13.0.0/m2/m2pim/Indexing.mod >> # errno 9 /home/gaius/opt/lib/gcc/x86_64-pc-linux-gnu/13.0.0/m2/m2pim/errno.def >> # termios 9 /home/gaius/opt/lib/gcc/x86_64-pc-linux-gnu/13.0.0/m2/m2pim/termios.def >> # FIO 9 /home/gaius/opt/lib/gcc/x86_64-pc-linux-gnu/13.0.0/m2/m2pim/FIO.mod >> # IO 8 /home/gaius/opt/lib/gcc/x86_64-pc-linux-gnu/13.0.0/m2/m2pim/IO.mod >> # StdIO 7 /home/gaius/opt/lib/gcc/x86_64-pc-linux-gnu/13.0.0/m2/m2pim/StdIO.mod >> # Debug 6 /home/gaius/opt/lib/gcc/x86_64-pc-linux-gnu/13.0.0/m2/m2pim/Debug.mod >> # SysStorage 5 /home/gaius/opt/lib/gcc/x86_64-pc-linux-gnu/13.0.0/m2/m2pim/SysStorage.mod >> # SysExceptions 4 /home/gaius/opt/lib/gcc/x86_64-pc-linux-gnu/13.0.0/m2/m2pim/SysExceptions.def >> # M2EXCEPTION 4 /home/gaius/opt/lib/gcc/x86_64-pc-linux-gnu/13.0.0/m2/m2pim/M2EXCEPTION.mod >> # Storage 4 /home/gaius/opt/lib/gcc/x86_64-pc-linux-gnu/13.0.0/m2/m2pim/Storage.mod >> # RTExceptions 3 /home/gaius/opt/lib/gcc/x86_64-pc-linux-gnu/13.0.0/m2/m2pim/RTExceptions.mod >> # M2RTS 2 /home/gaius/opt/lib/gcc/x86_64-pc-linux-gnu/13.0.0/m2/m2pim/M2RTS.mod >> # hello 1 hello.mod >> # >> # Initialization order >> # >> StrIO >> StrLib >> ASCII >> NumberIO >> Indexing >> errno >> termios >> FIO >> IO >> StdIO >> Debug >> SysStorage >> SysExceptions >> M2EXCEPTION >> hello >> >> and now to generate the scaffold for a static application: >> >> $ ~/opt/bin/gm2 -fmakeinit -c -g hello.mod >> $ cat hello_m2.cpp >> extern "C" void exit(int); >> >> extern "C" void RTExceptions_DefaultErrorCatch(void); >> extern "C" void _M2_Storage_init (int argc, char *argv[]); >> extern "C" void _M2_Storage_finish (void); >> extern "C" void _M2_SYSTEM_init (int argc, char *argv[]); >> extern "C" void _M2_SYSTEM_finish (void); >> extern "C" void _M2_M2RTS_init (int argc, char *argv[]); >> extern "C" void _M2_M2RTS_finish (void); >> extern "C" void _M2_RTExceptions_init (int argc, char *argv[]); >> extern "C" void _M2_RTExceptions_finish (void); >> extern "C" void _M2_StrIO_init (int argc, char *argv[]); >> extern "C" void _M2_StrIO_finish (void); >> extern "C" void _M2_StrLib_init (int argc, char *argv[]); >> extern "C" void _M2_StrLib_finish (void); >> extern "C" void _M2_ASCII_init (int argc, char *argv[]); >> extern "C" void _M2_ASCII_finish (void); >> extern "C" void _M2_NumberIO_init (int argc, char *argv[]); >> extern "C" void _M2_NumberIO_finish (void); >> extern "C" void _M2_Indexing_init (int argc, char *argv[]); >> extern "C" void _M2_Indexing_finish (void); >> extern "C" void _M2_errno_init (int argc, char *argv[]); >> extern "C" void _M2_errno_finish (void); >> extern "C" void _M2_termios_init (int argc, char *argv[]); >> extern "C" void _M2_termios_finish (void); >> extern "C" void _M2_FIO_init (int argc, char *argv[]); >> extern "C" void _M2_FIO_finish (void); >> extern "C" void _M2_IO_init (int argc, char *argv[]); >> extern "C" void _M2_IO_finish (void); >> extern "C" void _M2_StdIO_init (int argc, char *argv[]); >> extern "C" void _M2_StdIO_finish (void); >> extern "C" void _M2_Debug_init (int argc, char *argv[]); >> extern "C" void _M2_Debug_finish (void); >> extern "C" void _M2_SysStorage_init (int argc, char *argv[]); >> extern "C" void _M2_SysStorage_finish (void); >> extern "C" void _M2_SysExceptions_init (int argc, char *argv[]); >> extern "C" void _M2_SysExceptions_finish (void); >> extern "C" void _M2_M2EXCEPTION_init (int argc, char *argv[]); >> extern "C" void _M2_M2EXCEPTION_finish (void); >> extern "C" void _M2_hello_init (int argc, char *argv[]); >> extern "C" void _M2_hello_finish (void); >> >> extern "C" void M2RTS_ExecuteTerminationProcedures(void); >> >> extern "C" void M2RTS_ExecuteInitialProcedures(void); >> >> static void init (int argc, char *argv[]) >> { >> try { >> _M2_Storage_init (argc, argv); >> _M2_SYSTEM_init (argc, argv); >> _M2_M2RTS_init (argc, argv); >> _M2_RTExceptions_init (argc, argv); >> _M2_StrIO_init (argc, argv); >> _M2_StrLib_init (argc, argv); >> _M2_ASCII_init (argc, argv); >> _M2_NumberIO_init (argc, argv); >> _M2_Indexing_init (argc, argv); >> _M2_errno_init (argc, argv); >> _M2_termios_init (argc, argv); >> _M2_FIO_init (argc, argv); >> _M2_IO_init (argc, argv); >> _M2_StdIO_init (argc, argv); >> _M2_Debug_init (argc, argv); >> _M2_SysStorage_init (argc, argv); >> _M2_SysExceptions_init (argc, argv); >> _M2_M2EXCEPTION_init (argc, argv); >> M2RTS_ExecuteInitialProcedures (); >> _M2_hello_init (argc, argv); >> } >> catch (...) { >> RTExceptions_DefaultErrorCatch(); >> } >> } >> >> static void finish (void) >> { >> try { >> M2RTS_ExecuteTerminationProcedures (); >> _M2_hello_finish (); >> _M2_M2EXCEPTION_finish (); >> _M2_SysExceptions_finish (); >> _M2_SysStorage_finish (); >> _M2_Debug_finish (); >> _M2_StdIO_finish (); >> _M2_IO_finish (); >> _M2_FIO_finish (); >> _M2_termios_finish (); >> _M2_errno_finish (); >> _M2_Indexing_finish (); >> _M2_NumberIO_finish (); >> _M2_ASCII_finish (); >> _M2_StrLib_finish (); >> _M2_StrIO_finish (); >> _M2_RTExceptions_finish (); >> _M2_M2RTS_finish (); >> _M2_SYSTEM_finish (); >> _M2_Storage_finish (); >> exit (0); >> } >> catch (...) { >> RTExceptions_DefaultErrorCatch(); >> } >> } >> >> int main (int argc, char *argv[]) >> { >> init (argc, argv); >> finish (); >> return (0); >> } >> >> >> or similarly for a shared library: >> >> $ ~/opt/bin/gm2 -fshared -fmakeinit -c -g hello.mod >> $ cat hello_m2.cpp >> extern "C" void exit(int); >> >> extern "C" void RTExceptions_DefaultErrorCatch(void); >> extern "C" void _M2_Storage_init (int argc, char *argv[]); >> extern "C" void _M2_Storage_finish (void); >> extern "C" void _M2_SYSTEM_init (int argc, char *argv[]); >> extern "C" void _M2_SYSTEM_finish (void); >> extern "C" void _M2_M2RTS_init (int argc, char *argv[]); >> extern "C" void _M2_M2RTS_finish (void); >> extern "C" void _M2_RTExceptions_init (int argc, char *argv[]); >> extern "C" void _M2_RTExceptions_finish (void); >> extern "C" void _M2_StrIO_init (int argc, char *argv[]); >> extern "C" void _M2_StrIO_finish (void); >> extern "C" void _M2_StrLib_init (int argc, char *argv[]); >> extern "C" void _M2_StrLib_finish (void); >> extern "C" void _M2_ASCII_init (int argc, char *argv[]); >> extern "C" void _M2_ASCII_finish (void); >> extern "C" void _M2_NumberIO_init (int argc, char *argv[]); >> extern "C" void _M2_NumberIO_finish (void); >> extern "C" void _M2_Indexing_init (int argc, char *argv[]); >> extern "C" void _M2_Indexing_finish (void); >> extern "C" void _M2_errno_init (int argc, char *argv[]); >> extern "C" void _M2_errno_finish (void); >> extern "C" void _M2_termios_init (int argc, char *argv[]); >> extern "C" void _M2_termios_finish (void); >> extern "C" void _M2_FIO_init (int argc, char *argv[]); >> extern "C" void _M2_FIO_finish (void); >> extern "C" void _M2_IO_init (int argc, char *argv[]); >> extern "C" void _M2_IO_finish (void); >> extern "C" void _M2_StdIO_init (int argc, char *argv[]); >> extern "C" void _M2_StdIO_finish (void); >> extern "C" void _M2_Debug_init (int argc, char *argv[]); >> extern "C" void _M2_Debug_finish (void); >> extern "C" void _M2_SysStorage_init (int argc, char *argv[]); >> extern "C" void _M2_SysStorage_finish (void); >> extern "C" void _M2_SysExceptions_init (int argc, char *argv[]); >> extern "C" void _M2_SysExceptions_finish (void); >> extern "C" void _M2_M2EXCEPTION_init (int argc, char *argv[]); >> extern "C" void _M2_M2EXCEPTION_finish (void); >> extern "C" void _M2_hello_init (int argc, char *argv[]); >> extern "C" void _M2_hello_finish (void); >> >> extern "C" void M2RTS_ExecuteTerminationProcedures(void); >> >> extern "C" void M2RTS_ExecuteInitialProcedures(void); >> >> static void init (int argc, char *argv[]) >> { >> try { >> _M2_Storage_init (argc, argv); >> _M2_SYSTEM_init (argc, argv); >> _M2_M2RTS_init (argc, argv); >> _M2_RTExceptions_init (argc, argv); >> _M2_StrIO_init (argc, argv); >> _M2_StrLib_init (argc, argv); >> _M2_ASCII_init (argc, argv); >> _M2_NumberIO_init (argc, argv); >> _M2_Indexing_init (argc, argv); >> _M2_errno_init (argc, argv); >> _M2_termios_init (argc, argv); >> _M2_FIO_init (argc, argv); >> _M2_IO_init (argc, argv); >> _M2_StdIO_init (argc, argv); >> _M2_Debug_init (argc, argv); >> _M2_SysStorage_init (argc, argv); >> _M2_SysExceptions_init (argc, argv); >> _M2_M2EXCEPTION_init (argc, argv); >> M2RTS_ExecuteInitialProcedures (); >> _M2_hello_init (argc, argv); >> } >> catch (...) { >> RTExceptions_DefaultErrorCatch(); >> } >> } >> >> static void finish (void) >> { >> try { >> M2RTS_ExecuteTerminationProcedures (); >> _M2_hello_finish (); >> _M2_M2EXCEPTION_finish (); >> _M2_SysExceptions_finish (); >> _M2_SysStorage_finish (); >> _M2_Debug_finish (); >> _M2_StdIO_finish (); >> _M2_IO_finish (); >> _M2_FIO_finish (); >> _M2_termios_finish (); >> _M2_errno_finish (); >> _M2_Indexing_finish (); >> _M2_NumberIO_finish (); >> _M2_ASCII_finish (); >> _M2_StrLib_finish (); >> _M2_StrIO_finish (); >> _M2_RTExceptions_finish (); >> _M2_M2RTS_finish (); >> _M2_SYSTEM_finish (); >> _M2_Storage_finish (); >> exit (0); >> } >> catch (...) { >> RTExceptions_DefaultErrorCatch(); >> } >> } >> >> int main (int argc, char *argv[]) >> { >> init (argc, argv); >> finish (); >> return (0); >> } > > So seeing the above the scaffold is only generated for the main program > translation unit - I suppose an application can consist of more than one > translation unit. yes indeed. There are four module kinds in Modula-2 - the definition module, implementation module and program module. There are inner modules (but for linking purposes we can ignore these). The definition module which consists of external prototypes, types, consts, global variables (but no code) - typically called foo.def. The implementation module (foo.mod) must implement everything defined in the corresponding definition module. The program module (bar.mod) is the application. The expected route is for the application module (program module) to be the module which is linked - but interestingly this does not have to be enforced and you are allowed to link an implementation module as the application. > And the main TU compilation (recursively) will import > all other TUs (but they are compiled separately?). yes the *.mod files can be compiled separately. The *.def modules are parsed when required aka .h files. > Is there a well-defined order the module initialization has to happen > across a program? yes in ISO m2 it is specified. The last paragraph is the most crucial. "The initialization order shall be determined by first processing the import lists of the program module. The import list of the program shall be processed by processing the import lists of each separate module whose identifier appears in the import lists of the program module, in order of occurrence in those import lists. If an import list is an unqualified import, the import list shall be processed by processing the import lists of the separate module whose identifier appears in the import list. If an import list is a simple import, the import list shall be processed by processing in turn the import lists of each separate module whose identifier appears in the import list identifiers. If the import lists of a separate module have finished being processed, or have started to be processed, the import lists of that module shall not be processed again. Otherwise, the import lists of the definition module shall be processed, and then the import lists of the implementation module shall be processed. The initialization order shall be the order in which the corresponding separate modules finish the processing of their import lists." > I'm thinking of each m2 TU adding to a global initialization / finalization > vector thus adding > > static initvec[] __attribute__((section("m2init"))) = { _M2_..._init, > _M2_..._init, ... }; > static finivec[] __attribute__((section("m2fini"))) = { > _M2_..._finish, __M2_..._finish, ...}; yes this looks good, very neat. There already exist a M2RTS pair of procedures which are created for the init/finish section of the application which can process these vectors. > and the main program just calling into the m2 runtime with the address > of the sections which the would apply runtime sorting to weed out > duplicates (or have the individual _init/_finish protect against > multiple invocations). That's of course harder if the > initialization/finalization order is well-defined across the import > graph (or tree). yes and the compiler can estabish the order using a combination of topological sort and user defined ordering. > Of course the module objects could also emit meta-data so the runtime > can replicate the import graph. Not sure if optimizing the compile or > the runtime is more important here. Yes it would be very easy to add the complete import graph. I guess it might be necessary for close source users. Replicating the import graph allows for easy runtime satisfying of the ISO initialization sequence. I think all this is great (when memory is inexpensive). We also need to allow users to turn all this off - really trivial by comparison - I'm remembering the tiny address space targets (separate I/D spaces of 1k :-). The IR GENERIC scaffold mentioned in the previous email should also be useful for tiny memory targets. >> yes indeed all these programs could be placed into the front end, >> producing an IR GENERIC scaffold by default > > It does look quite managable to create the above indeed. You might want > to look into the C++ frontend creating global variable > initialization/finalization functions > for a TU like > > struct X { X(); ~X(); } x; yes this looks very useful and clean. I've taken the liberty of creating a devel/m2link branch (branched from devel/modula-2) where development of this new linking mechanism can take place. I think it might be worth implementing three different mechanisms: (i) generation of source code scaffold. gm2 -c -fno-m2link-ir bar.mod generates the scaffold bar_m2.cpp or bar_m2.c and bar.o without any extra scaffold code in bar.o. The user compiles and performs the link with gcc/g++/gm2 and a set of objects/libraries and the scaffold. gcc -c bar_m2.c or g++ -c bar_m2.cpp (if C++ is available for the target). gm2 bar.o bar_m2.o -lm2min Primarily for any legacy code and tiny embedded targets. (ii) generation of scaffold as IR in program module.o. At least two advantages: easy for the user to debug their code and it doesn't require a C++ runtime on the target. gm2 -fm2link-ir bar.mod (iii) the scaffold via struct X { X(); ~X(); } x; pushing function addresses, complete import graph replication. Perhaps allowing users to override the order (-fuselist) which is embedded as static initvec[] __attribute__((section("m2init"))) = { _M2_..._init, _M2_..._init, ... };. If absent it deduces the sequence from the graph. gm2 bar.mod (i) is really a non implementation and (ii) and (iii) are minor variants of each other. regards, Gaius