xref: /petsc/config/BuildSystem/config/compilerOptions.py (revision b78ea06ca2360cbc3704cad5b3ce835b2b1ccf4f)
1import config.base
2import os
3import re
4import nargs
5
6re_win32fe_cl    = re.compile(r'win32fe[\s_]+cl')
7re_win32fe_icl   = re.compile(r'win32fe[\s_]+ic[lx]')
8re_win32fe_ifort = re.compile(r'win32fe[\s_]+if(x|ort)')
9
10class CompilerOptions(config.base.Configure):
11  def getCFlags(self, compiler, bopt, language):
12    import config.setCompilers
13
14    if language == 'C':
15      if [s for s in ['mpicc','mpiicc','mpiicx'] if os.path.basename(compiler).find(s)>=0]:
16        try:
17          output = self.executeShellCommand(compiler + ' -show', log = self.log)[0]
18          self.framework.addMakeMacro('MPICC_SHOW',output.strip().replace('\n','\\\\n').replace('"','\\"'))
19        except:
20          self.framework.addMakeMacro('MPICC_SHOW',"Unavailable")
21      else:
22        self.framework.addMakeMacro('MPICC_SHOW',"Unavailable")
23
24    flags = []
25    # GNU gcc
26    if config.setCompilers.Configure.isGNU(compiler, self.log) or config.setCompilers.Configure.isClang(compiler, self.log):
27      if bopt == '':
28        flags.extend(['-Wall', '-Wwrite-strings', '-Wno-unknown-pragmas'])
29        if config.setCompilers.Configure.isClang(compiler, self.log):
30          # gcc does not support -Wno-implicit-float-conversion so -Wconversion is always noisy
31          flags.extend(['-Wconversion', '-Wno-sign-conversion', '-Wno-float-conversion', '-Wno-implicit-float-conversion', '-Qunused-arguments'])
32          if config.setCompilers.Configure.isDarwinCatalina(self.log):
33            flags.extend(['-fno-stack-check'])
34        else:
35          flags.extend(['-Wno-lto-type-mismatch'])
36          if config.setCompilers.Configure.isGcc110plus(compiler, self.log):
37            flags.extend(['-Wno-stringop-overflow'])
38          if config.setCompilers.Configure.isARM(self.log):
39            flags.extend(['-mfp16-format=ieee']) #  ARM for utilizing 16 bit storage of floating point
40        # skip -fstack-protector for brew gcc - as this gives SEGV
41        if not ((config.setCompilers.Configure.isDarwin(self.log) or config.setCompilers.Configure.isMINGW(compiler, self.log)) and config.setCompilers.Configure.isGNU(compiler, self.log)):
42          flags.extend(['-fstack-protector'])
43        if self.argDB['with-visibility']:
44          flags.extend(['-fvisibility=hidden'])
45        if language == 'CUDA':
46          flags.extend(['-x cuda'])
47      elif bopt == 'g':
48        flags.extend(['-O0','-g3'])
49      elif bopt == 'O':
50        flags.append('-g')
51        if config.setCompilers.Configure.isClang(compiler, self.log):
52          flags.append('-O3')
53        else:
54          flags.append('-O')
55    else:
56      # Linux Intel
57      if config.setCompilers.Configure.isIntel(compiler, self.log) and not re_win32fe_icl.search(compiler):
58        if bopt == '':
59          flags.extend(['-wd1572', '-Wno-unknown-pragmas'])
60          # next one fails in OpenMP build and we don't use it anyway so remove
61          # flags.append('-Qoption,cpp,--extended_float_type')
62        elif bopt == 'g':
63          flags.extend(['-g','-O0'])
64        elif bopt == 'O':
65          flags.append('-g')
66          flags.append('-O3')
67      # Windows Intel
68      elif re_win32fe_icl.search(compiler):
69        if bopt == '':
70          flags.extend(['-Qstd=c99'])
71          if self.argDB['with-shared-libraries']:
72            flags.extend(['-MD'])
73          else:
74            flags.extend(['-MT'])
75        elif bopt == 'g':
76          flags.extend(['-Z7','-Od'])
77        elif bopt == 'O':
78          flags.extend(['-O3', '-QxW'])
79      # Windows Microsoft
80      elif re_win32fe_cl.search(compiler):
81        if bopt == '':
82          dir(self)
83          # cause compiler to generate only a single copy of static strings; needed usage of __func__ in PETSc
84          flags.extend(['-GF'])
85          if self.argDB['with-shared-libraries']:
86            flags.extend(['-MD','-wd4996'])
87          else:
88            flags.extend(['-MT','-wd4996'])
89          # cause compiler to handle preprocessor per the standard https://docs.microsoft.com/en-us/cpp/build/reference/zc-preprocessor?view=msvc-170
90          flags.extend(['-Zc:preprocessor ','-experimental:preprocessor'])
91        elif bopt == 'g':
92          flags.extend(['-Z7','-Od'])
93        elif bopt == 'O':
94          flags.extend(['-O2', '-QxW'])
95      elif config.setCompilers.Configure.isNVCC(compiler, self.log):
96        if bopt == '':
97          if self.argDB['with-visibility']:
98            flags.append(('-Xcompiler', '-fvisibility=hidden'))
99        elif bopt == 'g':
100          # nvcc --help says:
101          #  -g : Generate debug information for host code.
102          #  -G : Generate debug information for device code. Turns off all optimizations. Don't use for profiling; use -lineinfo instead.
103          #  -lineinfo: Generate line-number information for device code.
104          # We use '-g -lineinfo' to generate debug info for both host and device code in *.cu files.
105          # If users want to turn off all optimizations, they can use --CUDAOPTFLAGS="-G".
106          flags.extend(['-g', '-lineinfo'])
107        elif bopt == 'O':
108          flags.append('-O3')
109      # NEC
110      elif config.setCompilers.Configure.isNEC(compiler, self.log):
111        if bopt == '':
112          flags.extend(['-Wall', '-fdiag-vector=0', '-fdiag-parallel=0', '-fdiag-inline=0'])
113        elif bopt == 'O':
114          flags.append('-O1') # defaults to O2, which is quite buggy (as of version 3.3.1)
115        elif bopt == 'g':
116          flags.append('-g')
117          flags.append('-traceback=verbose')
118          flags.append('-O0')
119    # Generic
120    if not len(flags):
121      if bopt == 'g':
122        flags.extend(['-g','-O0'])
123      elif bopt == 'O':
124        flags.append('-O')
125    return flags
126
127  def getCxxFlags(self, compiler, bopt, language):
128    import config.setCompilers
129
130    if [s for s in ['mpiCC','mpic++','mpicxx','mpiicxx','mpiicpc','mpiicpx'] if os.path.basename(compiler).find(s)>=0]:
131      try:
132        output   = self.executeShellCommand(compiler+' -show', log = self.log)[0]
133        self.framework.addMakeMacro('MPICXX_SHOW',output.strip().replace('\n','\\\\n'))
134      except:
135        self.framework.addMakeMacro('MPICXX_SHOW',"Unavailable")
136    else:
137      self.framework.addMakeMacro('MPICXX_SHOW',"Unavailable")
138
139    flags = []
140    # GNU g++
141    if config.setCompilers.Configure.isGNU(compiler, self.log) or config.setCompilers.Configure.isClang(compiler, self.log):
142      if bopt == '':
143        flags.extend(['-Wall', '-Wwrite-strings', '-Wno-strict-aliasing', '-Wno-unknown-pragmas'])
144        if config.setCompilers.Configure.isGNU(compiler, self.log):
145          flags.extend(['-Wno-psabi', '-Wno-lto-type-mismatch'])
146        if not any([
147            # skip -fstack-protector for brew gcc - as this gives SEGV
148            (config.setCompilers.Configure.isDarwin(self.log) or config.setCompilers.Configure.isMINGW(compiler, self.log)) and config.setCompilers.Configure.isGNU(compiler, self.log),
149            # hipcc for ROCm-4.0 crashes on some source files with -fstack-protector
150            config.setCompilers.Configure.isHIP(compiler, self.log),
151        ]):
152          flags.extend(['-fstack-protector'])
153        if config.setCompilers.Configure.isDarwinCatalina(self.log) and config.setCompilers.Configure.isClang(compiler, self.log):
154          flags.extend(['-fno-stack-check'])
155        # The option below would prevent warnings about compiling C as C++ being deprecated, but it causes Clang to SEGV, http://llvm.org/bugs/show_bug.cgi?id=12924
156        # flags.extend([('-x','c++')])
157        if self.argDB['with-visibility']:
158          flags.extend(['-fvisibility=hidden'])
159      elif bopt in ['g']:
160        # -g3 causes an as SEGV on OSX
161        if config.setCompilers.Configure.isHIP(compiler, self.log):
162          # HIP can cause buggy code with -O0
163          flags.extend(['-g'])
164        else:
165          flags.extend(['-O0','-g'])
166      elif bopt in ['O']:
167        flags.append('-g')
168        if 'USER' in os.environ:
169          if config.setCompilers.Configure.isClang(compiler, self.log):
170            flags.append('-O3')
171          else:
172            flags.append('-O')
173    # IBM
174    elif compiler.find('mpCC') >= 0 or compiler.find('xlC') >= 0:
175      if bopt == '':
176        flags.append('-qrtti=dyna')  # support dynamic casts in C++
177      elif bopt in ['g']:
178        flags.extend(['-g','-O0'])
179      elif bopt in ['O']:
180        flags.append('-O')
181    else:
182      # Linux Intel
183      if config.setCompilers.Configure.isIntel(compiler, self.log) and not re_win32fe_icl.search(compiler):
184        if bopt == '':
185          flags.append('-wd1572')
186        elif bopt == 'g':
187          flags.extend(['-g','-O0'])
188        elif bopt == 'O':
189          flags.append('-g')
190          flags.append('-O3')
191      # Windows Intel
192      elif re_win32fe_icl.search(compiler):
193        if bopt == '':
194          if self.argDB['with-shared-libraries']:
195            flags.extend(['-MD','-GR','-EHsc'])
196          else:
197            flags.extend(['-MT','-GR','-EHsc']) # removing GX in favor of EHsc
198        elif bopt in ['g']:
199          flags.extend(['-Z7','-Od'])
200        elif bopt in ['O']:
201          flags.extend(['-O3', '-QxW'])
202      # Windows Microsoft
203      elif re_win32fe_cl.search(compiler):
204        if bopt == '':
205          # cause compiler to generate only a single copy of static strings; needed usage of __func__ in PETSc
206          flags.extend(['-GF'])
207          if self.argDB['with-shared-libraries']:
208            flags.extend(['-MD','-GR','-EHsc'])
209          else:
210            flags.extend(['-MT','-GR','-EHsc']) # removing GX in favor of EHsc
211          # cause compiler to handle preprocessor per the standard https://docs.microsoft.com/en-us/cpp/build/reference/zc-preprocessor?view=msvc-170
212          flags.extend(['-Zc:preprocessor ','-experimental:preprocessor'])
213          # cause compiler to emit the correct value for __cplusplus macro as per the
214          # standard
215          # https://learn.microsoft.com/en-us/cpp/build/reference/zc-cplusplus?view=msvc-170
216          flags.extend(['-Zc:__cplusplus'])
217        elif bopt == 'g':
218          flags.extend(['-Z7','-Zm200','-Od'])
219        elif bopt == 'O':
220          flags.extend(['-O2','-QxW','-Zm200'])
221      # NEC
222      elif config.setCompilers.Configure.isNEC(compiler, self.log):
223        if bopt == '':
224          flags.extend(['-Wall', '-fdiag-vector=0', '-fdiag-parallel=0', '-fdiag-inline=0'])
225        elif bopt == 'O':
226          flags.append('-O1') # defaults to O2, which is quite buggy (as of version 3.3.1)
227        elif bopt == 'g':
228          flags.append('-g')
229          flags.append('-traceback=verbose')
230          flags.append('-O0')
231    # Generic
232    if not len(flags):
233      if bopt in ['g']:
234        if config.setCompilers.Configure.isHIP(compiler, self.log):
235          # HIP can cause buggy code with -O0
236          flags.extend(['-g'])
237        else:
238          flags.extend(['-g','-O0'])
239      elif bopt in ['O']:
240        flags.append('-O')
241    return flags
242
243  def getFortranFlags(self, compiler, bopt):
244    if [s for s in ['mpif77','mpif90','mpifort','mpiifort','mpiifx'] if os.path.basename(compiler).find(s)>=0]:
245      try:
246        output   = self.executeShellCommand(compiler+' -show', log = self.log)[0]
247        self.framework.addMakeMacro('MPIFC_SHOW',output.strip().replace('\n','\\\\n'))
248      except:
249        self.framework.addMakeMacro('MPIFC_SHOW',"Unavailable")
250    else:
251      self.framework.addMakeMacro('MPIFC_SHOW',"Unavailable")
252
253    flags = []
254    if config.setCompilers.Configure.isGNU(compiler, self.log):
255      if bopt == '':
256        flags.extend(['-Wall', '-ffree-line-length-none', '-ffree-line-length-0', '-Wno-lto-type-mismatch'])
257        if config.setCompilers.Configure.isGfortran8plus(compiler, self.log):
258          flags.extend(['-Wno-unused-dummy-argument']) # Silence warning because dummy parameters are sometimes necessary
259        if config.setCompilers.Configure.isMINGW(compiler, self.log):
260          flags.extend(['-fallow-invalid-boz'])
261      elif bopt == 'g':
262        # g77 3.2.3 preprocesses the file into nothing if we give -g3
263        flags.extend(['-g','-O0'])
264      elif bopt == 'O':
265        flags.append('-g')
266        flags.extend(['-O'])
267    else:
268      # Portland Group Fortran 90
269      if config.setCompilers.Configure.isPGI(compiler, self.log):
270        self.framework.addDefine('PETSC_HAVE_PGF90_COMPILER','1')
271        if bopt == '':
272          flags.append('-Mfree')
273        elif bopt == 'O':
274          flags.extend(['-fast', '-Mnoframe'])
275      # Linux Intel
276      if config.setCompilers.Configure.isIntel(compiler, self.log) and not re_win32fe_ifort.search(compiler):
277        if bopt == '':
278          flags.append('-fpscomp logicals') # enforce Fortran logical to be compatible with C
279        elif bopt == 'g':
280          flags.extend(['-O0','-g'])
281        elif bopt == 'O':
282          flags.append('-g')
283          flags.append('-O3')
284      # Windows Intel
285      elif re_win32fe_ifort.search(compiler):
286        if bopt == '':
287          flags.append('-fpscomp:logicals') # enforce Fortran logical to be compatible with C
288          if self.argDB['with-shared-libraries']:
289            flags.extend(['-MD'])
290          else:
291            flags.extend(['-MT'])
292        elif bopt == 'g':
293          flags.extend(['-Z7','-Od'])
294        elif bopt == 'O':
295          flags.extend(['-O3', '-QxW'])
296      # NEC
297      elif config.setCompilers.Configure.isNEC(compiler, self.log):
298        if bopt == '':
299          flags.extend(['-Wall', '-fdiag-vector=0', '-fdiag-parallel=0', '-fdiag-inline=0'])
300        elif bopt == 'O':
301          flags.append('-O2')
302          flags.append('-fno-reciprocal-math')
303        elif bopt == 'g':
304          flags.append('-g')
305          flags.append('-traceback=verbose')
306          flags.append('-O0')
307      # NVIDIA
308      elif config.setCompilers.Configure.isNVC(compiler, self.log):
309        flags.append('-Munixlogical') # enforce Fortran logical to be compatible with C
310        if bopt == 'g':
311          flags.extend(['-g','-O0'])
312        elif bopt == 'O':
313          flags.append('-O')
314    # Generic
315    if not len(flags):
316      if bopt == 'g':
317        flags.extend(['-g','-O0'])
318      elif bopt == 'O':
319        flags.append('-O')
320    return flags
321
322  def getCompilerFlags(self, language, compiler, bopt):
323    if bopt == 'gcov':
324      raise RuntimeError('Internal error! bopt = gcov is deprecated')
325
326    flags = []
327    if language == 'C' or language == 'CUDA':
328      flags = self.getCFlags(compiler, bopt, language)
329    elif language == 'Cxx' or language == 'HIP' or language == 'SYCL':
330      flags = self.getCxxFlags(compiler, bopt, language)
331    elif language in ['Fortran', 'FC']:
332      flags = self.getFortranFlags(compiler, bopt)
333    if bopt == 'O':
334      flat_flags  = (' '.join(f) if isinstance(f, (list, tuple)) else f for f in flags)
335      fopt_prefix = 'F' if language in ('Fortran', 'FC') else language.upper()
336      self.logPrintWarning('Using default ' + language + ' optimization flags "'+' '.join(flat_flags)+'". You might consider manually setting optimal optimization flags for your system with ' + fopt_prefix + 'OPTFLAGS="optimization flags" see config/examples/arch-*-opt.py for examples')
337    return flags
338
339  def getCompilerVersion(self, language, compiler):
340    if compiler is None:
341      raise RuntimeError('Invalid compiler for version determination')
342    version = 'Unknown'
343    try:
344      if language == 'C' or language == 'CUDA':
345        if compiler.endswith('xlc') or compiler.endswith('mpcc'):
346          flags = "lslpp -L vac.C | grep vac.C | awk '{print $2}'"
347        else:
348          flags = compiler+' --version'
349      elif language == 'Cxx' or language == 'HIP' or language == 'SYCL':
350        if compiler.endswith('xlC') or compiler.endswith('mpCC'):
351          flags = "lslpp -L vacpp.cmp.core  | grep vacpp.cmp.core  | awk '{print $2}'"
352        else:
353          flags = compiler+' --version'
354      elif language in ['Fortran', 'FC']:
355        if compiler.endswith('xlf') or compiler.endswith('xlf90'):
356          flags = "lslpp -L xlfcmp | grep xlfcmp | awk '{print $2}'"
357        else:
358          flags = compiler+' --version'
359      try:
360        (output, error, status) = config.base.Configure.executeShellCommand(flags, log = self.log)
361      except:
362        flags = compiler+' -v'
363        (output, error, status) = config.base.Configure.executeShellCommand(flags, log = self.log)
364        output = error + output
365      if not status:
366        if compiler.find('win32fe') > -1:
367          version = '\\n'.join(output.split('\n')[0:2])
368          version = version.replace('\r','')
369        else:
370          #PGI/Windows writes an empty '\r\n' on the first line of output
371          if output.count('\n') > 1 and output.split('\n')[0] == '\r':
372            version = output.split('\r\n')[1]
373          #NVCC
374          elif output.find('Cuda compiler driver') >= 0:
375            version = output.split('\n')[3]
376          else:
377            version = output.split('\n')[0]
378
379    except RuntimeError as e:
380      self.logWrite('Could not determine compiler version: '+str(e))
381    self.logWrite('getCompilerVersion: '+str(compiler)+' '+str(version)+'\n')
382    self.framework.addMakeMacro(language+'_VERSION',version)
383    return version
384