gollvm: support -fPIE/-fno-PIE/-fpie/-fno-pie

Make sure that all of the various PIE compiler options are supported
properly. Prior to this point the driver accepted -fpic/-fPIC and
-pie/-no-pie but not -fPIE/-fno-PIE/-fpie/-fno-pie. As with clang,
rightmost pic/pie flag wins, e.g. -fpic -fno-PIE turns off -fpic.

Change-Id: I81b2503b28951d20a6fa3b548cb1331afa792457
Reviewed-on: https://go-review.googlesource.com/115055
Reviewed-by: Ian Lance Taylor <iant@golang.org>
diff --git a/driver/CompileGo.cpp b/driver/CompileGo.cpp
index 4100966..9f25875 100644
--- a/driver/CompileGo.cpp
+++ b/driver/CompileGo.cpp
@@ -415,6 +415,8 @@
   module_->setTargetTriple(triple_.getTriple());
   module_->setDataLayout(target_->createDataLayout());
   module_->setPICLevel(driver_.getPicLevel());
+  if (driver_.picIsPIE())
+    module_->setPIELevel(driver_.getPieLevel());
 
   // Now construct Llvm_backend helper.
   bridge_.reset(new Llvm_backend(context_, module_.get(), linemap_.get()));
diff --git a/driver/Driver.cpp b/driver/Driver.cpp
index 63d2e43..6916894 100644
--- a/driver/Driver.cpp
+++ b/driver/Driver.cpp
@@ -127,26 +127,72 @@
   return (arg ? arg->getOption().matches(options::OPT_pie) : false);
 }
 
-// Return any settings from the -fPIC/-fpic options, if present. The
-// intent of the code below is to support "rightmost on the command
-// line wins" (compatible with clang and other compilers), so if you
-// specify "-fPIC -fpic" you get small PIC, whereas "-fPIC -fpic
-// -fPIC" this will give you large PIC.
+// Return any settings from the -fPIC/-fpic/-fPIE/-fpie options, if
+// present. The intent of the code below is to support "rightmost
+// on the command line wins" (compatible with clang and other
+// compilers), so if you specify "-fPIC -fpic" you get small PIC,
+// whereas "-fPIC -fpic -fPIC" will give you large PIC.
+// Similarly the presence of a "-fno-pic" to the right of "-fPIE"
+// will disable use of the PIE code model.
+
 PICLevel::Level Driver::getPicLevel()
 {
   opt::Arg *arg = args_.getLastArg(gollvm::options::OPT_fpic,
                                    gollvm::options::OPT_fno_pic,
                                    gollvm::options::OPT_fPIC,
-                                   gollvm::options::OPT_fno_PIC);
+                                   gollvm::options::OPT_fno_PIC,
+                                   gollvm::options::OPT_fpie,
+                                   gollvm::options::OPT_fno_pie,
+                                   gollvm::options::OPT_fPIE,
+                                   gollvm::options::OPT_fno_PIE);
   if (arg == nullptr)
     return PICLevel::NotPIC;
-  if (arg->getOption().matches(gollvm::options::OPT_fpic))
+  if (arg->getOption().matches(gollvm::options::OPT_fpic) ||
+      arg->getOption().matches(gollvm::options::OPT_fpie))
     return PICLevel::SmallPIC;
-  else if (arg->getOption().matches(gollvm::options::OPT_fPIC))
+  else if (arg->getOption().matches(gollvm::options::OPT_fPIC) ||
+           arg->getOption().matches(gollvm::options::OPT_fPIE))
     return PICLevel::BigPIC;
   return PICLevel::NotPIC;
 }
 
+// Similar to the routine above, but for -fPIE/-fpie etc.
+
+PIELevel::Level Driver::getPieLevel()
+{
+  opt::Arg *arg = args_.getLastArg(gollvm::options::OPT_fpie,
+                                   gollvm::options::OPT_fno_pie,
+                                   gollvm::options::OPT_fPIE,
+                                   gollvm::options::OPT_fno_PIE);
+  if (arg == nullptr)
+    return PIELevel::Default;
+  if (arg->getOption().matches(gollvm::options::OPT_fpie))
+    return PIELevel::Small;
+  else if (arg->getOption().matches(gollvm::options::OPT_fPIE))
+    return PIELevel::Large;
+  return PIELevel::Default;
+}
+
+// Returns TRUE if the rightmost enable -fpic/-fpie command line option is
+// PIE as opposed to PIC.
+
+bool Driver::picIsPIE()
+{
+  opt::Arg *lpa = args_.getLastArg(gollvm::options::OPT_fPIC,
+                              gollvm::options::OPT_fno_PIC,
+                              gollvm::options::OPT_fpic,
+                              gollvm::options::OPT_fno_pic,
+                              gollvm::options::OPT_fPIE,
+                              gollvm::options::OPT_fno_PIE,
+                              gollvm::options::OPT_fpie,
+                              gollvm::options::OPT_fno_pie);
+  if (!lpa)
+    return false;
+  opt::Option opt = lpa->getOption();
+  return (opt.matches(gollvm::options::OPT_fPIE) ||
+          opt.matches(gollvm::options::OPT_fpie));
+}
+
 // Given a pair of llvm::opt options (presumably corresponding to
 // -fXXX and -fno-XXX boolean flags), select the correct value for the
 // option depending on the relative position of the options on the
diff --git a/driver/Driver.h b/driver/Driver.h
index a69d888..24b35c9 100644
--- a/driver/Driver.h
+++ b/driver/Driver.h
@@ -93,6 +93,8 @@
 
   // Helpers related to command line options.
   llvm::PICLevel::Level getPicLevel();
+  llvm::PIELevel::Level getPieLevel();
+  bool picIsPIE();
   bool isPIE();
   template<typename IT>
   llvm::Optional<IT> getLastArgAsInteger(gollvm::options::ID id,
diff --git a/driver/GollvmOptions.td b/driver/GollvmOptions.td
index bb947d0..9686261 100644
--- a/driver/GollvmOptions.td
+++ b/driver/GollvmOptions.td
@@ -223,6 +223,11 @@
 def fno_PIC : Flag<["-"], "fno-PIC">, Group<f_Group>;
 def fpic : Flag<["-"], "fpic">, Group<f_Group>;
 def fno_pic : Flag<["-"], "fno-pic">, Group<f_Group>;
+def fPIE : Flag<["-"], "fPIE">, Group<f_Group>;
+def fno_PIE : Flag<["-"], "fno-PIE">, Group<f_Group>;
+def fpie : Flag<["-"], "fpie">, Group<f_Group>;
+def fno_pie : Flag<["-"], "fno-pie">, Group<f_Group>;
+
 
 def fno_inline : Flag<["-"], "fno-inline">, Group<f_Group>,
   HelpText<"Disable inlining">;