diff mbox series

modula-2: Fixes for preprocessing [PR102343, PR108182]

Message ID 20230125084629.55372-1-iain@sandoe.co.uk
State New
Headers show
Series modula-2: Fixes for preprocessing [PR102343, PR108182] | expand

Commit Message

Iain Sandoe Jan. 25, 2023, 8:46 a.m. UTC
Tested on x86_64, powerpc64 - linux-gnu (with 32b multilibs),
i686, powerpc darwin (with 64b multilibs) x86_64 darwin (with and without
32b multilib).
OK for trunk?
thanks
Iain

--- 8< ---

Modula-2 uses the C preprocessor to implement handling for conditional
code and macros.  However, this is not done directly, because the process
is applied recursively to imported definitions and modules.

The cc1gm2 executable records the parameters as a template command line
needed to create a composite 'cc1 -E' for each file to be preprocessed
starting with the main file from the original command line.

This patch fixes the capture of the C preprocessor template to include
the target information needed for correct multilib operation.

In order to match the existing semantics of '-E, -M and -MM' these have
to be handled as a 'pre-processor only' job (i.e. the recursion is omitted
and only the main file is processed).

Whereas C-family front ends always pre-process, Modula-2 only does so
when specifically requested (via the -fcpp option).

'-MD, -MMD and -MQ' also require special handling, since (in principle)
these options can be applied to any command line (with -fcpp) providing
dependency information as a by-product.

TODO: the preprocessor is not able to determine def and mod dependencies
for Modula-2 and so the output of this only shows the object to module
dep.  We should be able to append the .def and .mod dependencies.

The patch amends save-temps handling to cater for the preprocessor
recursion and to avoid writing saved files into the source directories.

The patch changes the extension for Modula-2 preprocessed source to .m2i
to avoid clashes with .i.

The main driver code is amended to add default handlers for .mod and .m2i
so that a useful error message will be emitted if the Modula-2 compiler
is not built-in.

The compiler will now also handle code generation from a .m2i preprocessed
source.

TODO: We should not need to pass the '-c' option to the compiler to alter
the processing of init code.

Signed-off-by: Iain Sandoe <iain@sandoe.co.uk>

	PR modula2/102343
	PR modula2/108182

gcc/ChangeLog:

	* gcc.cc: Provide default specs for Modula-2 so that when the
	language is not built-in better diagnostics are emitted for
	attempts to use .mod or .m2i file extensions.

gcc/m2/ChangeLog:

	* gm2-compiler/M2Comp.mod: Early exit for pre-processor-only jobs.
	* gm2-compiler/M2Options.def (SetPPOnly, GetPPOnly, SetMD, GetMD,
	SetMMD, GetMMD, SetMQ, GetMQ, SetObj, GetObj, SetDumpDir,
	GetDumpDir):New.
	* gm2-compiler/M2Options.mod:(SetPPOnly, GetPPOnly, SetMD, GetMD,
	SetMMD, GetMMD, SetMQ, GetMQ, SetObj, GetObj, SetDumpDir,
	GetDumpDir):New.
	* gm2-compiler/M2Preprocess.def (PreprocessModule): Add flag to
	indicate the main file.
	* gm2-compiler/M2Preprocess.mod: Handle Preprocess-only jobs,
	handle MD, MMD and MQ options.
	* gm2-gcc/m2options.h (M2Options_SetPPOnly, M2Options_GetPPOnly,
	M2Options_SetDumpDir, M2Options_SetMD, M2Options_GetMD,
	M2Options_SetMMD, M2Options_GetMMD, M2Options_SetMQ, M2Options_GetMQ,
	M2Options_SetObj, M2Options_GetObj): New.
	* gm2-gcc/m2type.cc (m2type_InitBaseTypes): Early exit for pre-
	processor-only jobs.
	* gm2-lang.cc (gm2_langhook_init): Handle preprocess-only commands.
	(gm2_langhook_option_lang_mask): Claim C and Driver options so that
	we can intercept them for building pre-processor commands.
	(gm2_langhook_init_options): Collect the preprocessor line here.
	Save options that have different actions for preprocessor and compile
	commands.
	(gm2_langhook_handle_option): Only handle the modula-2 options here.
	(gm2_langhook_post_options): Do not create a back-end for pre-
	processor-only jobs.
	* gm2spec.cc (lang_specific_driver): Ignore PCH options, append a
	scaffold-main for cases where we are building a main module with
	-c.
	* lang-specs.h: Revise to handle preprocessor-only jobs and to
	consume pre-processed files.
	* lang.opt: Remove Driver and C options copies (we claim these
	separately).
---
 gcc/gcc.cc                           |   1 +
 gcc/m2/gm2-compiler/M2Comp.mod       |  38 +++--
 gcc/m2/gm2-compiler/M2Options.def    |  90 +++++++++-
 gcc/m2/gm2-compiler/M2Options.mod    | 139 +++++++++++++++-
 gcc/m2/gm2-compiler/M2Preprocess.def |   2 +-
 gcc/m2/gm2-compiler/M2Preprocess.mod | 130 +++++++++++++--
 gcc/m2/gm2-gcc/m2options.h           |  11 ++
 gcc/m2/gm2-gcc/m2type.cc             |   4 +
 gcc/m2/gm2-lang.cc                   | 237 ++++++++++++++++++++-------
 gcc/m2/gm2spec.cc                    |  33 +++-
 gcc/m2/lang-specs.h                  |  40 +++--
 gcc/m2/lang.opt                      | 119 +-------------
 12 files changed, 619 insertions(+), 225 deletions(-)

Comments

Gaius Mulley Jan. 25, 2023, 12:03 p.m. UTC | #1
Iain Sandoe <iains.gcc@gmail.com> writes:

> Tested on x86_64, powerpc64 - linux-gnu (with 32b multilibs),
> i686, powerpc darwin (with 64b multilibs) x86_64 darwin (with and without
> 32b multilib).
> OK for trunk?
> thanks
> Iain
>
> --- 8< ---
>
> Modula-2 uses the C preprocessor to implement handling for conditional
> code and macros.  However, this is not done directly, because the process
> is applied recursively to imported definitions and modules.
>
> The cc1gm2 executable records the parameters as a template command line
> needed to create a composite 'cc1 -E' for each file to be preprocessed
> starting with the main file from the original command line.
>
> This patch fixes the capture of the C preprocessor template to include
> the target information needed for correct multilib operation.
>
> In order to match the existing semantics of '-E, -M and -MM' these have
> to be handled as a 'pre-processor only' job (i.e. the recursion is omitted
> and only the main file is processed).
>
> Whereas C-family front ends always pre-process, Modula-2 only does so
> when specifically requested (via the -fcpp option).
>
> '-MD, -MMD and -MQ' also require special handling, since (in principle)
> these options can be applied to any command line (with -fcpp) providing
> dependency information as a by-product.
>
> TODO: the preprocessor is not able to determine def and mod dependencies
> for Modula-2 and so the output of this only shows the object to module
> dep.  We should be able to append the .def and .mod dependencies.
>
> The patch amends save-temps handling to cater for the preprocessor
> recursion and to avoid writing saved files into the source directories.
>
> The patch changes the extension for Modula-2 preprocessed source to .m2i
> to avoid clashes with .i.
>
> The main driver code is amended to add default handlers for .mod and .m2i
> so that a useful error message will be emitted if the Modula-2 compiler
> is not built-in.
>
> The compiler will now also handle code generation from a .m2i preprocessed
> source.
>
> TODO: We should not need to pass the '-c' option to the compiler to alter
> the processing of init code.
>
> Signed-off-by: Iain Sandoe <iain@sandoe.co.uk>
>
> 	PR modula2/102343
> 	PR modula2/108182
>
> gcc/ChangeLog:
>
> 	* gcc.cc: Provide default specs for Modula-2 so that when the
> 	language is not built-in better diagnostics are emitted for
> 	attempts to use .mod or .m2i file extensions.
>
> gcc/m2/ChangeLog:
>
> 	* gm2-compiler/M2Comp.mod: Early exit for pre-processor-only jobs.
> 	* gm2-compiler/M2Options.def (SetPPOnly, GetPPOnly, SetMD, GetMD,
> 	SetMMD, GetMMD, SetMQ, GetMQ, SetObj, GetObj, SetDumpDir,
> 	GetDumpDir):New.
> 	* gm2-compiler/M2Options.mod:(SetPPOnly, GetPPOnly, SetMD, GetMD,
> 	SetMMD, GetMMD, SetMQ, GetMQ, SetObj, GetObj, SetDumpDir,
> 	GetDumpDir):New.
> 	* gm2-compiler/M2Preprocess.def (PreprocessModule): Add flag to
> 	indicate the main file.
> 	* gm2-compiler/M2Preprocess.mod: Handle Preprocess-only jobs,
> 	handle MD, MMD and MQ options.
> 	* gm2-gcc/m2options.h (M2Options_SetPPOnly, M2Options_GetPPOnly,
> 	M2Options_SetDumpDir, M2Options_SetMD, M2Options_GetMD,
> 	M2Options_SetMMD, M2Options_GetMMD, M2Options_SetMQ, M2Options_GetMQ,
> 	M2Options_SetObj, M2Options_GetObj): New.
> 	* gm2-gcc/m2type.cc (m2type_InitBaseTypes): Early exit for pre-
> 	processor-only jobs.
> 	* gm2-lang.cc (gm2_langhook_init): Handle preprocess-only commands.
> 	(gm2_langhook_option_lang_mask): Claim C and Driver options so that
> 	we can intercept them for building pre-processor commands.
> 	(gm2_langhook_init_options): Collect the preprocessor line here.
> 	Save options that have different actions for preprocessor and compile
> 	commands.
> 	(gm2_langhook_handle_option): Only handle the modula-2 options here.
> 	(gm2_langhook_post_options): Do not create a back-end for pre-
> 	processor-only jobs.
> 	* gm2spec.cc (lang_specific_driver): Ignore PCH options, append a
> 	scaffold-main for cases where we are building a main module with
> 	-c.
> 	* lang-specs.h: Revise to handle preprocessor-only jobs and to
> 	consume pre-processed files.
> 	* lang.opt: Remove Driver and C options copies (we claim these
> 	separately).


Hi Iain,

yes LGTM - thanks for the fixes and it is great to have the ability for
gm2 to handle preprocessed source

regards,
Gaius
diff mbox series

Patch

diff --git a/gcc/gcc.cc b/gcc/gcc.cc
index d629ca5e424..d8130024a8c 100644
--- a/gcc/gcc.cc
+++ b/gcc/gcc.cc
@@ -1423,6 +1423,7 @@  static const struct compiler default_compilers[] =
   {".r", "#Ratfor", 0, 0, 0},
   {".go", "#Go", 0, 1, 0},
   {".d", "#D", 0, 1, 0}, {".dd", "#D", 0, 1, 0}, {".di", "#D", 0, 1, 0},
+  {".mod", "#Modula-2", 0, 0, 0}, {".m2i", "#Modula-2", 0, 0, 0},
   /* Next come the entries for C.  */
   {".c", "@c", 0, 0, 1},
   {"@c",
diff --git a/gcc/m2/gm2-compiler/M2Comp.mod b/gcc/m2/gm2-compiler/M2Comp.mod
index fd5ea1b379e..05eaaccb554 100644
--- a/gcc/m2/gm2-compiler/M2Comp.mod
+++ b/gcc/m2/gm2-compiler/M2Comp.mod
@@ -22,7 +22,8 @@  along with GNU Modula-2; see the file COPYING3.  If not see
 IMPLEMENTATION MODULE M2Comp ;
 
 
-FROM M2Options IMPORT Statistics, Quiet, WholeProgram, ExtendedOpaque, GenModuleList ;
+FROM M2Options IMPORT PPonly, Statistics, Quiet, WholeProgram,
+                      ExtendedOpaque, GenModuleList ;
 
 FROM M2Pass IMPORT SetPassToPass0, SetPassToPass1, SetPassToPass2, SetPassToPassC, SetPassToPass3,
                    SetPassToNoPass, SetPassToPassHidden ;
@@ -60,11 +61,12 @@  FROM SymbolTable IMPORT GetSymName, IsDefImp, NulSym,
                         ResolveConstructorTypes, SanityCheckConstants, IsDefinitionForC,
                         IsBuiltinInModule, PutModLink, IsDefLink, IsModLink ;
 
-FROM FIO IMPORT StdErr ;
+FROM FIO IMPORT StdErr, StdOut ;
 FROM NameKey IMPORT Name, GetKey, KeyToCharStar, makekey ;
 FROM M2Printf IMPORT fprintf1 ;
 FROM M2Quiet IMPORT qprintf0, qprintf1, qprintf2 ;
 FROM DynamicStrings IMPORT String, InitString, KillString, InitStringCharStar, Dup, Mark, string ;
+FROM M2Options IMPORT Verbose ;
 
 CONST
    Debugging = FALSE ;
@@ -126,6 +128,10 @@  PROCEDURE Compile (s: String) ;
 BEGIN
    DoPass0(s) ;
    FlushWarnings ; FlushErrors ;
+   IF PPonly
+   THEN
+      RETURN
+   END;
    ResetForNewPass ; ResetErrorScope ;
    qprintf0('Pass 1: scopes, enumerated types, imports and exports\n') ;
    DoPass1 ;
@@ -198,7 +204,7 @@  VAR
    name    : ADDRESS ;
    isdefimp: BOOLEAN ;
 BEGIN
-   IF OpenSource(PreprocessModule(s))
+   IF OpenSource(s)
    THEN
       ExamineCompilationUnit(name, isdefimp) ;
       IF isdefimp
@@ -226,15 +232,26 @@  VAR
    Sym     : CARDINAL ;
    i       : CARDINAL ;
    SymName,
-   FileName: String ;
+   FileName,
+   PPSource: String ;
 BEGIN
    P0Init ;
    SetPassToPass0 ;
-   PeepInto(s) ;
+   (* Maybe preprocess the main file.  *)
+   PPSource := PreprocessModule(s, TRUE);
+   IF PPonly
+   THEN
+      RETURN
+   END;
+   PeepInto (PPSource) ;
    Main := GetMainModule() ;
    i := 1 ;
    Sym := GetModuleNo(i) ;
-   qprintf1('Compiling: %s\n', s) ;
+   qprintf1('Compiling: %s\n', PPSource) ;
+   IF Verbose
+   THEN
+      fprintf1(StdOut, 'Compiling: %s\n', PPSource) ;
+   END ;
    qprintf0('Pass 0: lexical analysis, parsing, modules and associated filenames\n') ;
    WHILE Sym#NulSym DO
       SymName := InitStringCharStar(KeyToCharStar(GetSymName(Sym))) ;
@@ -243,7 +260,7 @@  BEGIN
          IF FindSourceDefFile(SymName, FileName)
          THEN
             ModuleType := Definition ;
-            IF OpenSource(AssociateDefinition(PreprocessModule(FileName), Sym))
+            IF OpenSource(AssociateDefinition(PreprocessModule(FileName, FALSE), Sym))
             THEN
                IF NOT P0SyntaxCheck.CompilationUnit()
                THEN
@@ -280,15 +297,16 @@  BEGIN
          (* only need to read implementation module if hidden types are declared or it is the main module *)
          IF Main=Sym
          THEN
-            FileName := Dup(s)
+            FileName := Dup (PPSource)
          ELSE
             IF FindSourceModFile (SymName, FileName)
             THEN
+               FileName := PreprocessModule (FileName, FALSE)
             END
          END ;
          IF FileName#NIL
          THEN
-            IF OpenSource (AssociateModule (PreprocessModule (FileName), Sym))
+            IF OpenSource (AssociateModule (Dup (FileName), Sym))
             THEN
                IF NOT P0SyntaxCheck.CompilationUnit()
                THEN
@@ -325,7 +343,7 @@  BEGIN
             IF FindSourceModFile (SymName, FileName)
             THEN
                qprintf2 ('   Module %-20s : %s (linking)\n', SymName, FileName) ;
-               IF OpenSource (AssociateModule (PreprocessModule (FileName), Sym))
+               IF OpenSource (AssociateModule (PreprocessModule (FileName, FALSE), Sym))
                THEN
                   PutModLink (Sym, TRUE) ;   (* This source is only used to determine link time info.  *)
                   IF NOT P0SyntaxCheck.CompilationUnit ()
diff --git a/gcc/m2/gm2-compiler/M2Options.def b/gcc/m2/gm2-compiler/M2Options.def
index e7b34cf2903..df42a4a5b65 100644
--- a/gcc/m2/gm2-compiler/M2Options.def
+++ b/gcc/m2/gm2-compiler/M2Options.def
@@ -52,11 +52,12 @@  EXPORT QUALIFIED SetReturnCheck, SetNilCheck, SetCaseCheck,
 		 SetWholeValueCheck, GetWholeValueCheck,
                  SetLowerCaseKeywords,
                  SetIndex, SetRange, SetWholeDiv, SetStrictTypeChecking,
-                 Setc, Getc, SetUselist, GetUselist, GetUselistFilename,
-                 SetShared, SetB,
+                 Setc, Getc, SetPPOnly, GetPPOnly,
+                 SetUselist, GetUselist, GetUselistFilename,
+                 SetShared,
 
                  Iso, Pim, Pim2, Pim3, Pim4,
-                 cflag,
+                 PPonly, cflag,
                  PositiveModFloorDiv,
                  Pedantic, Verbose, Statistics,
                  UnboundedByReference, VerboseUnbounded,
@@ -83,7 +84,7 @@  EXPORT QUALIFIED SetReturnCheck, SetNilCheck, SetCaseCheck,
                  DebugBuiltins, setdefextension, setmodextension,
                  SetStatistics, SetWall,
                  SetSaveTemps, SetSaveTempsDir, SaveTemps, GetSaveTempsDir,
-                 GenModuleList,
+                 SetDumpDir, GetDumpDir, GenModuleList,
                  CppArg, CppCommandLine, CppRemember,
 		 SetDebugFunctionLineNumbers, DebugFunctionLineNumbers,
 		 SetGenerateStatementNote, GenerateStatementNote,
@@ -92,10 +93,11 @@  EXPORT QUALIFIED SetReturnCheck, SetNilCheck, SetCaseCheck,
                  SetScaffoldMain, ScaffoldMain,
                  SetRuntimeModuleOverride, GetRuntimeModuleOverride,
                  SetGenModuleList, GetGenModuleFilename, SharedFlag,
-                 GetB ;
+                 SetB, GetB, SetMD, GetMD, SetMMD, GetMMD, SetObj, GetObj ;
 
 
 VAR
+   PPonly,                       (* -E/M/MM present? - preprocessing only    *)
    cflag,                        (* -c flag present?                         *)
    Iso,                          (* -fiso use ISO SYSTEM.def                 *)
    Pim,                          (* -fpim use PIM [234] SYSTEM.def           *)
@@ -174,6 +176,18 @@  VAR
    Coding,
    Profiling               : BOOLEAN ;
 
+(*
+   SetPPOnly - set the PPonly to value (on E, M, MM).
+*)
+
+PROCEDURE SetPPOnly (value: BOOLEAN) ;
+
+
+(*
+   GetPPOnly - get the PPonly (Preprocess only).
+*)
+
+PROCEDURE GetPPOnly () : BOOLEAN ;
 
 (*
    Setc - set the cflag (compile only flag -c) to value.
@@ -195,13 +209,64 @@  PROCEDURE Getc () : BOOLEAN ;
 
 PROCEDURE SetB (arg: ADDRESS) ;
 
-
 (*
    GetB - returns argument to the -B option as a string or NIL if it were never set.
 *)
 
 PROCEDURE GetB () : ADDRESS ;
 
+(*
+   SetMD - assigns MD file to arg.
+*)
+
+PROCEDURE SetMD (arg: ADDRESS) ;
+
+(*
+   GetMD - returns the filename set for MD or NIL if it was never set.
+*)
+
+PROCEDURE GetMD () : ADDRESS ;
+
+
+(*
+   SetMMD - assigns MMD file to arg.
+*)
+
+PROCEDURE SetMMD (arg: ADDRESS) ;
+
+(*
+   GetMMD - returns the filename set for MMD or NIL if it was never set.
+*)
+
+PROCEDURE GetMMD () : ADDRESS ;
+
+(*
+   SetMQ - assigns MQ file to arg.
+*)
+
+PROCEDURE SetMQ (arg: ADDRESS) ;
+
+(*
+   GetMQ - returns the filename set for MQ or NIL if it was never set.
+*)
+
+PROCEDURE GetMQ () : ADDRESS ;
+
+(*
+   SetScaffoldDynamic - set the -fscaffold-dynamic flag.
+*)
+
+(*
+   SetObj - assigns given object file to arg.
+*)
+
+PROCEDURE SetObj (arg: ADDRESS) ;
+
+(*
+   GetObj - returns the filename set for Object or NIL if it was never set.
+*)
+
+PROCEDURE GetObj () : ADDRESS ;
 
 (*
    SetScaffoldDynamic - set the -fscaffold-dynamic flag.
@@ -784,6 +849,19 @@  PROCEDURE SetSaveTempsDir (arg: ADDRESS) ;
 
 PROCEDURE GetSaveTempsDir () : String ;
 
+(*
+   SetDumpDir - Specify dump dir.
+*)
+
+PROCEDURE SetDumpDir (arg: ADDRESS) ;
+
+
+(*
+   GetDumpDir - return DumpDir or NIL.
+*)
+
+PROCEDURE GetDumpDir () : String ;
+
 
 (*
    SetGenModuleList - set the GenModuleList flag to value and pass
diff --git a/gcc/m2/gm2-compiler/M2Options.mod b/gcc/m2/gm2-compiler/M2Options.mod
index 14e978a46ba..865b8578367 100644
--- a/gcc/m2/gm2-compiler/M2Options.mod
+++ b/gcc/m2/gm2-compiler/M2Options.mod
@@ -54,7 +54,12 @@  CONST
 
 VAR
    Barg,
+   MDarg,
+   MMDarg,
+   MQarg,
+   CmdLineObj,
    SaveTempsDir,
+   DumpDir,
    GenModuleListFilename,
    UselistFilename,
    RuntimeModuleOverride,
@@ -132,6 +137,94 @@  BEGIN
 END GetB ;
 
 
+(*
+   SetMD - assigns MDarg to the filename from arg.
+   This overrides any previous MMD.
+*)
+
+PROCEDURE SetMD (arg: ADDRESS) ;
+BEGIN
+   MMDarg := KillString (MMDarg) ;
+   MDarg := KillString (MDarg) ;
+   MDarg := InitStringCharStar (arg)
+END SetMD ;
+
+
+(*
+   GetMD - returns MDarg filename as a c-string or NIL if it was never set.
+*)
+
+PROCEDURE GetMD () : ADDRESS ;
+BEGIN
+   RETURN string (MDarg)
+END GetMD ;
+
+
+(*
+   SetMMD - assigns MMDarg to the filename from arg.
+   This overrides any previous MD.
+*)
+
+PROCEDURE SetMMD (arg: ADDRESS) ;
+BEGIN
+   MDarg := KillString (MDarg) ;
+   MMDarg := KillString (MMDarg) ;
+   MMDarg := InitStringCharStar (arg)
+END SetMMD ;
+
+
+(*
+   GetMMD - returns MMDarg filename as a c-string or NIL if it was never set.
+*)
+
+PROCEDURE GetMMD () : ADDRESS ;
+BEGIN
+   RETURN string (MMDarg)
+END GetMMD ;
+
+
+(*
+   SetMQ - assigns MQarg to the filename from arg.
+*)
+
+PROCEDURE SetMQ (arg: ADDRESS) ;
+BEGIN
+   MQarg := KillString (MQarg) ;
+   MQarg := InitStringCharStar (arg)
+END SetMQ ;
+
+
+(*
+   GetMMD - returns MQarg filename as a c-string or NIL if it was never set.
+*)
+
+PROCEDURE GetMQ () : ADDRESS ;
+BEGIN
+   RETURN string (MQarg)
+END GetMQ ;
+
+
+(*
+   SetObj - assigns CmdLineObj to the filename from arg.
+*)
+
+PROCEDURE SetObj (arg: ADDRESS) ;
+BEGIN
+   CmdLineObj := KillString (CmdLineObj) ;
+   CmdLineObj := InitStringCharStar (arg)
+END SetObj ;
+
+
+(*
+   GetObj - returns CmdLineObj filename as a c-string or NIL if it was never set.
+*)
+
+PROCEDURE GetObj () : ADDRESS ;
+BEGIN
+   RETURN string (CmdLineObj)
+END GetObj ;
+
+
 (*
    CppCommandLine - returns the Cpp command line and all arguments.
                     NIL is returned if the -fcpp is absent.
@@ -364,6 +457,25 @@  BEGIN
 END GetCpp ;
 
 
+(*
+   SetPPOnly - set the PPonly (preprocess only) to value.
+*)
+
+PROCEDURE SetPPOnly (value: BOOLEAN) ;
+BEGIN
+   PPonly := value
+END SetPPOnly ;
+
+(*
+   GetPPOnly - get the PPonly (preprocess only).
+*)
+
+PROCEDURE GetPPOnly () : BOOLEAN ;
+BEGIN
+   RETURN PPonly
+END GetPPOnly ;
+
+
 (*
    Setc - set the cflag (compile only flag -c) to value.
 *)
@@ -1050,7 +1162,8 @@  END SetSaveTemps ;
 
 PROCEDURE SetSaveTempsDir (arg: ADDRESS) ;
 BEGIN
-   SaveTempsDir := InitStringCharStar (arg)
+   SaveTempsDir := InitStringCharStar (arg) ;
+   SaveTemps := TRUE
 END SetSaveTempsDir ;
 
 
@@ -1063,6 +1176,24 @@  BEGIN
    RETURN SaveTempsDir
 END GetSaveTempsDir ;
 
+(*
+   SetDumpDir - Set the dump dir.
+*)
+
+PROCEDURE SetDumpDir (arg: ADDRESS) ;
+BEGIN
+   DumpDir := InitStringCharStar (arg)
+END SetDumpDir ;
+
+
+(*
+   GetDumpDir - return DumpDir or NIL.
+*)
+
+PROCEDURE GetDumpDir () : String ;
+BEGIN
+   RETURN DumpDir
+END GetDumpDir ;
 
 (*
    SetScaffoldDynamic - set the -fscaffold-dynamic flag.
@@ -1247,5 +1378,9 @@  BEGIN
    GenModuleListFilename        := NIL ;
    SharedFlag                   := FALSE ;
    Barg                         := NIL ;
-   SaveTempsDir                 := NIL
+   MDarg                        := NIL ;
+   MMDarg                       := NIL ;
+   MQarg                        := NIL ;
+   SaveTempsDir                 := NIL ;
+   DumpDir                      := NIL
 END M2Options.
diff --git a/gcc/m2/gm2-compiler/M2Preprocess.def b/gcc/m2/gm2-compiler/M2Preprocess.def
index 08fe192b434..02585800317 100644
--- a/gcc/m2/gm2-compiler/M2Preprocess.def
+++ b/gcc/m2/gm2-compiler/M2Preprocess.def
@@ -45,7 +45,7 @@  EXPORT QUALIFIED PreprocessModule ;
                       All temporary files will be deleted when the compiler exits.
 *)
 
-PROCEDURE PreprocessModule (filename: String) : String ;
+PROCEDURE PreprocessModule (filename: String; isMain: BOOLEAN) : String ;
 
 
 END M2Preprocess.
diff --git a/gcc/m2/gm2-compiler/M2Preprocess.mod b/gcc/m2/gm2-compiler/M2Preprocess.mod
index eb080157f77..ebd9cb9f617 100644
--- a/gcc/m2/gm2-compiler/M2Preprocess.mod
+++ b/gcc/m2/gm2-compiler/M2Preprocess.mod
@@ -25,7 +25,7 @@  IMPLEMENTATION MODULE M2Preprocess ;
 FROM SYSTEM IMPORT WORD ;
 
 FROM DynamicStrings IMPORT string, InitString, Mark, KillString, EqualArray, InitStringCharStar,
-                           Dup, ConCat, ConCatChar, RIndex, Slice ;
+                           Dup, ConCat, ConCatChar, RIndex, Slice, Length ;
 
 FROM choosetemp IMPORT make_temp_file ;
 FROM pexecute IMPORT pexecute ;
@@ -33,7 +33,8 @@  FROM libc IMPORT system, exit, unlink, printf, atexit ;
 FROM Lists IMPORT List, InitList, KillList, IncludeItemIntoList, ForeachItemInListDo ;
 FROM FIO IMPORT StdErr, StdOut ;
 FROM M2Printf IMPORT fprintf1 ;
-FROM M2Options IMPORT Verbose, CppCommandLine, SaveTemps ;
+FROM M2Options IMPORT Verbose, PPonly, GetObj, GetMD, GetMMD, GetMQ,
+                      CppCommandLine, SaveTemps, GetSaveTempsDir, GetDumpDir ;
 FROM NameKey IMPORT Name, MakeKey, KeyToCharStar, makekey ;
 
 
@@ -77,14 +78,80 @@  BEGIN
    RETURN 0
 END RemoveFiles ;
 
+(*
+   Return the filename with no path.
+*)
+
+PROCEDURE GetFileName (Path: String) : String ;
+VAR
+   fstart: INTEGER ;
+BEGIN
+   fstart := RIndex(Path, '/', 0) ;
+   IF fstart=-1
+   THEN
+      fstart := 0
+   ELSE
+      fstart := fstart + 1
+   END ;
+   RETURN Dup (Slice(Path, fstart, Length (Path)))
+END GetFileName ;
+
+
+(*
+   Return basename.
+*)
+
+PROCEDURE BaseName (Path: String) : String ;
+VAR
+   ext,
+   basename: INTEGER ;
+BEGIN
+   basename := RIndex(Path, '/', 0) ;
+   IF basename=-1
+   THEN
+      basename := 0
+   ELSE
+      basename := basename + 1
+   END ;
+   ext := RIndex(Path, '.', 0) ;
+   IF ext=-1
+   THEN
+      ext := 0
+   END ;
+   RETURN Dup (Slice(Path, basename, ext))
+END BaseName ;
 
 (*
-   MakeSaveTempsFileName - return a temporary file "filename.i".
+   MakeSaveTempsFileName - return a temporary file like 
+   "./filename.{def,mod}.m2i" in the CWD unless SaveTempsDir = obj,
+   when we put it in the dumpdir if that is specified (or fallback to '.'
+   if not).
+   We have to keep the original extension because that disambiguates .def
+   and .mod files (otherwise, we'd need two 'preprocessed' extensions).
 *)
 
 PROCEDURE MakeSaveTempsFileName (filename: String) : String ;
+VAR
+   NewName,
+   DumpDir,
+   NewDir: String ;
 BEGIN
-   RETURN ConCat (Dup (filename), InitString ('.i'))
+   NewName := ConCat (GetFileName (filename), InitString ('.m2i')) ;
+   NewDir := GetSaveTempsDir () ;
+   DumpDir := GetDumpDir () ;
+(*   IF Verbose
+   THEN
+      fprintf1 (StdOut, "newname: %s", NewName) ;
+      fprintf1 (StdOut, " NewDir: %s", NewDir) ;
+      fprintf1 (StdOut, " DumpDir: %s\n", DumpDir)
+   END ;
+*)
+   IF (NewDir AND EqualArray (NewDir, 'obj')) AND DumpDir
+   THEN
+      RETURN Dup (ConCat (DumpDir, NewName))
+   ELSE
+      RETURN Dup (ConCat (InitString ('./'), NewName))
+   END ;
 END MakeSaveTempsFileName ;
 
 
@@ -98,7 +165,7 @@  END MakeSaveTempsFileName ;
                       All temporary files will be deleted when the compiler exits.
 *)
 
-PROCEDURE PreprocessModule (filename: String) : String ;
+PROCEDURE PreprocessModule (filename: String; isMain: BOOLEAN) : String ;
 VAR
    tempfile,
    command,
@@ -107,18 +174,55 @@  BEGIN
    command := CppCommandLine () ;
    IF (command = NIL) OR EqualArray (command, '')
    THEN
-      RETURN filename
+      RETURN Dup (filename)
    ELSE
-      IF SaveTemps
+      commandLine := Dup (command) ;
+      tempfile := NIL ;
+      (* We support MD and MMD for the main file only, at present.  *)
+      IF isMain OR PPonly
+      THEN
+         IF GetMD ()
+         THEN
+            tempfile := ConCat( Mark (InitString(' -MD ')),
+                                InitStringCharStar (GetMD ()))
+         ELSIF GetMMD ()
+         THEN
+            tempfile := ConCat( Mark (InitString(' -MMD ')),
+                                InitStringCharStar (GetMMD ()))
+         END ;
+         IF tempfile
+         THEN
+            commandLine := ConCat (Dup (commandLine), Dup (tempfile)) ;
+            (* We can only add MQ if we already have an MD/MMD.  *)
+            IF GetMQ ()
+            THEN
+               tempfile := ConCat( Mark (InitString(' -MQ ')),
+                                 InitStringCharStar (GetMQ ())) ;
+               commandLine := ConCat (Dup (commandLine), Dup (tempfile))
+            END ;
+         END ;
+      END ;
+      (* The output file depends on whether we are in stand-alone PP mode, and
+         if an output file is specified.  *)
+      tempfile := NIL ;
+      IF PPonly
+      THEN
+         IF GetObj()
+         THEN
+           tempfile := InitStringCharStar (GetObj ())
+         END ;
+      ELSIF SaveTemps
       THEN
-         tempfile := InitStringCharStar (MakeSaveTempsFileName (filename))
+         tempfile := MakeSaveTempsFileName (filename)
       ELSE
-         tempfile := InitStringCharStar (make_temp_file (KeyToCharStar (MakeKey('i'))))
+         tempfile := InitStringCharStar (make_temp_file (KeyToCharStar (MakeKey('.m2i'))))
+      END ;
+      commandLine := ConCat (ConCatChar (Dup (commandLine), ' '), filename) ;
+      IF tempfile
+      THEN
+         commandLine := ConCat (ConCat (Dup (commandLine),
+                                        Mark (InitString(' -o '))), tempfile) ;
       END ;
-      commandLine := Dup (command) ;
-      commandLine := ConCat (ConCat (ConCat (ConCatChar (Dup (commandLine), ' '), filename),
-                                     Mark (InitString(' -o '))),
-                             tempfile) ;
 (*  use pexecute in the future
       res := pexecute(string(Slice(commandLine, 0, Index(commandLine, ' ', 0))), etc etc );
 *)
diff --git a/gcc/m2/gm2-gcc/m2options.h b/gcc/m2/gm2-gcc/m2options.h
index 9cccb370a1a..92b4fd5b64c 100644
--- a/gcc/m2/gm2-gcc/m2options.h
+++ b/gcc/m2/gm2-gcc/m2options.h
@@ -61,6 +61,8 @@  EXTERN int M2Options_GetWholeValueCheck (void);
 
 EXTERN void M2Options_Setc (int value);
 EXTERN int M2Options_Getc (void);
+EXTERN void M2Options_SetPPOnly (int value);
+EXTERN int M2Options_GetPPOnly (void);
 
 EXTERN void M2Options_SetUselist (int value, const char *filename);
 EXTERN void M2Options_SetAutoInit (int value);
@@ -112,6 +114,7 @@  EXTERN void M2Options_SetStrictTypeChecking (int value);
 EXTERN void M2Options_SetWall (int value);
 EXTERN void M2Options_SetSaveTemps (int value);
 EXTERN void M2Options_SetSaveTempsDir (const char *arg);
+EXTERN void M2Options_SetDumpDir (const char *arg);
 EXTERN int M2Options_GetSaveTemps (void);
 EXTERN void M2Options_SetScaffoldStatic (int value);
 EXTERN void M2Options_SetScaffoldDynamic (int value);
@@ -121,6 +124,14 @@  EXTERN void M2Options_SetGenModuleList (int value, const char *filename);
 EXTERN void M2Options_SetShared (int value);
 EXTERN void M2Options_SetB (const char *arg);
 EXTERN char *M2Options_GetB (void);
+EXTERN void M2Options_SetMD (const char *arg);
+EXTERN char *M2Options_GetMD (void);
+EXTERN void M2Options_SetMMD (const char *arg);
+EXTERN char *M2Options_GetMMD (void);
+EXTERN void M2Options_SetMQ (const char *arg);
+EXTERN char *M2Options_GetMQ (void);
+EXTERN void M2Options_SetObj (const char *arg);
+EXTERN char *M2Options_GetObj (void);
 
 #undef EXTERN
 #endif /* m2options_h.  */
diff --git a/gcc/m2/gm2-gcc/m2type.cc b/gcc/m2/gm2-gcc/m2type.cc
index ad5064fc63a..634fad8df7c 100644
--- a/gcc/m2/gm2-gcc/m2type.cc
+++ b/gcc/m2/gm2-gcc/m2type.cc
@@ -36,6 +36,7 @@  along with GNU Modula-2; see the file COPYING3.  If not see
 #include "m2tree.h"
 #include "m2treelib.h"
 #include "m2type.h"
+#include "m2options.h"
 
 #undef USE_BOOLEAN
 static int broken_set_debugging_info = TRUE;
@@ -1782,6 +1783,9 @@  m2type_InitBaseTypes (location_t location)
 
   m2_packed_boolean_type_node = build_nonstandard_integer_type (1, TRUE);
 
+  if (M2Options_GetPPOnly ())
+    return;
+
   m2builtins_init (location);
   m2except_InitExceptions (location);
   m2expr_init (location);
diff --git a/gcc/m2/gm2-lang.cc b/gcc/m2/gm2-lang.cc
index 98707430ef5..4d9cae205a7 100644
--- a/gcc/m2/gm2-lang.cc
+++ b/gcc/m2/gm2-lang.cc
@@ -107,6 +107,8 @@  struct GTY (()) language_function
 
 /* Language hooks.  */
 
+static void gm2_langhook_parse_file (void);
+
 bool
 gm2_langhook_init (void)
 {
@@ -120,6 +122,13 @@  gm2_langhook_init (void)
 
   /* GNU Modula-2 uses exceptions.  */
   using_eh_for_cleanups ();
+
+  if (M2Options_GetPPOnly ())
+    {
+      /* preprocess the file here.  */
+      gm2_langhook_parse_file ();
+      return false; /* Finish now, no further compilation.  */
+    }
   return true;
 }
 
@@ -128,7 +137,9 @@  gm2_langhook_init (void)
 static unsigned int
 gm2_langhook_option_lang_mask (void)
 {
-  return CL_ModulaX2;
+  /* We need to process some driver options and pass through some C
+     ones to build our preprocessing lines.  */
+  return CL_ModulaX2 | CL_C | CL_DRIVER;
 }
 
 /* Initialize the options structure.  */
@@ -155,27 +166,146 @@  gm2_langhook_init_options_struct (struct gcc_options *opts)
 
 static vec<bool> filename_cpp;
 
+/* Build the C preprocessor command line here, since we need to include
+   options that are not passed to the handle_option function.  */
+
 void
 gm2_langhook_init_options (unsigned int decoded_options_count,
                            struct cl_decoded_option *decoded_options)
 {
   unsigned int i;
   bool in_cpp_args = false;
+  bool building_cpp_command = false;
 
   for (i = 1; i < decoded_options_count; i++)
     {
-      switch (decoded_options[i].opt_index)
-        {
-        case OPT_fcpp_begin:
-          in_cpp_args = true;
-          break;
-        case OPT_fcpp_end:
-          in_cpp_args = false;
-          break;
-        case OPT_SPECIAL_input_file:
-        case OPT_SPECIAL_program_name:
-          filename_cpp.safe_push (in_cpp_args);
-        }
+      enum opt_code code = (enum opt_code)decoded_options[i].opt_index;
+      const struct cl_option *option = &cl_options[code];
+      const char *opt = (const char *)option->opt_text;
+      const char *arg = decoded_options[i].arg;
+      HOST_WIDE_INT value = decoded_options[i].value;
+      switch (code)
+	{
+	case OPT_fcpp:
+	  gcc_checking_assert (building_cpp_command);
+	  break;
+	case OPT_fcpp_begin:
+	  in_cpp_args = true;
+	  building_cpp_command = true;
+	  break;
+	case OPT_fcpp_end:
+	  in_cpp_args = false;
+	  break;
+	case OPT_SPECIAL_input_file:
+	  filename_cpp.safe_push (in_cpp_args);
+	  break;
+
+	/* C and driver opts that are not passed to the preprocessor for
+	   modula-2, but that we use internally for building preprocesor
+	   command lines.  */
+	case OPT_B:
+	  M2Options_SetB (arg);
+	  break;
+	case OPT_c:
+	  M2Options_Setc (value);
+	  break;
+	case OPT_dumpdir:
+	  if (building_cpp_command)
+	    M2Options_SetDumpDir (arg);
+	  break;
+	case OPT_save_temps:
+	  if (building_cpp_command)
+	    M2Options_SetSaveTemps (value);
+	  break;
+	case OPT_save_temps_:
+	  if (building_cpp_command)
+	    /* Also sets SaveTemps. */
+	    M2Options_SetSaveTempsDir (arg);
+	  break;
+
+	case OPT_E:
+	  if (!in_cpp_args)
+	    {
+	      M2Options_SetPPOnly (value);
+	      building_cpp_command = true;
+	    }
+	  M2Options_CppArg (opt, arg, (option->flags & CL_JOINED)
+			      && !(option->flags & CL_SEPARATE));
+	  break;
+	case OPT_M:
+	case OPT_MM:
+	  gcc_checking_assert (building_cpp_command);
+	  M2Options_SetPPOnly (value);
+	  /* This is a preprocessor command.  */
+	  M2Options_CppArg (opt, arg, (option->flags & CL_JOINED)
+			      && !(option->flags & CL_SEPARATE));
+	  break;
+
+	/* We can only use MQ when the command line is either PP-only, or
+	   when there is a MD/MMD on it.  */
+	case OPT_MQ:
+	  M2Options_SetMQ (arg);
+	  break;
+
+	case OPT_o:
+	  M2Options_SetObj (arg);
+	  break;
+
+	/* C and driver options that we ignore for the preprocessor lines.  */
+	case OPT_fpch_deps:
+	case OPT_fpch_preprocess:
+	  break;
+
+	case OPT_fplugin_:
+	  /* FIXME: We might need to handle this specially, since the modula-2
+	     plugin is not usable here, but others might be.
+	     For now skip all plugins to avoid fails with the m2 one.  */
+	  break;
+
+	/* Preprocessor arguments with a following filename.  */
+	case OPT_MD:
+	case OPT_MMD:
+	  /* Save the filename associated with the MD/MMD which will also
+	     mark the option as used.  FIXME: maybe we should diagnose a
+	     missing filename here, rather than assert.  */
+	  gcc_checking_assert (i+1 < decoded_options_count);
+	  gcc_checking_assert (decoded_options[i+1].opt_index
+			       == OPT_SPECIAL_input_file);
+	  /* Pick up the following filename.  */
+	  arg = decoded_options[i+1].arg;
+	  if (code == OPT_MD)
+	    M2Options_SetMD (arg);
+	  else
+	    M2Options_SetMMD (arg);
+	  break;
+
+	/* Options we act on and also pass to the preprocessor.  */
+	case OPT_O:
+	  M2Options_SetOptimizing (value);
+	  if (building_cpp_command)
+	    M2Options_CppArg (opt, arg, (option->flags & CL_JOINED)
+			      && !(option->flags & CL_SEPARATE));
+	  break;
+	case OPT_v:
+	  M2Options_SetVerbose (value);
+	  /* FALLTHROUGH */
+	default:
+	  if (code >= N_OPTS)
+	    {
+	      // FIXME remove debug.
+	      fprintf(stderr, "%s : %s\n", opt, (arg ? arg : ""));
+	      break;
+	    }
+	  /* Do not pass Modula-2 args to the preprocessor, any that we care
+	     about here should already have been handled above.  */
+	  if (option->flags & CL_ModulaX2)
+	    break;
+	  /* Otherwise, add this to the CPP command line.  */
+	  if (building_cpp_command)
+	    M2Options_CppArg (opt, arg, (option->flags & CL_JOINED)
+			      && !(option->flags & CL_SEPARATE));
+	  break;
+	}
     }
   filename_cpp.safe_push (false);
 }
@@ -197,28 +327,16 @@  gm2_langhook_handle_option (
 {
   enum opt_code code = (enum opt_code)scode;
 
+  const struct cl_option *option = &cl_options[scode];
+  const char *opt = (const char *)option->opt_text;
   /* ignore file names.  */
   if (code == N_OPTS)
     return 1;
 
   switch (code)
     {
-    case OPT_B:
-      M2Options_SetB (arg);
-      return 1;
-    case OPT_c:
-      M2Options_Setc (value);
-      return 1;
     case OPT_I:
-      if (insideCppArgs)
-	{
-	  const struct cl_option *option = &cl_options[scode];
-	  const char *opt = (const char *)option->opt_text;
-	  M2Options_CppArg (opt, arg, (option->flags & CL_JOINED)
-				       && !(option->flags & CL_SEPARATE));
-	}
-      else
-	Ipaths.push_back (arg);
+      Ipaths.push_back (arg);
       return 1;
     case OPT_fiso:
       M2Options_SetISO (value);
@@ -358,6 +476,9 @@  gm2_langhook_handle_option (
     case OPT_fcpp:
       M2Options_SetCpp (value);
       return 1;
+    case OPT_fpreprocessed:
+      /* Provided for compatibility; ignore for now.  */
+      return 1;
     case OPT_fcpp_begin:
       insideCppArgs = TRUE;
       return 1;
@@ -396,31 +517,25 @@  gm2_langhook_handle_option (
       return 1;
       break;
     case OPT_iprefix:
+      iprefix = arg;
+      return 1;
+      break;
     case OPT_imultilib:
+      imultilib = arg;
+      return 1;
+      break;
     case OPT_isystem:
+      isystem.push_back (arg);
+      return 1;
+      break;
     case OPT_iquote:
+      iquote.push_back (arg);
+      return 1;
+      break;
     case OPT_isysroot:
-      if (insideCppArgs)
-	{
-	  const struct cl_option *option = &cl_options[scode];
-	  const char *opt = (const char *)option->opt_text;
-	  M2Options_CppArg (opt, arg, (option->flags & CL_JOINED)
-				       && !(option->flags & CL_SEPARATE));
-	}
-      if (code == OPT_iprefix)
-	iprefix = arg;
-      else if (code == OPT_imultilib)
-	imultilib = arg;
-      else if (code == OPT_iquote)
-	iquote.push_back (arg);
-      else if (code == OPT_isystem)
-	isystem.push_back (arg);
       /* Otherwise, ignored, at least for now. */
       return 1;
       break;
-    case OPT_O:
-      M2Options_SetOptimizing (value);
-      return 1;
     case OPT_quiet:
       M2Options_SetQuiet (value);
       return 1;
@@ -445,24 +560,19 @@  gm2_langhook_handle_option (
         }
       else
         return 0;
-    case OPT_save_temps:
-      M2Options_SetSaveTemps (value);
-      return 1;
-    case OPT_save_temps_:
-      M2Options_SetSaveTempsDir (arg);
-      return 1;
-    case OPT_v:
-      M2Options_SetVerbose (value);
+    case OPT_o:
+      /* Options we ignore, always.  */
       return 1;
     default:
       if (insideCppArgs)
-	{
-	  const struct cl_option *option = &cl_options[scode];
-	  const char *opt = (const char *)option->opt_text;
-	  M2Options_CppArg (opt, arg, (option->flags & CL_JOINED)
-				       && !(option->flags & CL_SEPARATE));
-	  return 1;
-	}
+	/* Already handled.  */
+	return 1;
+      else if (option->flags & CL_DRIVER)
+	/* Ignore driver options we do not specifically use.  */
+	return 1;
+      else if (option->flags & CL_C)
+	/* Ignore C options we do not specifically use.  */
+	return 1;
       return 0;
     }
   return 0;
@@ -574,7 +684,7 @@  gm2_langhook_post_options (const char **pfilename)
     add_m2_import_paths (flibs);
 
  /* Returning false means that the backend should be used.  */
-  return false;
+  return M2Options_GetPPOnly ();
 }
 
 /* Call the compiler for every source filename on the command line.  */
@@ -597,7 +707,8 @@  static void
 gm2_langhook_parse_file (void)
 {
   gm2_parse_input_files (in_fnames, num_in_fnames);
-  write_globals ();
+  if (!M2Options_GetPPOnly ())
+    write_globals ();
 }
 
 static tree
diff --git a/gcc/m2/gm2spec.cc b/gcc/m2/gm2spec.cc
index c248d1bfcb2..bc93133243c 100644
--- a/gcc/m2/gm2spec.cc
+++ b/gcc/m2/gm2spec.cc
@@ -133,8 +133,10 @@  static const char *add_include (const char *libpath, const char *library);
 
 static bool seen_scaffold_static = false;
 static bool seen_scaffold_dynamic = false;
-static bool scaffold_dynamic = true; // Default uses -fscaffold-dynamic.
+static bool seen_scaffold_main = false;
 static bool scaffold_static = false;
+static bool scaffold_dynamic = true; // Default uses -fscaffold-dynamic.
+static bool scaffold_main = false;
 static bool seen_gen_module_list = false;
 static bool seen_uselist = false;
 static bool uselist = false;
@@ -525,17 +527,20 @@  lang_specific_driver (struct cl_decoded_option **in_decoded_options,
 	  scaffold_static = decoded_options[i].value;
 	  args[i] |= SKIPOPT; /* We will add the option if it is needed.  */
 	  break;
+	case OPT_fscaffold_main:
+	  seen_scaffold_main = true;
+	  scaffold_main = decoded_options[i].value;
+	  args[i] |= SKIPOPT; /* We will add the option if it is needed.  */
+	  break;
 	case OPT_fgen_module_list_:
 	  seen_gen_module_list = true;
 	  gen_module_list = decoded_options[i].value;
 	  if (gen_module_list)
 	    gen_module_filename = decoded_options[i].arg;
-	  args[i] |= SKIPOPT; /* We will add the option if it is needed.  */
 	  break;
 	case OPT_fuse_list_:
 	  seen_uselist = true;
 	  uselist = decoded_options[i].value;
-	  args[i] |= SKIPOPT; /* We will add the option if it is needed.  */
 	  break;
 
 	case OPT_nostdlib:
@@ -592,6 +597,14 @@  lang_specific_driver (struct cl_decoded_option **in_decoded_options,
 	  library = -1;
 	  break;
 
+	/* PCH makes no sense here, we do not catch -output-pch on purpose,
+	   that should flag an error.  */
+	case OPT_fpch_deps:
+	case OPT_fpch_preprocess:
+	case OPT_Winvalid_pch:
+	  args[i] |= SKIPOPT;
+	  break;
+
 	case OPT_static:
 	  static_link = 1;
 	  break;
@@ -694,8 +707,10 @@  lang_specific_driver (struct cl_decoded_option **in_decoded_options,
      We also add default scaffold linking options.  */
 
   /* If we have not seen either uselist or gen_module_list and we need
-     to link then we turn on -fgen_module_list=- as the default.  */
-  if ((! (seen_uselist || seen_gen_module_list)) && linking)
+     to link or compile a module list then we turn on -fgen_module_list=-
+     as the default.  */
+  if (!seen_uselist && !seen_gen_module_list
+      && (linking || scaffold_main))
     append_option (OPT_fgen_module_list_, "-", 1);
 
   /* We checked that they were not both enabled above, if there was a set
@@ -705,6 +720,14 @@  lang_specific_driver (struct cl_decoded_option **in_decoded_options,
   if (seen_scaffold_static)
     append_option (OPT_fscaffold_static, NULL, scaffold_static);
 
+  /* If the user has set fscaffold-main specifically, use that.  Otherwise, if
+     we are linking then set it so that we generate the relevant code for the
+     main module.  */
+  if (seen_scaffold_main)
+    append_option (OPT_fscaffold_main, NULL, scaffold_main);
+  else if (linking)
+    append_option (OPT_fscaffold_main, NULL, true);
+
   if (allow_libraries)
     {
       /* If the libraries have not been specified by the user, select the
diff --git a/gcc/m2/lang-specs.h b/gcc/m2/lang-specs.h
index bf882649b21..6228c3c5dad 100644
--- a/gcc/m2/lang-specs.h
+++ b/gcc/m2/lang-specs.h
@@ -21,19 +21,35 @@  along with GCC; see the file COPYING3.  If not see
 /* This is the contribution to the `default_compilers' array in gcc.c for
    GNU Modula-2.  */
 
-/* Pass the preprocessor options on the command line together with
-   the exec prefix.  */
-
+/* A spec for the 'integrated' preprocessor implementation for Modula-2.  */
 #define M2CPP \
-  "%{fcpp:-fcpp-begin " \
-  "      -E -lang-asm -traditional-cpp " \
-  "      %(cpp_unique_options) -fcpp-end; \
-     : %I } "
+  "%{E|M|MM|fcpp: %{E} -fcpp-begin " \
+  "      %{!E:-E} %(cpp_unique_options) -traditional-cpp -ansi " \
+  "      -fcpp-end %{B*} %{save-temps*} ; \
+     : %{v} %I } "
+
+/* We have three modes:
+   1. When the preprocessing step is explict and there is no following
+      compilation.  Here we do a similar process to cc1 -E where most of
+      the compilation is short-circuited.
+   2. When we are mimicking an integrated preprocessor.  Here we use the
+      modula-2 'fcpp' to construct a command line for the preprocessor and
+      snarf save-temps and dumpdir inputs to try and be consistent.
+   3. We can consume a pre-processed modula-2 source.  */
 
   {".mod", "@modula-2", 0, 0, 0},
   {"@modula-2",
-      "cc1gm2 " M2CPP
-      "      %(cc1_options) %{B*} %{c*} %{+e*} %{I*} "
-      "      %{i*} %{save-temps*} %{v} "
-      "      %i %{!fsyntax-only:%(invoke_as)}",
-      0, 0, 0},
+   /* For preprocessing we use cc1 but wrap it in cc1gm2.  */
+   "%{E|M|MM:\
+      cc1gm2 " M2CPP " %{!fcpp:-fcpp;:%{fcpp}} %{I*} %i } \
+    %{!E:%{!M:%{!MM:\
+      cc1gm2 " M2CPP " %(cc1_options) %{I*} %i %{c} \
+      %{MF*:%eto generate dependencies you must specify either '-M' or '-MM'} \
+      %{!fsyntax-only:%(invoke_as)} \
+    }}}", 0, 0, 0},
+  {".m2i", "@modula-2-cpp-output", 0, 0, 0},
+  {"@modula-2-cpp-output",
+   "%{!M:%{!MM:%{!E: \
+      cc1gm2 %<fcpp %(cc1_options) %{v} %I -fmod=.mod.m2i -fdef=.def.m2i %{I*} \
+	-fpreprocessed %i %{c} \
+    %{!fsyntax-only:%(invoke_as)}}}}", 0, 0, 0},
diff --git a/gcc/m2/lang.opt b/gcc/m2/lang.opt
index 7a19adc0790..43d6ba56767 100644
--- a/gcc/m2/lang.opt
+++ b/gcc/m2/lang.opt
@@ -26,78 +26,6 @@ 
 Language
 Modula-2
 
-B
-Modula-2
-; Documented in c.opt
-
-D
-Modula-2
-; Documented in c.opt
-
-E
-Modula-2
-; Documented in c.opt (passed to the preprocessor if -fcpp is used)
-
-I
-Modula-2 Joined Separate
-; Documented in c.opt
-
-L
-Modula-2 Joined Separate
-; Not documented
-
-M
-Modula-2
-; Documented in c.opt
-
-MD
-Modula-2
-; Documented in c.opt
-
-MF
-Modula-2
-; Documented in c.opt
-
-MG
-Modula-2
-; Documented in c.opt
-
-MM
-Modula-2
-; Documented in c.opt
-
-MMD
-Modula-2
-; Documented in c.opt
-
-Mmodules
-Modula-2
-; Documented in c.opt
-
-Mno-modules
-Modula-2
-; Documented in c.opt
-
-MP
-Modula-2
-; Documented in c.opt
-
-MQ
-Modula-2
-; Documented in c.opt
-
-MT
-Modula-2
-; Documented in c.opt
-
-P
-Modula-2
-; Documented in c.opt
-
-O
-Modula-2
-; Documented in c.opt
-
 Wall
 Modula-2
 ; Documented in c.opt
@@ -274,6 +202,10 @@  fpositive-mod-floor-div
 Modula-2
 force positive result from MOD and DIV result floor
 
+fpreprocessed
+Modula-2
+; Documented in c.opt
+
 fpthread
 Modula-2
 link against the pthread library (default on)
@@ -350,53 +282,14 @@  fwholevalue
 Modula-2
 turns on runtime checking to check whether a whole number is about to exceed range
 
-iprefix
-Modula-2
-; Documented in c.opt
-
-iquote
-Modula-2
-; Documented in c.opt
-
-isystem
-Modula-2
-; Documented in c.opt
-
-idirafter
-Modula-2
-; Documented in c.opt
-
-imultilib
-Modula-2
+;fworking-directory
+;Modula-2
 ; Documented in c.opt
 
 lang-asm
 Modula-2
 ; Documented in c.opt
 
--save-temps
-Modula-2 Alias(save-temps)
-
-save-temps
-Modula-2
-save temporary preprocessed files
-
-save-temps=
-Modula-2 Joined
-save temporary preprocessed files
-
-traditional-cpp
-Modula-2
-; Documented in c.opt
-
-v
-Modula-2
-; Documented in c.opt
-
-x
-Modula-2 Joined
-specify the language from the compiler driver
-
 static-libgm2
 Driver
 Link the standard Modula-2 libraries statically in the compilation.