1 /**
2  * DMD-specific parameters.
3  *
4  * Copyright:   Copyright (C) 1999-2023 by The D Language Foundation, All Rights Reserved
5  * Authors:     $(LINK2 https://www.digitalmars.com, Walter Bright)
6  * License:     $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
7  * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/dmdparams.d, _dmdparams.d)
8  * Documentation:  https://dlang.org/phobos/dmd_dmdparams.html
9  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/dmdparams.d
10  */
11 
12 module dmd.dmdparams;
13 
14 import dmd.target;
15 
16 /// Position Indepent Code setting
17 enum PIC : ubyte
18 {
19     fixed,              /// located at a specific address
20     pic,                /// Position Independent Code
21     pie,                /// Position Independent Executable
22 }
23 
24 struct DMDparams
25 {
26     bool alwaysframe;       // always emit standard stack frame
27     ubyte dwarf;            // DWARF version
28     bool map;               // generate linker .map file
29     bool vasm;              // print generated assembler for each function
30 
31     bool dll;               // generate shared dynamic library
32     bool lib;               // write library file instead of object file(s)
33     bool link = true;       // perform link
34     bool oneobj;            // write one object file instead of multiple ones
35 
36     bool optimize;          // run optimizer
37     bool nofloat;           // code should not pull in floating point support
38     PIC pic = PIC.fixed;    // generate fixed, pic or pie code
39     bool stackstomp;        // add stack stomping code
40 
41     bool symdebug;          // insert debug symbolic information
42     bool symdebugref;       // insert debug information for all referenced types, too
43 
44     const(char)[] defaultlibname;   // default library for non-debug builds
45     const(char)[] debuglibname;     // default library for debug builds
46     const(char)[] mscrtlib;         // MS C runtime library
47 
48     // Hidden debug switches
49     bool debugb;
50     bool debugc;
51     bool debugf;
52     bool debugr;
53     bool debugx;
54     bool debugy;
55 }
56 
57 /**
58  Sets CPU Operating System, and optionally C/C++ runtime environment from the given triple
59  e.g.
60     x86_64+avx2-apple-darwin20.3.0
61     x86-unknown-linux-musl-clang
62     x64-windows-msvc
63     x64-pc-windows-msvc
64  */
65 struct Triple
66 {
67     private const(char)[] source;
68     CPU               cpu;
69     bool              is64bit;
70     bool              isLP64;
71     Target.OS         os;
72     ubyte             osMajor;
73     TargetC.Runtime   cenv;
74     TargetCPP.Runtime cppenv;
75 
76     this(const(char)* _triple)
77     {
78         import dmd.root.string : toDString, toCStringThen;
79         const(char)[] triple = _triple.toDString();
80         const(char)[] next()
81         {
82             size_t i = 0;
83             const tmp = triple;
84             while (triple.length && triple[0] != '-')
85             {
86                 triple = triple[1 .. $];
87                 ++i;
88             }
89             if (triple.length && triple[0] == '-')
90             {
91                 triple = triple[1 .. $];
92             }
93             return tmp[0 .. i];
94         }
95 
96         parseArch(next);
97         const(char)[] vendorOrOS = next();
98         const(char)[] _os;
99         if (tryParseVendor(vendorOrOS))
100             _os = next();
101         else
102             _os = vendorOrOS;
103         os = parseOS(_os, osMajor);
104 
105         const(char)[] _cenv = next();
106         if (_cenv.length)
107             cenv = parseCEnv(_cenv);
108         else if (this.os == Target.OS.Windows)
109             cenv = TargetC.Runtime.Microsoft;
110         const(char)[] _cppenv = next();
111         if (_cppenv.length)
112             cppenv = parseCPPEnv(_cppenv);
113         else if (this.os == Target.OS.Windows)
114             cppenv = TargetCPP.Runtime.Microsoft;
115     }
116     private extern(D):
117 
118     void unknown(const(char)[] unk, const(char)* what)
119     {
120         import dmd.errors : error;
121         import dmd.root.string : toCStringThen;
122         import dmd.location;
123         unk.toCStringThen!(p => error(Loc.initial,"unknown %s `%s` for `-target`", what, p.ptr));
124     }
125 
126     void parseArch(const(char)[] arch)
127     {
128         bool matches(const(char)[] str)
129         {
130             import dmd.root.string : startsWith;
131             if (!arch.ptr.startsWith(str))
132                 return false;
133             arch = arch[str.length .. $];
134             return true;
135         }
136 
137         if (matches("x86_64"))
138             is64bit = true;
139         else if (matches("x86"))
140             is64bit = false;
141         else if (matches("x64"))
142             is64bit = true;
143         else if (matches("x32"))
144         {
145             is64bit = true;
146             isLP64 = false;
147         }
148         else
149             return unknown(arch, "architecture");
150 
151         if (!arch.length)
152             return;
153 
154         switch (arch)
155         {
156             case "+sse2": cpu = CPU.sse2; break;
157             case "+avx":  cpu = CPU.avx;  break;
158             case "+avx2": cpu = CPU.avx2; break;
159             default:
160                 unknown(arch, "architecture feature");
161         }
162     }
163 
164     // try parsing vendor if present
165     bool tryParseVendor(const(char)[] vendor)
166     {
167         switch (vendor)
168         {
169             case "unknown": return true;
170             case "apple":   return true;
171             case "pc":      return true;
172             case "amd":     return true;
173             default:        return false;
174         }
175     }
176 
177     /********************************
178      * Parse OS and osMajor version number.
179      * Params:
180      *  _os = string to check for operating system followed by version number
181      *  osMajor = set to version number (if any), otherwise set to 0.
182      *            Set to 255 if version number is 255 or larger and error is generated
183      * Returns:
184      *  detected operating system, Target.OS.none if none
185      */
186     Target.OS parseOS(const(char)[] _os, out ubyte osMajor)
187     {
188         import dmd.errors : error;
189         import dmd.location;
190 
191         bool matches(const(char)[] str)
192         {
193             import dmd.root.string : startsWith;
194             if (!_os.ptr.startsWith(str))
195                 return false;
196             _os = _os[str.length .. $];
197             return true;
198         }
199         Target.OS os;
200         if (matches("darwin"))
201             os = Target.OS.OSX;
202         else if (matches("dragonfly"))
203             os =  Target.OS.DragonFlyBSD;
204         else if (matches("freebsd"))
205             os =  Target.OS.FreeBSD;
206         else if (matches("openbsd"))
207             os =  Target.OS.OpenBSD;
208         else if (matches("linux"))
209             os =  Target.OS.linux;
210         else if (matches("windows"))
211             os =  Target.OS.Windows;
212         else
213         {
214             unknown(_os, "operating system");
215             return Target.OS.none;
216         }
217 
218         bool overflow;
219         auto major = parseNumber(_os, overflow);
220         if (overflow || major >= 255)
221         {
222             error(Loc.initial, "OS version overflowed max of 254");
223             major = 255;
224         }
225         osMajor = cast(ubyte)major;
226 
227         /* Note that anything after the number up to the end or '-',
228          * such as '.3.4.hello.betty', is ignored
229          */
230 
231         return os;
232     }
233 
234     /*******************************
235      * Parses a decimal number out of the str and returns it.
236      * Params:
237      *  str = string to parse the number from, updated to text after the number
238      *  overflow = set to true iff an overflow happens
239      * Returns:
240      *  parsed number
241      */
242     private pure static
243     uint parseNumber(ref const(char)[] str, ref bool overflow)
244     {
245         auto s = str;
246         ulong n;
247         while (s.length)
248         {
249             const c = s[0];
250             if (c < '0' || '9' < c)
251                 break;
252             n = n * 10 + (c - '0');
253             overflow |= (n > uint.max); // sticky overflow check
254             s = s[1 .. $];              // consume digit
255         }
256         str = s;
257         return cast(uint)n;
258     }
259 
260     TargetC.Runtime parseCEnv(const(char)[] cenv)
261     {
262         with (TargetC.Runtime) switch (cenv)
263         {
264             case "musl":         return Musl;
265             case "msvc":         return Microsoft;
266             case "bionic":       return Bionic;
267             case "digital_mars": return DigitalMars;
268             case "newlib":       return Newlib;
269             case "uclibc":       return UClibc;
270             case "glibc":        return Glibc;
271             default:
272             {
273                 unknown(cenv, "C runtime environment");
274                 return Unspecified;
275             }
276         }
277     }
278 
279     TargetCPP.Runtime parseCPPEnv(const(char)[] cppenv)
280     {
281         with (TargetCPP.Runtime) switch (cppenv)
282         {
283             case "clang":        return Clang;
284             case "gcc":          return Gcc;
285             case "msvc":         return Microsoft;
286             case "sun":          return Sun;
287             case "digital_mars": return DigitalMars;
288             default:
289             {
290                 unknown(cppenv, "C++ runtime environment");
291                 return Unspecified;
292             }
293         }
294     }
295 }
296 
297 void setTriple(ref Target target, const ref Triple triple)
298 {
299     target.cpu     = triple.cpu;
300     target.is64bit = triple.is64bit;
301     target.isLP64  = triple.isLP64;
302     target.os      = triple.os;
303     target.osMajor = triple.osMajor;
304     target.c.runtime   = triple.cenv;
305     target.cpp.runtime = triple.cppenv;
306 }
307 
308 /**
309 Returns: the final defaultlibname based on the command-line parameters
310 */
311 extern (D) const(char)[] finalDefaultlibname()
312 {
313     import dmd.globals : global;
314     return global.params.betterC ? null :
315         driverParams.symdebug ? driverParams.debuglibname : driverParams.defaultlibname;
316 }
317 
318 __gshared DMDparams driverParams = DMDparams.init;