[flang-dev] Compiling Flang with Microsoft Visual Studio

Michael Kruse via flang-dev flang-dev at lists.llvm.org
Wed Jul 22 13:15:29 PDT 2020


In the last Flang technical call, I offered to check whether patches work
under Windows. Turns out, Flang currently does not compile at all when
using MSVC. Here are my notes for making is compile using the latest msvc
compiler version 19.26.28806. I don't think we need to put effort into
making it compile with older versions.

This is not a list of fixes. Some changes are not general purpose
solutions, some break check-flang. However, I would gladly help working on
fixing these upstream. However, with these changes f18 compiles and links
successfully.


CMAKE_CXX_STANDARD=14

https://cmake.org/cmake/help/latest/prop_tgt/CXX_STANDARD.html

Flang's CMakeLists.txt sets CMAKE_CXX_STANDARD=17. This is ignored when
using a pre-configured llvm-prject build directory and adding flang to
LLVM_ENABLED_PROJECTS. Maybe we can find a better solution such as setting
CXX_STANDARD for flang targets only, but I am not sure whether the
libraries are compatible when compiled for different versions of the C++
standard.


flang\include\flang\Common\idioms.h(20,1): fatal error C1189: #error:  this
is a C++17 program

https://devblogs.microsoft.com/cppblog/msvc-now-correctly-reports-__cplusplus/

For historic reasons, msvc never bumped the value of the __cplusplus macro.
Adding /Zc:__cplusplus to CMAKE_CXX_FLAGS fixes this.

NOTE: When saying 'adding to CMAKE_CXX_FLAGS fixes this' I do not suggest
to add
list(APPEND CMAKE_CXX_FLAGS "...")
into some CMakeLists.txt. A much better way is to use
add_compile_options(...)
CMAKE_CXX_FLAGS should be entirely under user control and never modified
within the CMakeLists.txt.


-Wno-unused-parameter -Wno-error

lib/Optimizer/CMakeLists.txt adds these unconditionally to CMAKE_CXX_FLAGS
(which, as mentioned before, should not be done). Msvc does not understand
these flags.
Also, -Werror is not used by default anymore. Adding -Wno-error to selected
sources also seem to defeat the purpose of -Werror.

--- a/flang/lib/Optimizer/CMakeLists.txt
+++ b/flang/lib/Optimizer/CMakeLists.txt
@@ -1,4 +1,4 @@
-set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-unused-parameter -Wno-error")
 get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS)

 add_flang_library(FIROptimizer

--- a/flang/lib/Lower/CMakeLists.txt
+++ b/flang/lib/Lower/CMakeLists.txt
@@ -1,4 +1,3 @@
-set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-error -Wno-unused-parameter")
 get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS)

 add_flang_library(FortranLower

llvm\include\llvm/ADT/iterator.h(68,19): warning C4996:
'std::iterator<IteratorCategoryT,T,DifferenceTypeT,PointerT,ReferenceT>':
warning STL4015: The std::iterator class template (used as a base class to
provide typedefs) is deprecated in C++17. (The <iterator> header is NOT
deprecated.) The C++ Standard has never required user-defined iterators to
derive from std::iterator.

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0174r1.html#2.1

When C++17 mode is enabled, msvc starts warning about deprecated features.
Most prevalant is the warning to not derive from std::iterator, mostly in
the LLVM support library. I.e. this will probably only fixed when LLVM
itself bumps to C++17.
Adding /D_SILENCE_ALL_CXX17_DEPRECATION_WARNINGS to CXX_CMAKE_FLAGS
disables these warnings.


fatal error C1128: number of sections exceeded object file format limit:
compile with /bigobj

https://docs.microsoft.com/en-us/cpp/error-messages/compiler-errors-1/fatal-error-c1128?view=vs-2019

Some files contain more symbols than the standard PE-COFF object format
allows.
Adding /bigobj to CMAKE_CXX_FLAGS switches to a new object format.
Alternatively, /Gy- could be used, but that effectively disables COMDAT
folding/pruning.


INTERNAL COMPILER ERROR in
'C:\PROGRA~2\MICROS~1\2019\COMMUN~1\VC\Tools\MSVC\1426~1.288\bin\Hostx64\x64\cl.exe'

https://developercommunity.visualstudio.com/content/problem/1067774/ice-internal-compiler-error-on-constexpr-range-bas.html

This occurs when iterating over an std::initializer_list in a constexpr
constructor. As a workaround, I replaced the std::initializer_list
constructor with a variadic template constructor. Unfortunately, this also
requires some other changes that rely on implicit conversion of
std::initializer_list literals in the source.

--- a/flang/include/flang/Common/enum-set.h
+++ b/flang/include/flang/Common/enum-set.h
@@ -36,10 +36,10 @@ public:
   using enumerationType = ENUM;

   constexpr EnumSet() {}
-  constexpr EnumSet(const std::initializer_list<enumerationType> &enums) {
-    for (auto x : enums) {
-      set(x);
-    }
+  explicit constexpr EnumSet(enumerationType Singletpn) { set(Singletpn); }
+  template<typename... T>
+  constexpr EnumSet(enumerationType a, T... Remaining) :
EnumSet(Remaining...) {
+    set(a);
   }
   constexpr EnumSet(const EnumSet &) = default;
   constexpr EnumSet(EnumSet &&) = default;

flang\lib\Semantics\symbol.cpp(258,1): error C2668:
'Fortran::semantics::Symbol::detailsIf': ambiguous call to overloaded
function

Msvc is confused whether to call to const or non-const qualified method.
Since the calling method is const-qualified, there really should not be
such an ambiguity.
The following patch the issue:

--- a/flang/lib/Semantics/symbol.cpp
+++ b/flang/lib/Semantics/symbol.cpp
@@ -255,7 +255,7 @@ bool Symbol::CanReplaceDetails(const Details &details)
const {
               return has<SubprogramNameDetails>() || has<EntityDetails>();
             },
             [&](const DerivedTypeDetails &) {
-              auto *derived{detailsIf<DerivedTypeDetails>()};
+              auto *derived{this->detailsIf<DerivedTypeDetails>()};
               return derived && derived->isForwardReferenced();
             },
             [](const auto &) { return false; },


lang\runtime\file.cpp(186,19): error C2589: '(': illegal token on right
side of '::'

https://stackoverflow.com/questions/11544073/how-do-i-deal-with-the-max-macro-in-windows-h-colliding-with-max-in-std

windows.h defines macros min and max, conflicting with std::min. Defining
NOMINMAX makes windows.h stop doing that:

--- a/flang/runtime/file.cpp
+++ b/flang/runtime/file.cpp
@@ -15,6 +15,7 @@
 #include <fcntl.h>
 #include <stdlib.h>
 #ifdef _WIN32
+#define NOMINMAX
 #include <io.h>
 #include <windows.h>
 #else


flang\include\flang\Evaluate\expression.h(646): warning C4099:
'Fortran::evaluate::Relational<Fortran::evaluate::SomeType>': type name
first seen using 'class' now seen using 'struct'

When forward-declaring a class/struct, it should match struct/class with
the keyword used to define the struct class.

Clang version of the same warning:
flang/include/flang/Evaluate/expression.h:646:17: warning: class template
'Relational' was previously declared as a struct template; this is valid,
but may result in linker errors under the Microsoft C++ ABI
[-Wmismatched-tags]

--- a/flang/include/flang/Evaluate/expression.h
+++ b/flang/include/flang/Evaluate/expression.h
@@ -640,7 +643,7 @@ public:
 FOR_EACH_INTEGER_KIND(extern template struct Relational, )
 FOR_EACH_REAL_KIND(extern template struct Relational, )
 FOR_EACH_CHARACTER_KIND(extern template struct Relational, )
-extern template struct Relational<SomeType>;
+extern template class Relational<SomeType>;

 // Logical expressions of a kind bigger than LogicalResult
 // do not include Relational<> operations as possibilities,


include\flang/Decimal/decimal.h(109): error C2765:
'Fortran::decimal::ConvertToBinary': an explicit specialization or
instantiation of a function template cannot have any default arguments

https://docs.microsoft.com/en-us/cpp/error-messages/compiler-errors-2/compiler-error-c2765?view=vs-2019

Self explaining. Msvc is definitely right here:

https://en.cppreference.com/w/cpp/language/template_specialization

>  Default function arguments cannot be specified in explicit
> specializations of function templates, member function templates, and
> member functions of class templates when the class is implicitly
> instantiated.


--- a/flang/include/flang/Decimal/decimal.h
+++ b/flang/include/flang/Decimal/decimal.h
@@ -106,17 +106,17 @@ ConversionToBinaryResult<PREC> ConvertToBinary(
     const char *&, enum FortranRounding = RoundNearest);

 extern template ConversionToBinaryResult<8> ConvertToBinary<8>(
-    const char *&, enum FortranRounding = RoundNearest);
+    const char *&, enum FortranRounding);
 extern template ConversionToBinaryResult<11> ConvertToBinary<11>(
-    const char *&, enum FortranRounding = RoundNearest);
+    const char *&, enum FortranRounding);
 extern template ConversionToBinaryResult<24> ConvertToBinary<24>(
-    const char *&, enum FortranRounding = RoundNearest);
+    const char *&, enum FortranRounding);
 extern template ConversionToBinaryResult<53> ConvertToBinary<53>(
-    const char *&, enum FortranRounding = RoundNearest);
+    const char *&, enum FortranRounding);
 extern template ConversionToBinaryResult<64> ConvertToBinary<64>(
-    const char *&, enum FortranRounding = RoundNearest);
+    const char *&, enum FortranRounding);
 extern template ConversionToBinaryResult<113> ConvertToBinary<113>(
-    const char *&, enum FortranRounding = RoundNearest);
+    const char *&, enum FortranRounding);
 } // namespace Fortran::decimal
 extern "C" {
 #define NS(x) Fortran::decimal::x


include\flang/Evaluate/call.h(230): warning C4661:
'std::optional<Fortran::evaluate::Constant<Fortran::evaluate::Type<Fortran::common::TypeCategory::Integer,1>>>
Fortran::evaluate::FunctionRef<Fortran::evaluate::Type<Fortran::common::TypeCategory::Integer,1>>::Fold(Fortran::evaluate::FoldingContext
&)': no suitable definition provided for explicit template instantiation
request

Some methods seem to not have any definitions. Seems they are never used,
hence it still works.


flang\include\flang\Evaluate\constant.h(102): error C2664:
'std::vector<Fortran::evaluate::value::Complex<Fortran::evaluate::value::Real<Fortran::evaluate::value::Integer<16,false,16,unsigned
short,unsigned
int>,8>>,std::allocator<Fortran::evaluate::value::Complex<Fortran::evaluate::value::Real<Fortran::evaluate::value::Integer<16,false,16,unsigned
short,unsigned int>,8>>>>::vector(std::initializer_list<_Ty>,const _Alloc
&)': cannot convert argument 1 from 'initializer list' to
'std::initializer_list<_Ty>'

Splitting the member initializer to the constructor body and making it more
explicit works:

--- a/flang/include/flang/Evaluate/constant.h
+++ b/flang/include/flang/Evaluate/constant.h
@@ -98,8 +98,11 @@ public:
   template <typename A>
   ConstantBase(const A &x, Result res = Result{}) : result_{res},
values_{x} {}
   template <typename A, typename = common::NoLvalue<A>>
-  ConstantBase(A &&x, Result res = Result{})
-      : result_{res}, values_{std::move(x)} {}
+  ConstantBase(A &&x, Result res /*= Result{}*/, int a, int b, int c)
+      : result_{res} {
+    Element y = std::move(x);
+    values_.push_back(std::move(y));
+  }



flang\include\flang\Evaluate\expression.h(751): error C3200:
'Fortran::evaluate::Expr<Fortran::evaluate::SomeKind<Fortran::common::TypeCategory::Logical>>':
invalid template argument for template parameter 'F', expected a class
template

`Expr` inside the definition of `Expr` may be interpreted as the current
instantiation (Same reason why template parameters do not need to be
repeated for declaring its constructor). Declaring a forward declaration
under a different name helps:

--- a/flang/include/flang/Evaluate/expression.h
+++ b/flang/include/flang/Evaluate/expression.h
@@ -736,6 +739,9 @@ public:
       u;
 };

+template <typename T>
+using OtherExpr = Expr<T>;
+
 // A polymorphic expression of known intrinsic type category, but dynamic
 // kind, represented as a discriminated union over Expr<Type<CAT, K>>
 // for each supported kind K in the category.
@@ -745,7 +751,7 @@ public:
   using Result = SomeKind<CAT>;
   EVALUATE_UNION_CLASS_BOILERPLATE(Expr)
   int GetKind() const;
-  common::MapTemplate<Expr, CategoryTypes<CAT>> u;
+  common::MapTemplate<OtherExpr, CategoryTypes<CAT>> u;
 };


flang\lib\Evaluate\characteristics.cpp: error C2672: '_C_invoke': no
matching overloaded function found

The `template <int KIND> Result operator()(const TypeParamInquiry<KIND> &)`
is defined in evaluate::GetShapeHelper as well as in evaluate::Traverse
that GetShapHelper derives from (indirectly through evaluate::AnyTraverse).
Additionally, GetShapeHelper declares `using Base::operator();`, i.e.
explicitly importing the overloads from the base class (Base is AnyTraverse
which does not declare any operator() itself but inherits from Traverse).
Therefore there is an obvious ambiguity when calling
GetShapeHelper::operator()(TypeParamInquiry) which stops a std::variant
from forwarding a call to operator().

I removed the overloads in the derived classes (same issue applies not only
to GetShapeHelper, but other classes as well). I assume this is responsible
for 3 fails in check-clang.

--- a/flang/include/flang/Evaluate/shape.h
+++ b/flang/include/flang/Evaluate/shape.h
@@ -93,9 +93,6 @@ public:

   Result operator()(const ImpliedDoIndex &) const { return Scalar(); }
   Result operator()(const DescriptorInquiry &) const { return Scalar(); }
-  template <int KIND> Result operator()(const TypeParamInquiry<KIND> &)
const {
-    return Scalar();
-  }
   Result operator()(const BOZLiteralConstant &) const { return Scalar(); }
   Result operator()(const StaticDataObject::Pointer &) const {
     return Scalar();


flang\include\flang/Lower/PFTBuilder.h(59): error C2665:
'std::variant<Fortran::common::Reference<Fortran::lower::pft::Program>,Fortran::common::Reference<Fortran::lower::pft::ModuleLikeUnit>,Fortran::common::Reference<Fortran::lower::pft::FunctionLikeUnit>,Fortran::common::Reference<Fortran::lower::pft::Evaluation>>::variant':
none of the 2 overloads could convert all the argument types

Msvc is confused whether to use std::variant's copy or move-constructur.
Unfortunately, ReferenceVariantBase, std::variant and common::Reference all
can only be set at initialization such that I did not manage to decompose
the conversions into separate steps.
The only way I managed to still compile this is by only accepting the
variants of a conversion-constructor and do the decomposition at the call
sites. Two of 4 of these call sites in this patch:

--- a/flang/include/flang/Lower/PFTBuilder.h
+++ b/flang/include/flang/Lower/PFTBuilder.h
@@ -55,8 +55,7 @@ public:
   using Ref = common::Reference<BaseType<B>>;

   ReferenceVariantBase() = delete;
-  template <typename B>
-  ReferenceVariantBase(B &b) : u{Ref<B>{b}} {}
+  ReferenceVariantBase(std::variant<Ref<A>...> b): u(b) { }

   template <typename B>
   constexpr BaseType<B> &get() const {

--- a/flang/lib/Lower/PFTBuilder.cpp
+++ b/flang/lib/Lower/PFTBuilder.cpp
@@ -65,7 +65,18 @@ class PFTBuilder {
 public:
   PFTBuilder(const semantics::SemanticsContext &semanticsContext)
       : pgm{std::make_unique<lower::pft::Program>()},
-        parentVariantStack{*pgm.get()}, semanticsContext{semanticsContext}
{}
+        semanticsContext{semanticsContext} {
+    lower::pft::Program &x = *pgm.get();
+    common::Reference<lower::pft::Program> z{x};
+    std::variant<
+      common::Reference<lower::pft::Program>,
+      common::Reference<lower::pft::ModuleLikeUnit>,
+      common::Reference<lower::pft::FunctionLikeUnit>,
+      common::Reference<lower::pft::Evaluation>
+    > w = z;
+    lower::pft::ParentVariant y = w;
+    parentVariantStack.push_back(y);
+  }

   /// Get the result
   std::unique_ptr<lower::pft::Program> result() { return std::move(pgm); }
@@ -905,11 +916,17 @@ private:
 template <typename A, typename T>
 static lower::pft::FunctionLikeUnit::FunctionStatement
 getFunctionStmt(const T &func) {
-  return std::get<parser::Statement<A>>(func.t);
+  using StmtTy = parser::Statement<A>;
+  const StmtTy &Tmp = std::get<StmtTy>(func.t);
+  lower::pft::FunctionLikeUnit::FunctionStatement Result{ Tmp };
+  return Result;
 }


expr-parsers.cpp: error C2338: tuple index out of bounds

For some reason, Msvc still wants to build the AST for the `constexpr
(sizeof...(PARSER) == 1)` body when the variadic template parameter PARSER
is empty. As a result, std::get<0> fails.

The patch below obviously is incorrect, but I did not want to spend too
much time finding a proper fix. Maybe hiding the `if constexpr` body in
another function call?

--- a/flang/lib/Parser/basic-parsers.h
+++ b/flang/lib/Parser/basic-parsers.h
@@ -729,13 +729,6 @@ public:
       return RESULT{};
     } else {
       if constexpr (sizeof...(PARSER) == 1) {
-        if constexpr (std::is_same_v<Success, typename
PARSER::resultType...>) {
-          if (std::get<0>(parsers_).Parse(state)) {
-            return RESULT{};
-          }
-        } else if (auto arg{std::get<0>(parsers_).Parse(state)}) {
-          return RESULT{std::move(*arg)};
-        }
       } else {
         ApplyArgs<PARSER...> results;
         using Sequence = std::index_sequence_for<PARSER...>;


flang\lib\Parser\basic-parsers.h(790): error C2672: 'applyFunction': no
matching overloaded function found

Msvc has trouble deriving the template arguments for the applyFunction
call. Explicitly specifying the template argument and decomposing implicit
conversions help here.

--- a/flang/lib/Parser/basic-parsers.h
+++ b/flang/lib/Parser/basic-parsers.h
@@ -782,8 +775,17 @@ public:
   constexpr NonemptySeparated(const NonemptySeparated &) = default;
   constexpr NonemptySeparated(PA p, PB sep) : parser_{p}, separator_{sep}
{}
   std::optional<resultType> Parse(ParseState &state) const {
-    return applyFunction(prepend<paType>, parser_, many(separator_ >>
parser_))
-        .Parse(state);
+    std::list<paType> (*arg1)(paType &&, std::list<paType> &&) =
prepend<paType>;
+    const PA &arg2 = parser_;
+    using SeqPA = SequenceParser<PB, PA>;
+    SeqPA Seq = separator_ >> parser_;
+    using ManyPA = ManyParser<SeqPA>;
+    ManyPA arg3 = many<SeqPA>(Seq);
+    using RESULT = std::list<paType>;
+    using PARSER1 = PA;
+    using PARSER2 = ManyParser<SeqPA>;
+    auto result{ applyFunction<RESULT, PA, ManyPA > (arg1, arg2, arg3) };
+    return result.Parse(state);
   }

flang\lib\Parser\basic-parsers.h(830): error C2131: expression did not
evaluate to a constant / note: failure was caused by call of undefined
function or one not declared 'constexpr'

Msvc has trouble declaring a class and defining a constexpr symbol in the
same declarator. Writing the items in different declarators helps.

This pattern is used for multiple singleton classes. One of the
occurrences is fixed by the patch below.

--- a/flang/lib/Parser/basic-parsers.h
+++ b/flang/lib/Parser/basic-parsers.h
@@ -800,13 +802,14 @@ inline constexpr auto nonemptySeparated(PA p, PB sep)
{
 // must discard its result in order to be compatible in type with other
 // parsers in an alternative, e.g. "x >> ok || y >> ok" is type-safe even
 // when x and y have distinct result types.
-constexpr struct OkParser {
+struct OkParser {
   using resultType = Success;
   constexpr OkParser() {}
   static constexpr std::optional<Success> Parse(ParseState &) {
     return Success{};
   }
-} ok;
+};
+constexpr OkParser ok;

flang\lib\Parser\expr-parsers.cpp(383): error C2672: 'applyLambda': no
matching overloaded function found

Another case where Msvc could not derive template arguments.

--- a/flang/lib/Parser/expr-parsers.cpp
+++ b/flang/lib/Parser/expr-parsers.cpp
@@ -371,8 +379,13 @@ template <> std::optional<Expr>
Parser<Expr>::Parse(ParseState &state) {
           return Expr{Expr::DefinedBinary(
               std::move(op), std::move(result).value(), std::move(right))};
         }};
+    ApplicableFunctionObject<Expr, Parser<DefinedOpName>, Level5Expr> arg1
= defBinOp;
+    Parser<DefinedOpName> arg2 = definedOpName;
+    Level5Expr arg3 = level5Expr;
+    auto lr = applyLambda<Expr, Parser<DefinedOpName>, Level5Expr>(arg1,
arg2, arg3);
     auto more{
-        attempt(sourced(applyLambda(defBinOp, definedOpName,
level5Expr)))};
+        attempt(sourced(lr))};
     while (std::optional<Expr> next{more.Parse(state)}) {
       result = std::move(next);
       result->source.ExtendToCover(source);


Use of POSIX functions in f18.cpp / f18-parse-demo.cpp

The following headers do not exist on Windows:

#include <sys/wait.h>
#include <unistd.h>

The following functions have platform-independent versions in LLVMSupport:

unlink -> llvm::sys::fs::remove

std::snprintf(tmpSourcePath, sizeof tmpSourcePath, "/tmp/f18-%lx.f90",
      static_cast<unsigned long>(getpid()));
llvm::raw_fd_ostream tmpSource(tmpSourcePath, EC, llvm::sys::fs::F_None);
 -->
std::error_code EC = llvm::sys::fs::createUniqueFile("f18-%%%%.f90", fd,
tmpSourcePath);

execvp -> llvm::sys::ExecuteAndWait

fork and wait (in ParentProcess) -> Call of llvm::sys::ExecuteAndWait will
be blocking:

--- a/flang/tools/f18/f18.cpp
+++ b/flang/tools/f18/f18.cpp
@@ -358,14 +354,11 @@ std::string CompileFortran(std::string path,
Fortran::parser::Options options,

 std::string CompileOtherLanguage(std::string path, DriverOptions &driver) {
   std::string relo{RelocatableName(driver, path)};
-  if (ParentProcess()) {
+  RunOtherCompiler(driver, path.data(), relo.data());
     if (!driver.compileOnly && driver.outputPath.empty()) {
       filesToDelete.push_back(relo);
     }
     return relo;
-  }
-  RunOtherCompiler(driver, path.data(), relo.data());
-  return {};
 }




Michael
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/flang-dev/attachments/20200722/8f88db8b/attachment-0001.html>


More information about the flang-dev mailing list