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