1 /*
2  * Copyright (c) 2015 Andrew Kelley
3  *
4  * This file is part of zig, which is MIT licensed.
5  * See http://opensource.org/licenses/MIT
6  */
7 
8 // This file is the entry point for zig0, which is *only* used to build
9 // stage2, the self-hosted compiler, into an object file, which is then
10 // linked by the same build system (cmake) that linked this binary.
11 
12 #include "stage1.h"
13 #include "heap.hpp"
14 #include "stage2.h"
15 #include "target.hpp"
16 #include "error.hpp"
17 #include "util.hpp"
18 #include "buffer.hpp"
19 #include "os.hpp"
20 
21 // This is the only file allowed to include config.h because config.h is
22 // only produced when building with cmake. When using the zig build system,
23 // zig0.cpp is never touched.
24 #include "config.h"
25 
26 #include <stdio.h>
27 #include <string.h>
28 
print_error_usage(const char * arg0)29 static int print_error_usage(const char *arg0) {
30     fprintf(stderr, "See `%s --help` for detailed usage information\n", arg0);
31     return EXIT_FAILURE;
32 }
33 
print_full_usage(const char * arg0,FILE * file,int return_code)34 static int print_full_usage(const char *arg0, FILE *file, int return_code) {
35     fprintf(file,
36         "Usage: %s [options]            builds an object file\n"
37         "\n"
38         "Options:\n"
39         "  --color [auto|off|on]        enable or disable colored error messages\n"
40         "  --name [name]                override output name\n"
41         "  -femit-bin=[path]            Output machine code\n"
42         "  -fcompiler-rt                Always include compiler-rt symbols in output\n"
43         "  --pkg-begin [name] [path]    make pkg available to import and push current pkg\n"
44         "  --pkg-end                    pop current pkg\n"
45         "  -ODebug                      build with optimizations off and safety on\n"
46         "  -OReleaseFast                build with optimizations on and safety off\n"
47         "  -OReleaseSafe                build with optimizations on and safety on\n"
48         "  -OReleaseSmall               build with size optimizations on and safety off\n"
49         "  --single-threaded            source may assume it is only used single-threaded\n"
50         "  -dynamic                     create a shared library (.so; .dll; .dylib)\n"
51         "  --strip                      exclude debug symbols\n"
52         "  -target [name]               <arch>-<os>-<abi> see the targets command\n"
53         "  -mcpu [cpu]                  specify target CPU and feature set\n"
54         "  --verbose-tokenize           enable compiler debug output for tokenization\n"
55         "  --verbose-ast                enable compiler debug output for AST parsing\n"
56         "  --verbose-ir                 enable compiler debug output for Zig IR\n"
57         "  --verbose-llvm-ir            enable compiler debug output for LLVM IR\n"
58         "  --verbose-cimport            enable compiler debug output for C imports\n"
59         "  --verbose-llvm-cpu-features  enable compiler debug output for LLVM CPU features\n"
60         "\n"
61     , arg0);
62     return return_code;
63 }
64 
get_zig_os_type(ZigLLVM_OSType os_type)65 static Os get_zig_os_type(ZigLLVM_OSType os_type) {
66     switch (os_type) {
67         case ZigLLVM_UnknownOS:
68             return OsFreestanding;
69         case ZigLLVM_Ananas:
70             return OsAnanas;
71         case ZigLLVM_CloudABI:
72             return OsCloudABI;
73         case ZigLLVM_DragonFly:
74             return OsDragonFly;
75         case ZigLLVM_FreeBSD:
76             return OsFreeBSD;
77         case ZigLLVM_Fuchsia:
78             return OsFuchsia;
79         case ZigLLVM_IOS:
80             return OsIOS;
81         case ZigLLVM_KFreeBSD:
82             return OsKFreeBSD;
83         case ZigLLVM_Linux:
84             return OsLinux;
85         case ZigLLVM_Lv2:
86             return OsLv2;
87         case ZigLLVM_Darwin:
88         case ZigLLVM_MacOSX:
89             return OsMacOSX;
90         case ZigLLVM_NetBSD:
91             return OsNetBSD;
92         case ZigLLVM_OpenBSD:
93             return OsOpenBSD;
94         case ZigLLVM_Solaris:
95             return OsSolaris;
96         case ZigLLVM_Win32:
97             return OsWindows;
98         case ZigLLVM_ZOS:
99             return OsZOS;
100         case ZigLLVM_Haiku:
101             return OsHaiku;
102         case ZigLLVM_Minix:
103             return OsMinix;
104         case ZigLLVM_RTEMS:
105             return OsRTEMS;
106         case ZigLLVM_NaCl:
107             return OsNaCl;
108         case ZigLLVM_AIX:
109             return OsAIX;
110         case ZigLLVM_CUDA:
111             return OsCUDA;
112         case ZigLLVM_NVCL:
113             return OsNVCL;
114         case ZigLLVM_AMDHSA:
115             return OsAMDHSA;
116         case ZigLLVM_PS4:
117             return OsPS4;
118         case ZigLLVM_ELFIAMCU:
119             return OsELFIAMCU;
120         case ZigLLVM_TvOS:
121             return OsTvOS;
122         case ZigLLVM_WatchOS:
123             return OsWatchOS;
124         case ZigLLVM_Mesa3D:
125             return OsMesa3D;
126         case ZigLLVM_Contiki:
127             return OsContiki;
128         case ZigLLVM_AMDPAL:
129             return OsAMDPAL;
130         case ZigLLVM_HermitCore:
131             return OsHermitCore;
132         case ZigLLVM_Hurd:
133             return OsHurd;
134         case ZigLLVM_WASI:
135             return OsWASI;
136         case ZigLLVM_Emscripten:
137             return OsEmscripten;
138     }
139     zig_unreachable();
140 }
141 
get_native_target(ZigTarget * target)142 static void get_native_target(ZigTarget *target) {
143     // first zero initialize
144     *target = {};
145 
146     ZigLLVM_OSType os_type;
147     ZigLLVM_ObjectFormatType oformat; // ignored; based on arch/os
148     ZigLLVM_VendorType trash;
149     ZigLLVMGetNativeTarget(
150             &target->arch,
151             &trash,
152             &os_type,
153             &target->abi,
154             &oformat);
155     target->os = get_zig_os_type(os_type);
156     target->is_native_os = true;
157     target->is_native_cpu = true;
158     if (target->abi == ZigLLVM_UnknownEnvironment) {
159         target->abi = target_default_abi(target->arch, target->os);
160     }
161 }
162 
target_parse_triple(struct ZigTarget * target,const char * zig_triple,const char * mcpu,const char * dynamic_linker)163 static Error target_parse_triple(struct ZigTarget *target, const char *zig_triple, const char *mcpu,
164         const char *dynamic_linker)
165 {
166     Error err;
167 
168     if (zig_triple != nullptr && strcmp(zig_triple, "native") == 0) {
169         zig_triple = nullptr;
170     }
171 
172     if (zig_triple == nullptr) {
173         get_native_target(target);
174 
175         if (mcpu == nullptr) {
176             target->llvm_cpu_name = ZigLLVMGetHostCPUName();
177             target->llvm_cpu_features = ZigLLVMGetNativeFeatures();
178         } else if (strcmp(mcpu, "baseline") == 0) {
179             target->is_native_os = false;
180             target->is_native_cpu = false;
181             target->llvm_cpu_name = "";
182             target->llvm_cpu_features = "";
183         } else {
184             const char *msg = "stage0 can't handle CPU/features in the target";
185             stage2_panic(msg, strlen(msg));
186         }
187     } else {
188         // first initialize all to zero
189         *target = {};
190 
191         SplitIterator it = memSplit(str(zig_triple), str("-"));
192 
193         Optional<Slice<uint8_t>> opt_archsub = SplitIterator_next(&it);
194         Optional<Slice<uint8_t>> opt_os = SplitIterator_next(&it);
195         Optional<Slice<uint8_t>> opt_abi = SplitIterator_next(&it);
196 
197         if (!opt_archsub.is_some)
198             return ErrorMissingArchitecture;
199 
200         if ((err = target_parse_arch(&target->arch, (char*)opt_archsub.value.ptr, opt_archsub.value.len))) {
201             return err;
202         }
203 
204         if (!opt_os.is_some)
205             return ErrorMissingOperatingSystem;
206 
207         if ((err = target_parse_os(&target->os, (char*)opt_os.value.ptr, opt_os.value.len))) {
208             return err;
209         }
210 
211         if (opt_abi.is_some) {
212             if ((err = target_parse_abi(&target->abi, (char*)opt_abi.value.ptr, opt_abi.value.len))) {
213                 return err;
214             }
215         } else {
216             target->abi = target_default_abi(target->arch, target->os);
217         }
218 
219         if (mcpu != nullptr && strcmp(mcpu, "baseline") != 0) {
220             const char *msg = "stage0 can't handle CPU/features in the target";
221             stage2_panic(msg, strlen(msg));
222         }
223     }
224 
225     return ErrorNone;
226 }
227 
228 
str_starts_with(const char * s1,const char * s2)229 static bool str_starts_with(const char *s1, const char *s2) {
230     size_t s2_len = strlen(s2);
231     if (strlen(s1) < s2_len) {
232         return false;
233     }
234     return memcmp(s1, s2, s2_len) == 0;
235 }
236 
main_exit(Stage2ProgressNode * root_progress_node,int exit_code)237 int main_exit(Stage2ProgressNode *root_progress_node, int exit_code) {
238     if (root_progress_node != nullptr) {
239         stage2_progress_end(root_progress_node);
240     }
241     return exit_code;
242 }
243 
main(int argc,char ** argv)244 int main(int argc, char **argv) {
245     zig_stage1_os_init();
246 
247     char *arg0 = argv[0];
248     Error err;
249 
250     const char *in_file = nullptr;
251     const char *emit_bin_path = nullptr;
252     bool strip = false;
253     const char *out_name = nullptr;
254     bool verbose_ir = false;
255     bool verbose_llvm_ir = false;
256     bool verbose_cimport = false;
257     bool verbose_llvm_cpu_features = false;
258     ErrColor color = ErrColorAuto;
259     const char *dynamic_linker = nullptr;
260     bool link_libc = false;
261     bool link_libcpp = false;
262     const char *target_string = nullptr;
263     ZigStage1Pkg *cur_pkg = heap::c_allocator.create<ZigStage1Pkg>();
264     BuildMode optimize_mode = BuildModeDebug;
265     TargetSubsystem subsystem = TargetSubsystemAuto;
266     const char *override_lib_dir = nullptr;
267     const char *mcpu = nullptr;
268     bool single_threaded = false;
269     bool is_test_build = false;
270     bool include_compiler_rt = false;
271 
272     for (int i = 1; i < argc; i += 1) {
273         char *arg = argv[i];
274 
275         if (arg[0] == '-') {
276             if (strcmp(arg, "--") == 0) {
277                 fprintf(stderr, "Unexpected end-of-parameter mark: %s\n", arg);
278             } else if (strcmp(arg, "--test") == 0) {
279                 is_test_build = true;
280             } else if (strcmp(arg, "-ODebug") == 0) {
281                 optimize_mode = BuildModeDebug;
282             } else if (strcmp(arg, "-OReleaseFast") == 0) {
283                 optimize_mode = BuildModeFastRelease;
284             } else if (strcmp(arg, "-OReleaseSafe") == 0) {
285                 optimize_mode = BuildModeSafeRelease;
286             } else if (strcmp(arg, "-OReleaseSmall") == 0) {
287                 optimize_mode = BuildModeSmallRelease;
288             } else if (strcmp(arg, "--single-threaded") == 0) {
289                 single_threaded = true;
290             } else if (strcmp(arg, "--help") == 0) {
291                 return print_full_usage(arg0, stdout, EXIT_SUCCESS);
292             } else if (strcmp(arg, "--strip") == 0) {
293                 strip = true;
294             } else if (strcmp(arg, "--verbose-ir") == 0) {
295                 verbose_ir = true;
296             } else if (strcmp(arg, "--verbose-llvm-ir") == 0) {
297                 verbose_llvm_ir = true;
298             } else if (strcmp(arg, "--verbose-cimport") == 0) {
299                 verbose_cimport = true;
300             } else if (strcmp(arg, "--verbose-llvm-cpu-features") == 0) {
301                 verbose_llvm_cpu_features = true;
302             } else if (arg[1] == 'l' && arg[2] != 0) {
303                 // alias for --library
304                 const char *l = &arg[2];
305                 if (strcmp(l, "c") == 0) {
306                     link_libc = true;
307                 } else if (strcmp(l, "c++") == 0 || strcmp(l, "stdc++") == 0) {
308                     link_libcpp = true;
309                 }
310             } else if (strcmp(arg, "--pkg-begin") == 0) {
311                 if (i + 2 >= argc) {
312                     fprintf(stderr, "Expected 2 arguments after --pkg-begin\n");
313                     return print_error_usage(arg0);
314                 }
315                 ZigStage1Pkg *new_cur_pkg = heap::c_allocator.create<ZigStage1Pkg>();
316                 i += 1;
317                 new_cur_pkg->name_ptr = argv[i];
318                 new_cur_pkg->name_len = strlen(argv[i]);
319                 i += 1;
320                 new_cur_pkg->path_ptr = argv[i];
321                 new_cur_pkg->path_len = strlen(argv[i]);
322                 new_cur_pkg->parent = cur_pkg;
323                 cur_pkg->children_ptr = heap::c_allocator.reallocate<ZigStage1Pkg *>(cur_pkg->children_ptr,
324                         cur_pkg->children_len, cur_pkg->children_len + 1);
325                 cur_pkg->children_ptr[cur_pkg->children_len] = new_cur_pkg;
326                 cur_pkg->children_len += 1;
327 
328                 cur_pkg = new_cur_pkg;
329             } else if (strcmp(arg, "--pkg-end") == 0) {
330                 if (cur_pkg->parent == nullptr) {
331                     fprintf(stderr, "Encountered --pkg-end with no matching --pkg-begin\n");
332                     return EXIT_FAILURE;
333                 }
334                 cur_pkg = cur_pkg->parent;
335             } else if (str_starts_with(arg, "-mcpu=")) {
336                 mcpu = arg + strlen("-mcpu=");
337             } else if (str_starts_with(arg, "-femit-bin=")) {
338                 emit_bin_path = arg + strlen("-femit-bin=");
339             } else if (strcmp(arg, "-fcompiler-rt") == 0) {
340                 include_compiler_rt = true;
341             } else if (i + 1 >= argc) {
342                 fprintf(stderr, "Expected another argument after %s\n", arg);
343                 return print_error_usage(arg0);
344             } else {
345                 i += 1;
346                 if (strcmp(arg, "--color") == 0) {
347                     if (strcmp(argv[i], "auto") == 0) {
348                         color = ErrColorAuto;
349                     } else if (strcmp(argv[i], "on") == 0) {
350                         color = ErrColorOn;
351                     } else if (strcmp(argv[i], "off") == 0) {
352                         color = ErrColorOff;
353                     } else {
354                         fprintf(stderr, "--color options are 'auto', 'on', or 'off'\n");
355                         return print_error_usage(arg0);
356                     }
357                 } else if (strcmp(arg, "--name") == 0) {
358                     out_name = argv[i];
359                 } else if (strcmp(arg, "--dynamic-linker") == 0) {
360                     dynamic_linker = argv[i];
361                 } else if (strcmp(arg, "--zig-lib-dir") == 0) {
362                     override_lib_dir = argv[i];
363                 } else if (strcmp(arg, "--library") == 0 || strcmp(arg, "-l") == 0) {
364                     if (strcmp(argv[i], "c") == 0) {
365                         link_libc = true;
366                     } else if (strcmp(argv[i], "c++") == 0 || strcmp(argv[i], "stdc++") == 0) {
367                         link_libcpp = true;
368                     }
369                 } else if (strcmp(arg, "-target") == 0) {
370                     target_string = argv[i];
371                 } else if (strcmp(arg, "--subsystem") == 0) {
372                     if (strcmp(argv[i], "console") == 0) {
373                         subsystem = TargetSubsystemConsole;
374                     } else if (strcmp(argv[i], "windows") == 0) {
375                         subsystem = TargetSubsystemWindows;
376                     } else if (strcmp(argv[i], "posix") == 0) {
377                         subsystem = TargetSubsystemPosix;
378                     } else if (strcmp(argv[i], "native") == 0) {
379                         subsystem = TargetSubsystemNative;
380                     } else if (strcmp(argv[i], "efi_application") == 0) {
381                         subsystem = TargetSubsystemEfiApplication;
382                     } else if (strcmp(argv[i], "efi_boot_service_driver") == 0) {
383                         subsystem = TargetSubsystemEfiBootServiceDriver;
384                     } else if (strcmp(argv[i], "efi_rom") == 0) {
385                         subsystem = TargetSubsystemEfiRom;
386                     } else if (strcmp(argv[i], "efi_runtime_driver") == 0) {
387                         subsystem = TargetSubsystemEfiRuntimeDriver;
388                     } else {
389                         fprintf(stderr, "invalid: --subsystem %s\n"
390                                 "Options are:\n"
391                                 "  console\n"
392                                 "  windows\n"
393                                 "  posix\n"
394                                 "  native\n"
395                                 "  efi_application\n"
396                                 "  efi_boot_service_driver\n"
397                                 "  efi_rom\n"
398                                 "  efi_runtime_driver\n"
399                             , argv[i]);
400                         return EXIT_FAILURE;
401                     }
402                 } else if (strcmp(arg, "-mcpu") == 0) {
403                     mcpu = argv[i];
404                 } else {
405                     fprintf(stderr, "Invalid argument: %s\n", arg);
406                     return print_error_usage(arg0);
407                 }
408             }
409         } else if (!in_file) {
410             in_file = arg;
411         } else {
412             fprintf(stderr, "Unexpected extra parameter: %s\n", arg);
413             return print_error_usage(arg0);
414         }
415     }
416 
417     if (cur_pkg->parent != nullptr) {
418         fprintf(stderr, "Unmatched --pkg-begin\n");
419         return EXIT_FAILURE;
420     }
421 
422     Stage2Progress *progress = stage2_progress_create();
423     Stage2ProgressNode *root_progress_node = stage2_progress_start_root(progress, "", 0, 0);
424     if (color == ErrColorOff) stage2_progress_disable_tty(progress);
425 
426     ZigTarget target;
427     if ((err = target_parse_triple(&target, target_string, mcpu, dynamic_linker))) {
428         fprintf(stderr, "invalid target: %s\n", err_str(err));
429         return print_error_usage(arg0);
430     }
431 
432     if (in_file == nullptr) {
433         fprintf(stderr, "missing zig file\n");
434         return print_error_usage(arg0);
435     }
436 
437     if (out_name == nullptr) {
438         fprintf(stderr, "missing --name\n");
439         return print_error_usage(arg0);
440     }
441 
442     if (override_lib_dir == nullptr) {
443         fprintf(stderr, "missing --zig-lib-dir\n");
444         return print_error_usage(arg0);
445     }
446 
447     if (emit_bin_path == nullptr) {
448         fprintf(stderr, "missing -femit-bin=\n");
449         return print_error_usage(arg0);
450     }
451 
452     ZigStage1 *stage1 = zig_stage1_create(optimize_mode,
453         nullptr, 0,
454         in_file, strlen(in_file),
455         override_lib_dir, strlen(override_lib_dir),
456         &target, is_test_build);
457 
458     stage1->main_progress_node = root_progress_node;
459     stage1->root_name_ptr = out_name;
460     stage1->root_name_len = strlen(out_name);
461     stage1->strip = strip;
462     stage1->verbose_ir = verbose_ir;
463     stage1->verbose_llvm_ir = verbose_llvm_ir;
464     stage1->verbose_cimport = verbose_cimport;
465     stage1->verbose_llvm_cpu_features = verbose_llvm_cpu_features;
466     stage1->emit_o_ptr = emit_bin_path;
467     stage1->emit_o_len = strlen(emit_bin_path);
468     stage1->main_pkg = cur_pkg;
469     stage1->err_color = color;
470     stage1->link_libc = link_libc;
471     stage1->link_libcpp = link_libcpp;
472     stage1->subsystem = subsystem;
473     stage1->pic = true;
474     stage1->is_single_threaded = single_threaded;
475     stage1->include_compiler_rt = include_compiler_rt;
476 
477     zig_stage1_build_object(stage1);
478 
479     zig_stage1_destroy(stage1);
480 
481     return main_exit(root_progress_node, EXIT_SUCCESS);
482 }
483 
stage2_panic(const char * ptr,size_t len)484 void stage2_panic(const char *ptr, size_t len) {
485     fwrite(ptr, 1, len, stderr);
486     fprintf(stderr, "\n");
487     fflush(stderr);
488     abort();
489 }
490 
491 struct Stage2Progress {
492     int trash;
493 };
494 
495 struct Stage2ProgressNode {
496     int trash;
497 };
498 
stage2_progress_create(void)499 Stage2Progress *stage2_progress_create(void) {
500     return nullptr;
501 }
502 
stage2_progress_destroy(Stage2Progress * progress)503 void stage2_progress_destroy(Stage2Progress *progress) {}
504 
stage2_progress_start_root(Stage2Progress * progress,const char * name_ptr,size_t name_len,size_t estimated_total_items)505 Stage2ProgressNode *stage2_progress_start_root(Stage2Progress *progress,
506         const char *name_ptr, size_t name_len, size_t estimated_total_items)
507 {
508     return nullptr;
509 }
stage2_progress_start(Stage2ProgressNode * node,const char * name_ptr,size_t name_len,size_t estimated_total_items)510 Stage2ProgressNode *stage2_progress_start(Stage2ProgressNode *node,
511         const char *name_ptr, size_t name_len, size_t estimated_total_items)
512 {
513     return nullptr;
514 }
stage2_progress_end(Stage2ProgressNode * node)515 void stage2_progress_end(Stage2ProgressNode *node) {}
stage2_progress_complete_one(Stage2ProgressNode * node)516 void stage2_progress_complete_one(Stage2ProgressNode *node) {}
stage2_progress_disable_tty(Stage2Progress * progress)517 void stage2_progress_disable_tty(Stage2Progress *progress) {}
stage2_progress_update_node(Stage2ProgressNode * node,size_t completed_count,size_t estimated_total_items)518 void stage2_progress_update_node(Stage2ProgressNode *node, size_t completed_count, size_t estimated_total_items){}
519 
stage2_fetch_file(struct ZigStage1 * stage1,const char * path_ptr,size_t path_len,size_t * result_len)520 const char *stage2_fetch_file(struct ZigStage1 *stage1, const char *path_ptr, size_t path_len,
521         size_t *result_len)
522 {
523     Error err;
524     Buf contents_buf = BUF_INIT;
525     Buf path_buf = BUF_INIT;
526 
527     buf_init_from_mem(&path_buf, path_ptr, path_len);
528     if ((err = os_fetch_file_path(&path_buf, &contents_buf))) {
529         return nullptr;
530     }
531     *result_len = buf_len(&contents_buf);
532     return buf_ptr(&contents_buf);
533 }
534 
stage2_cimport(struct ZigStage1 * stage1,const char * c_src_ptr,size_t c_src_len,const char ** out_zig_path_ptr,size_t * out_zig_path_len,struct Stage2ErrorMsg ** out_errors_ptr,size_t * out_errors_len)535 Error stage2_cimport(struct ZigStage1 *stage1, const char *c_src_ptr, size_t c_src_len,
536         const char **out_zig_path_ptr, size_t *out_zig_path_len,
537         struct Stage2ErrorMsg **out_errors_ptr, size_t *out_errors_len)
538 {
539     const char *msg = "stage0 called stage2_cimport";
540     stage2_panic(msg, strlen(msg));
541 }
542 
stage2_add_link_lib(struct ZigStage1 * stage1,const char * lib_name_ptr,size_t lib_name_len,const char * symbol_name_ptr,size_t symbol_name_len)543 const char *stage2_add_link_lib(struct ZigStage1 *stage1,
544         const char *lib_name_ptr, size_t lib_name_len,
545         const char *symbol_name_ptr, size_t symbol_name_len)
546 {
547     return nullptr;
548 }
549 
stage2_version_string(void)550 const char *stage2_version_string(void) {
551     return ZIG_VERSION_STRING;
552 }
553 
stage2_version(void)554 struct Stage2SemVer stage2_version(void) {
555     return {ZIG_VERSION_MAJOR, ZIG_VERSION_MINOR, ZIG_VERSION_PATCH};
556 }
557