Commit d7968c6d33

Andrew Kelley <andrew@ziglang.org>
2020-02-19 03:59:43
improvements which allow zig to emit multiple things at once
example: zig build-obj test.zig -femit-llvm-ir -femit-asm this will generate all three: test.o test.s test.ll
1 parent dba12cd
lib/std/build.zig
@@ -1155,9 +1155,9 @@ pub const LibExeObjStep = struct {
     frameworks: BufSet,
     verbose_link: bool,
     verbose_cc: bool,
-    emit_ir: bool,
-    emit_asm: bool,
-    emit_bin: bool,
+    emit_llvm_ir: bool = false,
+    emit_asm: bool = false,
+    emit_bin: bool = true,
     disable_gen_h: bool,
     bundle_compiler_rt: bool,
     disable_stack_probing: bool,
@@ -1288,9 +1288,6 @@ pub const LibExeObjStep = struct {
             .builder = builder,
             .verbose_link = false,
             .verbose_cc = false,
-            .emit_ir = false,
-            .emit_asm = false,
-            .emit_bin = true,
             .build_mode = builtin.Mode.Debug,
             .is_dynamic = is_dynamic,
             .kind = kind,
@@ -1947,9 +1944,9 @@ pub const LibExeObjStep = struct {
         if (builder.verbose_link or self.verbose_link) zig_args.append("--verbose-link") catch unreachable;
         if (builder.verbose_cc or self.verbose_cc) zig_args.append("--verbose-cc") catch unreachable;
 
-        try zig_args.append(if (self.emit_ir) "-femit-llvm-ir" else "-fno-emit-llvm-ir");
-        try zig_args.append(if (self.emit_asm) "-femit-asm" else "-fno-emit-asm");
-        try zig_args.append(if (self.emit_bin) "-femit-bin" else "-fno-emit-bin");
+        if (self.emit_llvm_ir) try zig_args.append("-femit-llvm-ir");
+        if (self.emit_asm) try zig_args.append("-femit-asm");
+        if (!self.emit_bin) try zig_args.append("-fno-emit-bin");
 
         if (self.strip) {
             try zig_args.append("--strip");
src/all_types.hpp
@@ -2136,8 +2136,10 @@ struct CodeGen {
 
     Buf llvm_triple_str;
     Buf global_asm;
-    Buf output_file_path;
     Buf o_file_output_path;
+    Buf bin_file_output_path;
+    Buf asm_file_output_path;
+    Buf llvm_ir_file_output_path;
     Buf *cache_dir;
     // As an input parameter, mutually exclusive with enable_cache. But it gets
     // populated in codegen_build_and_link.
@@ -2198,8 +2200,6 @@ struct CodeGen {
     bool verbose_cimport;
     bool verbose_cc;
     bool verbose_llvm_cpu_features;
-    bool emit_asm;
-    bool emit_llvm_ir;
     bool error_during_imports;
     bool generate_error_name_table;
     bool enable_cache; // mutually exclusive with output_dir
@@ -2253,7 +2253,9 @@ struct CodeGen {
     bool function_sections;
     bool enable_dump_analysis;
     bool enable_doc_generation;
-    bool disable_bin_generation;
+    bool emit_bin;
+    bool emit_asm;
+    bool emit_llvm_ir;
     bool test_is_evented;
     CodeModel code_model;
 
src/codegen.cpp
@@ -7915,54 +7915,48 @@ static void do_code_gen(CodeGen *g) {
     }
 }
 
-void codegen_set_emit_asm(CodeGen *g, bool emit) {
-    g->emit_asm = emit;
-}
-
-void codegen_set_emit_llvm_ir(CodeGen *g, bool emit) {
-    g->emit_llvm_ir = emit;
-}
-
 static void zig_llvm_emit_output(CodeGen *g) {
     g->pass1_arena->destruct(&heap::c_allocator);
     g->pass1_arena = nullptr;
 
     bool is_small = g->build_mode == BuildModeSmallRelease;
 
-    Buf *output_path = &g->o_file_output_path;
     char *err_msg = nullptr;
-    if (!g->disable_bin_generation) {
-        if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, buf_ptr(output_path),
-                    ZigLLVM_EmitBinary, &err_msg, g->build_mode == BuildModeDebug, is_small,
-                    g->enable_time_report))
-        {
-            zig_panic("unable to write object file %s: %s", buf_ptr(output_path), err_msg);
-        }
-        validate_inline_fns(g);
-        g->link_objects.append(output_path);
-        if (g->bundle_compiler_rt && (g->out_type == OutTypeObj ||
-            (g->out_type == OutTypeLib && !g->is_dynamic)))
+    const char *asm_filename = nullptr;
+    const char *bin_filename = nullptr;
+    const char *llvm_ir_filename = nullptr;
+
+    if (g->emit_bin) bin_filename = buf_ptr(&g->o_file_output_path);
+    if (g->emit_asm) asm_filename = buf_ptr(&g->asm_file_output_path);
+    if (g->emit_llvm_ir) llvm_ir_filename = buf_ptr(&g->llvm_ir_file_output_path);
+
+    // Unfortunately, LLVM shits the bed when we ask for both binary and assembly. So we call the entire
+    // pipeline multiple times if this is requested.
+    if (asm_filename != nullptr && bin_filename != nullptr) {
+        if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, &err_msg, g->build_mode == BuildModeDebug,
+            is_small, g->enable_time_report, nullptr, bin_filename, llvm_ir_filename))
         {
-            zig_link_add_compiler_rt(g, g->sub_progress_node);
+            fprintf(stderr, "LLVM failed to emit file: %s\n", err_msg);
+            exit(1);
         }
+        bin_filename = nullptr;
+        llvm_ir_filename = nullptr;
     }
-    if (g->emit_asm) {
-        if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, buf_ptr(output_path),
-                    ZigLLVM_EmitAssembly, &err_msg, g->build_mode == BuildModeDebug, is_small,
-                    g->enable_time_report))
-        {
-            zig_panic("unable to write assembly file %s: %s", buf_ptr(output_path), err_msg);
-        }
-        validate_inline_fns(g);
+
+    if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, &err_msg, g->build_mode == BuildModeDebug,
+        is_small, g->enable_time_report, asm_filename, bin_filename, llvm_ir_filename))
+    {
+        fprintf(stderr, "LLVM failed to emit file: %s\n", err_msg);
+        exit(1);
     }
-    if (g->emit_llvm_ir) {
-        if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, buf_ptr(output_path),
-                    ZigLLVM_EmitLLVMIr, &err_msg, g->build_mode == BuildModeDebug, is_small,
-                    g->enable_time_report))
-        {
-            zig_panic("unable to write llvm-ir file %s: %s", buf_ptr(output_path), err_msg);
+
+    validate_inline_fns(g);
+
+    if (g->emit_bin) {
+        g->link_objects.append(&g->o_file_output_path);
+        if (g->bundle_compiler_rt && (g->out_type == OutTypeObj || (g->out_type == OutTypeLib && !g->is_dynamic))) {
+            zig_link_add_compiler_rt(g, g->sub_progress_node);
         }
-        validate_inline_fns(g);
     }
 
     LLVMDisposeModule(g->module);
@@ -10446,7 +10440,9 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) {
     cache_bool(ch, g->function_sections);
     cache_bool(ch, g->enable_dump_analysis);
     cache_bool(ch, g->enable_doc_generation);
-    cache_bool(ch, g->disable_bin_generation);
+    cache_bool(ch, g->emit_bin);
+    cache_bool(ch, g->emit_llvm_ir);
+    cache_bool(ch, g->emit_asm);
     cache_buf_opt(ch, g->mmacosx_version_min);
     cache_buf_opt(ch, g->mios_version_min);
     cache_usize(ch, g->version_major);
@@ -10491,15 +10487,15 @@ static void resolve_out_paths(CodeGen *g) {
     assert(g->output_dir != nullptr);
     assert(g->root_out_name != nullptr);
 
-    Buf *out_basename = buf_create_from_buf(g->root_out_name);
-    Buf *o_basename = buf_create_from_buf(g->root_out_name);
-    if (!g->disable_bin_generation) {
+    if (g->emit_bin) {
+        Buf *out_basename = buf_create_from_buf(g->root_out_name);
+        Buf *o_basename = buf_create_from_buf(g->root_out_name);
         switch (g->out_type) {
             case OutTypeUnknown:
                 zig_unreachable();
             case OutTypeObj:
                 if (g->enable_cache && g->link_objects.length == 1 && !need_llvm_module(g)) {
-                    buf_init_from_buf(&g->output_file_path, g->link_objects.at(0));
+                    buf_init_from_buf(&g->bin_file_output_path, g->link_objects.at(0));
                     return;
                 }
                 if (need_llvm_module(g) && g->link_objects.length != 0 && !g->enable_cache &&
@@ -10524,20 +10520,21 @@ static void resolve_out_paths(CodeGen *g) {
                             g->version_major, g->version_minor, g->version_patch));
                 break;
         }
+        os_path_join(g->output_dir, o_basename, &g->o_file_output_path);
+        os_path_join(g->output_dir, out_basename, &g->bin_file_output_path);
     }
-    else if (g->emit_asm) {
+    if (g->emit_asm) {
+        Buf *asm_basename = buf_create_from_buf(g->root_out_name);
         const char *asm_ext = target_asm_file_ext(g->zig_target);
-        buf_append_str(o_basename, asm_ext);
-        buf_append_str(out_basename, asm_ext);
+        buf_append_str(asm_basename, asm_ext);
+        os_path_join(g->output_dir, asm_basename, &g->asm_file_output_path);
     }
-    else if (g->emit_llvm_ir) {
+    if (g->emit_llvm_ir) {
+        Buf *llvm_ir_basename = buf_create_from_buf(g->root_out_name);
         const char *llvm_ir_ext = target_llvm_ir_file_ext(g->zig_target);
-        buf_append_str(o_basename, llvm_ir_ext);
-        buf_append_str(out_basename, llvm_ir_ext);
+        buf_append_str(llvm_ir_basename, llvm_ir_ext);
+        os_path_join(g->output_dir, llvm_ir_basename, &g->llvm_ir_file_output_path);
     }
-
-    os_path_join(g->output_dir, o_basename, &g->o_file_output_path);
-    os_path_join(g->output_dir, out_basename, &g->output_file_path);
 }
 
 void codegen_build_and_link(CodeGen *g) {
@@ -10699,7 +10696,7 @@ void codegen_build_and_link(CodeGen *g) {
         // If there is more than one object, we have to link them (with -r).
         // Finally, if we didn't make an object from zig source, and we don't have caching enabled,
         // then we have an object from C source that we must copy to the output dir which we do with a -r link.
-        if (!g->disable_bin_generation  &&
+        if (g->emit_bin  &&
                 (g->out_type != OutTypeObj || g->link_objects.length > 1 ||
                     (!need_llvm_module(g) && !g->enable_cache)))
         {
@@ -10777,6 +10774,7 @@ CodeGen *codegen_create(Buf *main_pkg_path, Buf *root_src_path, const ZigTarget
     Stage2LibCInstallation *libc, Buf *cache_dir, bool is_test_build, Stage2ProgressNode *progress_node)
 {
     CodeGen *g = heap::c_allocator.create<CodeGen>();
+    g->emit_bin = true;
     g->pass1_arena = heap::ArenaAllocator::construct(&heap::c_allocator, &heap::c_allocator, "pass1");
     g->main_progress_node = progress_node;
 
src/codegen.hpp
@@ -26,9 +26,6 @@ void codegen_set_clang_argv(CodeGen *codegen, const char **args, size_t len);
 void codegen_set_llvm_argv(CodeGen *codegen, const char **args, size_t len);
 void codegen_set_each_lib_rpath(CodeGen *codegen, bool each_lib_rpath);
 
-void codegen_set_emit_asm(CodeGen *codegen, bool emit);
-void codegen_set_emit_llvm_ir(CodeGen *codegen, bool emit);
-
 void codegen_set_strip(CodeGen *codegen, bool strip);
 void codegen_set_errmsg_color(CodeGen *codegen, ErrColor err_color);
 void codegen_set_out_name(CodeGen *codegen, Buf *out_name);
src/link.cpp
@@ -605,7 +605,7 @@ static const char *build_libc_object(CodeGen *parent_gen, const char *name, CFil
     c_source_files.append(c_file);
     child_gen->c_source_files = c_source_files;
     codegen_build_and_link(child_gen);
-    return buf_ptr(&child_gen->output_file_path);
+    return buf_ptr(&child_gen->bin_file_output_path);
 }
 
 static const char *path_from_zig_lib(CodeGen *g, const char *dir, const char *subpath) {
@@ -682,7 +682,7 @@ static const char *build_libunwind(CodeGen *parent, Stage2ProgressNode *progress
     }
     child_gen->c_source_files = c_source_files;
     codegen_build_and_link(child_gen);
-    return buf_ptr(&child_gen->output_file_path);
+    return buf_ptr(&child_gen->bin_file_output_path);
 }
 
 static void mingw_add_cc_args(CodeGen *parent, CFile *c_file) {
@@ -1123,7 +1123,7 @@ static const char *build_musl(CodeGen *parent, Stage2ProgressNode *progress_node
 
     child_gen->c_source_files = c_source_files;
     codegen_build_and_link(child_gen);
-    return buf_ptr(&child_gen->output_file_path);
+    return buf_ptr(&child_gen->bin_file_output_path);
 }
 
 static void add_msvcrt_os_dep(CodeGen *parent, CodeGen *child_gen, const char *src_path) {
@@ -1253,7 +1253,7 @@ static const char *get_libc_crt_file(CodeGen *parent, const char *file, Stage2Pr
                 child_gen->c_source_files.append(c_file);
             }
             codegen_build_and_link(child_gen);
-            return buf_ptr(&child_gen->output_file_path);
+            return buf_ptr(&child_gen->bin_file_output_path);
         } else if (strcmp(file, "msvcrt-os.lib") == 0) {
             CodeGen *child_gen = create_child_codegen(parent, nullptr, OutTypeLib, nullptr, "msvcrt-os", progress_node);
 
@@ -1270,7 +1270,7 @@ static const char *get_libc_crt_file(CodeGen *parent, const char *file, Stage2Pr
                 }
             }
             codegen_build_and_link(child_gen);
-            return buf_ptr(&child_gen->output_file_path);
+            return buf_ptr(&child_gen->bin_file_output_path);
         } else if (strcmp(file, "mingwex.lib") == 0) {
             CodeGen *child_gen = create_child_codegen(parent, nullptr, OutTypeLib, nullptr, "mingwex", progress_node);
 
@@ -1295,7 +1295,7 @@ static const char *get_libc_crt_file(CodeGen *parent, const char *file, Stage2Pr
                 zig_unreachable();
             }
             codegen_build_and_link(child_gen);
-            return buf_ptr(&child_gen->output_file_path);
+            return buf_ptr(&child_gen->bin_file_output_path);
         } else {
             zig_unreachable();
         }
@@ -1365,7 +1365,7 @@ static const char *get_libc_crt_file(CodeGen *parent, const char *file, Stage2Pr
             codegen_add_object(child_gen, buf_create_from_str(start_os));
             codegen_add_object(child_gen, buf_create_from_str(abi_note_o));
             codegen_build_and_link(child_gen);
-            return buf_ptr(&child_gen->output_file_path);
+            return buf_ptr(&child_gen->bin_file_output_path);
         } else if (strcmp(file, "libc_nonshared.a") == 0) {
             CodeGen *child_gen = create_child_codegen(parent, nullptr, OutTypeLib, nullptr, "c_nonshared", progress_node);
             {
@@ -1445,7 +1445,7 @@ static const char *get_libc_crt_file(CodeGen *parent, const char *file, Stage2Pr
                             build_libc_object(parent, deps[i].name, c_file, progress_node)));
             }
             codegen_build_and_link(child_gen);
-            return buf_ptr(&child_gen->output_file_path);
+            return buf_ptr(&child_gen->bin_file_output_path);
         } else {
             zig_unreachable();
         }
@@ -1519,7 +1519,7 @@ static Buf *build_a_raw(CodeGen *parent_gen, const char *aname, Buf *full_path,
     child_gen->want_stack_check = WantStackCheckDisabled;
 
     codegen_build_and_link(child_gen);
-    return &child_gen->output_file_path;
+    return &child_gen->bin_file_output_path;
 }
 
 static Buf *build_compiler_rt(CodeGen *parent_gen, OutType child_out_type, Stage2ProgressNode *progress_node) {
@@ -1681,7 +1681,7 @@ static void construct_linker_job_elf(LinkJob *lj) {
     } else if (is_dyn_lib) {
         lj->args.append("-shared");
 
-        assert(buf_len(&g->output_file_path) != 0);
+        assert(buf_len(&g->bin_file_output_path) != 0);
         soname = buf_sprintf("lib%s.so.%" ZIG_PRI_usize, buf_ptr(g->root_out_name), g->version_major);
     }
 
@@ -1690,7 +1690,7 @@ static void construct_linker_job_elf(LinkJob *lj) {
     }
 
     lj->args.append("-o");
-    lj->args.append(buf_ptr(&g->output_file_path));
+    lj->args.append(buf_ptr(&g->bin_file_output_path));
 
     if (lj->link_in_crt) {
         const char *crt1o;
@@ -1872,7 +1872,7 @@ static void construct_linker_job_wasm(LinkJob *lj) {
     }
     lj->args.append("--allow-undefined");
     lj->args.append("-o");
-    lj->args.append(buf_ptr(&g->output_file_path));
+    lj->args.append(buf_ptr(&g->bin_file_output_path));
 
     // .o files
     for (size_t i = 0; i < g->link_objects.length; i += 1) {
@@ -2248,7 +2248,7 @@ static void construct_linker_job_coff(LinkJob *lj) {
         lj->args.append("-DLL");
     }
 
-    lj->args.append(buf_ptr(buf_sprintf("-OUT:%s", buf_ptr(&g->output_file_path))));
+    lj->args.append(buf_ptr(buf_sprintf("-OUT:%s", buf_ptr(&g->bin_file_output_path))));
 
     if (g->libc_link_lib != nullptr && g->libc != nullptr) {
         lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", g->libc->crt_dir)));
@@ -2501,7 +2501,7 @@ static void construct_linker_job_macho(LinkJob *lj) {
         //lj->args.append("-install_name");
         //lj->args.append(buf_ptr(dylib_install_name));
 
-        assert(buf_len(&g->output_file_path) != 0);
+        assert(buf_len(&g->bin_file_output_path) != 0);
     }
 
     lj->args.append("-arch");
@@ -2532,14 +2532,14 @@ static void construct_linker_job_macho(LinkJob *lj) {
     }
 
     lj->args.append("-o");
-    lj->args.append(buf_ptr(&g->output_file_path));
+    lj->args.append(buf_ptr(&g->bin_file_output_path));
 
     for (size_t i = 0; i < g->rpath_list.length; i += 1) {
         Buf *rpath = g->rpath_list.at(i);
         add_rpath(lj, rpath);
     }
     if (is_dyn_lib) {
-        add_rpath(lj, &g->output_file_path);
+        add_rpath(lj, &g->bin_file_output_path);
     }
 
     if (is_dyn_lib) {
@@ -2659,14 +2659,14 @@ void codegen_link(CodeGen *g) {
                     progress_name, strlen(progress_name), 0));
         }
         if (g->verbose_link) {
-            fprintf(stderr, "ar rcs %s", buf_ptr(&g->output_file_path));
+            fprintf(stderr, "ar rcs %s", buf_ptr(&g->bin_file_output_path));
             for (size_t i = 0; i < file_names.length; i += 1) {
                 fprintf(stderr, " %s", file_names.at(i));
             }
             fprintf(stderr, "\n");
         }
-        if (ZigLLVMWriteArchive(buf_ptr(&g->output_file_path), file_names.items, file_names.length, os_type)) {
-            fprintf(stderr, "Unable to write archive '%s'\n", buf_ptr(&g->output_file_path));
+        if (ZigLLVMWriteArchive(buf_ptr(&g->bin_file_output_path), file_names.items, file_names.length, os_type)) {
+            fprintf(stderr, "Unable to write archive '%s'\n", buf_ptr(&g->bin_file_output_path));
             exit(1);
         }
         return;
src/main.cpp
@@ -62,17 +62,21 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) {
         "  -fno-stack-check             disable stack probing in safe builds\n"
         "  -fsanitize-c                 enable C undefined behavior detection in unsafe builds\n"
         "  -fno-sanitize-c              disable C undefined behavior detection in safe builds\n"
-        "  --emit [asm|bin|llvm-ir]     emit a specific file format as compilation output\n"
+        "  --emit [asm|bin|llvm-ir]     (deprecated) emit a specific file format as compilation output\n"
         "  -fPIC                        enable Position Independent Code\n"
         "  -fno-PIC                     disable Position Independent Code\n"
         "  -ftime-report                print timing diagnostics\n"
         "  -fstack-report               print stack size diagnostics\n"
-#ifdef ZIG_ENABLE_MEM_PROFILE
         "  -fmem-report                 print memory usage diagnostics\n"
-#endif
         "  -fdump-analysis              write analysis.json file with type information\n"
         "  -femit-docs                  create a docs/ dir with html documentation\n"
-        "  -fno-emit-bin                skip emitting machine code\n"
+        "  -fno-emit-docs               do not produce docs/ dir with html documentation\n"
+        "  -femit-bin                   (default) output machine code\n"
+        "  -fno-emit-bin                do not output machine code\n"
+        "  -femit-asm                   output .s (assembly code)\n"
+        "  -fno-emit-asm                (default) do not output .s (assembly code)\n"
+        "  -femit-llvm-ir               produce a .ll file with LLVM IR\n"
+        "  -fno-emit-llvm-ir            (default) do not produce a .ll file with LLVM IR\n"
         "  --libc [file]                Provide a file which specifies libc paths\n"
         "  --name [name]                override output name\n"
         "  --output-dir [dir]           override output directory (defaults to cwd)\n"
@@ -423,7 +427,7 @@ static int main0(int argc, char **argv) {
     bool stack_report = false;
     bool enable_dump_analysis = false;
     bool enable_doc_generation = false;
-    bool disable_bin_generation = false;
+    bool emit_bin = true;
     bool emit_asm = false;
     bool emit_llvm_ir = false;
     const char *cache_dir = nullptr;
@@ -553,7 +557,7 @@ static int main0(int argc, char **argv) {
         }
 
         Termination term;
-        args.items[0] = buf_ptr(&g->output_file_path);
+        args.items[0] = buf_ptr(&g->bin_file_output_path);
         os_spawn_process(args, &term);
         if (term.how != TerminationIdClean || term.code != 0) {
             fprintf(stderr, "\nBuild failed. The following command failed:\n");
@@ -632,8 +636,6 @@ static int main0(int argc, char **argv) {
                 enable_dump_analysis = true;
             } else if (strcmp(arg, "-femit-docs") == 0) {
                 enable_doc_generation = true;
-            } else if (strcmp(arg, "-fno-emit-bin") == 0) {
-                disable_bin_generation = true;
             } else if (strcmp(arg, "--enable-valgrind") == 0) {
                 valgrind_support = ValgrindSupportEnabled;
             } else if (strcmp(arg, "--disable-valgrind") == 0) {
@@ -703,9 +705,9 @@ static int main0(int argc, char **argv) {
             } else if (strcmp(arg, "--test-evented-io") == 0) {
                 test_evented_io = true;
             } else if (strcmp(arg, "-femit-bin") == 0) {
-                disable_bin_generation = false;
+                emit_bin = true;
             } else if (strcmp(arg, "-fno-emit-bin") == 0) {
-                disable_bin_generation = true;
+                emit_bin = false;
             } else if (strcmp(arg, "-femit-asm") == 0) {
                 emit_asm = true;
             } else if (strcmp(arg, "-fno-emit-asm") == 0) {
@@ -746,12 +748,12 @@ static int main0(int argc, char **argv) {
                 } else if (strcmp(arg, "--emit") == 0) {
                     if (strcmp(argv[i], "asm") == 0) {
                         emit_asm = true;
-                        disable_bin_generation = true;
+                        emit_bin = false;
                     } else if (strcmp(argv[i], "bin") == 0) {
-                        disable_bin_generation = false;
+                        emit_bin = true;
                     } else if (strcmp(argv[i], "llvm-ir") == 0) {
                         emit_llvm_ir = true;
-                        disable_bin_generation = true;
+                        emit_bin = false;
                     } else {
                         fprintf(stderr, "--emit options are 'asm', 'bin', or 'llvm-ir'\n");
                         return print_error_usage(arg0);
@@ -1113,8 +1115,8 @@ static int main0(int argc, char **argv) {
             {
                 fprintf(stderr, "Expected source file argument.\n");
                 return print_error_usage(arg0);
-            } else if (cmd == CmdRun && disable_bin_generation) {
-                fprintf(stderr, "Cannot run non-executable file.\n");
+            } else if (cmd == CmdRun && !emit_bin) {
+                fprintf(stderr, "Cannot run without emitting a binary file.\n");
                 return print_error_usage(arg0);
             }
 
@@ -1191,7 +1193,10 @@ static int main0(int argc, char **argv) {
             g->enable_stack_report = stack_report;
             g->enable_dump_analysis = enable_dump_analysis;
             g->enable_doc_generation = enable_doc_generation;
-            g->disable_bin_generation = disable_bin_generation;
+            g->emit_bin = emit_bin;
+            g->emit_asm = emit_asm;
+            g->emit_llvm_ir = emit_llvm_ir;
+
             codegen_set_out_name(g, buf_out_name);
             codegen_set_lib_version(g, ver_major, ver_minor, ver_patch);
             g->want_single_threaded = want_single_threaded;
@@ -1277,9 +1282,6 @@ static int main0(int argc, char **argv) {
 
 
             if (cmd == CmdBuild || cmd == CmdRun) {
-                codegen_set_emit_asm(g, emit_asm);
-                codegen_set_emit_llvm_ir(g, emit_llvm_ir);
-
                 g->enable_cache = get_cache_opt(enable_cache, cmd == CmdRun);
                 codegen_build_and_link(g);
                 if (root_progress_node != nullptr) {
@@ -1297,7 +1299,7 @@ static int main0(int argc, char **argv) {
                         mem::print_report();
 #endif
 
-                    const char *exec_path = buf_ptr(&g->output_file_path);
+                    const char *exec_path = buf_ptr(&g->bin_file_output_path);
                     ZigList<const char*> args = {0};
 
                     args.append(exec_path);
@@ -1317,21 +1319,21 @@ static int main0(int argc, char **argv) {
                 } else if (cmd == CmdBuild) {
                     if (g->enable_cache) {
 #if defined(ZIG_OS_WINDOWS)
-                        buf_replace(&g->output_file_path, '/', '\\');
+                        buf_replace(&g->bin_file_output_path, '/', '\\');
 #endif
                         if (final_output_dir_step != nullptr) {
                             Buf *dest_basename = buf_alloc();
-                            os_path_split(&g->output_file_path, nullptr, dest_basename);
+                            os_path_split(&g->bin_file_output_path, nullptr, dest_basename);
                             Buf *dest_path = buf_alloc();
                             os_path_join(final_output_dir_step, dest_basename, dest_path);
 
-                            if ((err = os_update_file(&g->output_file_path, dest_path))) {
-                                fprintf(stderr, "unable to copy %s to %s: %s\n", buf_ptr(&g->output_file_path),
+                            if ((err = os_update_file(&g->bin_file_output_path, dest_path))) {
+                                fprintf(stderr, "unable to copy %s to %s: %s\n", buf_ptr(&g->bin_file_output_path),
                                         buf_ptr(dest_path), err_str(err));
                                 return main_exit(root_progress_node, EXIT_FAILURE);
                             }
                         } else {
-                            if (printf("%s\n", buf_ptr(&g->output_file_path)) < 0)
+                            if (printf("%s\n", buf_ptr(&g->bin_file_output_path)) < 0)
                                 return main_exit(root_progress_node, EXIT_FAILURE);
                         }
                     }
@@ -1346,9 +1348,6 @@ static int main0(int argc, char **argv) {
                     codegen_print_timing_report(g, stderr);
                 return main_exit(root_progress_node, EXIT_SUCCESS);
             } else if (cmd == CmdTest) {
-                codegen_set_emit_asm(g, emit_asm);
-                codegen_set_emit_llvm_ir(g, emit_llvm_ir);
-
                 ZigTarget native;
                 get_native_target(&native);
 
@@ -1367,17 +1366,17 @@ static int main0(int argc, char **argv) {
                     zig_print_stack_report(g, stdout);
                 }
 
-                if (g->disable_bin_generation) {
+                if (!g->emit_bin) {
                     fprintf(stderr, "Semantic analysis complete. No binary produced due to -fno-emit-bin.\n");
                     return main_exit(root_progress_node, EXIT_SUCCESS);
                 }
 
-                Buf *test_exe_path_unresolved = &g->output_file_path;
+                Buf *test_exe_path_unresolved = &g->bin_file_output_path;
                 Buf *test_exe_path = buf_alloc();
                 *test_exe_path = os_path_resolve(&test_exe_path_unresolved, 1);
 
-                if (disable_bin_generation) {
-                    fprintf(stderr, "Created %s but skipping execution because it is non executable.\n",
+                if (!g->emit_bin) {
+                    fprintf(stderr, "Created %s but skipping execution because no binary generated.\n",
                             buf_ptr(test_exe_path));
                     return main_exit(root_progress_node, EXIT_SUCCESS);
                 }
src/zig_llvm.cpp
@@ -161,17 +161,32 @@ unsigned ZigLLVMDataLayoutGetProgramAddressSpace(LLVMTargetDataRef TD) {
 }
 
 bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,
-        const char *filename, ZigLLVM_EmitOutputType output_type, char **error_message, bool is_debug,
-        bool is_small, bool time_report)
+        char **error_message, bool is_debug,
+        bool is_small, bool time_report,
+        const char *asm_filename, const char *bin_filename, const char *llvm_ir_filename)
 {
     TimePassesIsEnabled = time_report;
 
-    std::error_code EC;
-    raw_fd_ostream dest(filename, EC, sys::fs::F_None);
-    if (EC) {
-        *error_message = strdup((const char *)StringRef(EC.message()).bytes_begin());
-        return true;
+    raw_fd_ostream *dest_asm = nullptr;
+    raw_fd_ostream *dest_bin = nullptr;
+
+    if (asm_filename) {
+        std::error_code EC;
+        dest_asm = new(std::nothrow) raw_fd_ostream(asm_filename, EC, sys::fs::F_None);
+        if (EC) {
+            *error_message = strdup((const char *)StringRef(EC.message()).bytes_begin());
+            return true;
+        }
     }
+    if (bin_filename) {
+        std::error_code EC;
+        dest_bin = new(std::nothrow) raw_fd_ostream(bin_filename, EC, sys::fs::F_None);
+        if (EC) {
+            *error_message = strdup((const char *)StringRef(EC.message()).bytes_begin());
+            return true;
+        }
+    }
+
     TargetMachine* target_machine = reinterpret_cast<TargetMachine*>(targ_machine_ref);
     target_machine->setO0WantsFastISel(true);
 
@@ -222,49 +237,51 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM
     }
     PMBuilder->populateFunctionPassManager(FPM);
 
-    // Set up the per-module pass manager.
-    legacy::PassManager MPM;
-    MPM.add(createTargetTransformInfoWrapperPass(target_machine->getTargetIRAnalysis()));
-    PMBuilder->populateModulePassManager(MPM);
-
-    // Set output pass.
-    TargetMachine::CodeGenFileType ft;
-    if (output_type != ZigLLVM_EmitLLVMIr) {
-        switch (output_type) {
-            case ZigLLVM_EmitAssembly:
-                ft = TargetMachine::CGFT_AssemblyFile;
-                break;
-            case ZigLLVM_EmitBinary:
-                ft = TargetMachine::CGFT_ObjectFile;
-                break;
-            default:
-                abort();
+    {
+        // Set up the per-module pass manager.
+        legacy::PassManager MPM;
+        MPM.add(createTargetTransformInfoWrapperPass(target_machine->getTargetIRAnalysis()));
+        PMBuilder->populateModulePassManager(MPM);
+
+        // Set output passes.
+        if (dest_bin) {
+            if (target_machine->addPassesToEmitFile(MPM, *dest_bin, nullptr, TargetMachine::CGFT_ObjectFile)) {
+                *error_message = strdup("TargetMachine can't emit an object file");
+                return true;
+            }
         }
-
-        if (target_machine->addPassesToEmitFile(MPM, dest, nullptr, ft)) {
-            *error_message = strdup("TargetMachine can't emit a file of this type");
-            return true;
+        if (dest_asm) {
+            if (target_machine->addPassesToEmitFile(MPM, *dest_asm, nullptr, TargetMachine::CGFT_AssemblyFile)) {
+                *error_message = strdup("TargetMachine can't emit an assembly file");
+                return true;
+            }
         }
-    }
 
-    // run per function optimization passes
-    FPM.doInitialization();
-    for (Function &F : *module)
-      if (!F.isDeclaration())
-        FPM.run(F);
-    FPM.doFinalization();
+        // run per function optimization passes
+        FPM.doInitialization();
+        for (Function &F : *module)
+        if (!F.isDeclaration())
+            FPM.run(F);
+        FPM.doFinalization();
 
-    MPM.run(*module);
+        MPM.run(*module);
 
-    if (output_type == ZigLLVM_EmitLLVMIr) {
-        if (LLVMPrintModuleToFile(module_ref, filename, error_message)) {
-            return true;
+        if (llvm_ir_filename) {
+            if (LLVMPrintModuleToFile(module_ref, llvm_ir_filename, error_message)) {
+                return true;
+            }
         }
-    }
 
-    if (time_report) {
-        TimerGroup::printAll(errs());
+        if (time_report) {
+            TimerGroup::printAll(errs());
+        }
+
+        // MPM goes out of scope and writes to the out streams
     }
+
+    delete dest_asm;
+    delete dest_bin;
+
     return false;
 }
 
src/zig_llvm.h
@@ -46,17 +46,10 @@ ZIG_EXTERN_C void ZigLLVMInitializeLowerIntrinsicsPass(LLVMPassRegistryRef R);
 ZIG_EXTERN_C char *ZigLLVMGetHostCPUName(void);
 ZIG_EXTERN_C char *ZigLLVMGetNativeFeatures(void);
 
-// We use a custom enum here since LLVM does not expose LLVMIr as an emit
-// output through the same mechanism as assembly/binary.
-enum ZigLLVM_EmitOutputType {
-    ZigLLVM_EmitAssembly,
-    ZigLLVM_EmitBinary,
-    ZigLLVM_EmitLLVMIr,
-};
-
 ZIG_EXTERN_C bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,
-        const char *filename, enum ZigLLVM_EmitOutputType output_type, char **error_message, bool is_debug,
-        bool is_small, bool time_report);
+        char **error_message, bool is_debug,
+        bool is_small, bool time_report,
+        const char *asm_filename, const char *bin_filename, const char *llvm_ir_filename);
 
 ZIG_EXTERN_C LLVMTargetMachineRef ZigLLVMCreateTargetMachine(LLVMTargetRef T, const char *Triple,
     const char *CPU, const char *Features, LLVMCodeGenOptLevel Level, LLVMRelocMode Reloc,