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.
