Commit 795703a39c

Marc Tiehuis <marctiehuis@gmail.com>
2017-11-03 14:09:33
Add emit command-line option (#580)
Add emit command-line option
1 parent a31b23c
src/all_types.hpp
@@ -1366,6 +1366,12 @@ enum BuildMode {
     BuildModeSafeRelease,
 };
 
+enum EmitFileType {
+    EmitFileTypeBinary,
+    EmitFileTypeAssembly,
+    EmitFileTypeLLVMIr,
+};
+
 struct LinkLib {
     Buf *name;
     Buf *path;
@@ -1449,6 +1455,7 @@ struct CodeGen {
         TypeTableEntry *entry_arg_tuple;
     } builtin_types;
 
+    EmitFileType emit_file_type;
     ZigTarget zig_target;
     LLVMTargetDataRef target_data_ref;
     unsigned pointer_size_bytes;
src/codegen.cpp
@@ -189,6 +189,10 @@ void codegen_set_is_test(CodeGen *g, bool is_test_build) {
     g->is_test_build = is_test_build;
 }
 
+void codegen_set_emit_file_type(CodeGen *g, EmitFileType emit_file_type) {
+    g->emit_file_type = emit_file_type;
+}
+
 void codegen_set_is_static(CodeGen *g, bool is_static) {
     g->is_static = is_static;
 }
@@ -4493,24 +4497,70 @@ static void do_code_gen(CodeGen *g) {
     LLVMVerifyModule(g->module, LLVMAbortProcessAction, &error);
 #endif
 
-    codegen_add_time_event(g, "LLVM Emit Object");
+    codegen_add_time_event(g, "LLVM Emit Output");
 
     char *err_msg = nullptr;
     Buf *o_basename = buf_create_from_buf(g->root_out_name);
-    const char *o_ext = target_o_file_ext(&g->zig_target);
-    buf_append_str(o_basename, o_ext);
+
+    switch (g->emit_file_type) {
+        case EmitFileTypeBinary:
+        {
+            const char *o_ext = target_o_file_ext(&g->zig_target);
+            buf_append_str(o_basename, o_ext);
+            break;
+        }
+        case EmitFileTypeAssembly:
+        {
+            const char *asm_ext = target_asm_file_ext(&g->zig_target);
+            buf_append_str(o_basename, asm_ext);
+            break;
+        }
+        case EmitFileTypeLLVMIr:
+        {
+            const char *llvm_ir_ext = target_llvm_ir_file_ext(&g->zig_target);
+            buf_append_str(o_basename, llvm_ir_ext);
+            break;
+        }
+        default:
+            zig_unreachable();
+    }
+
     Buf *output_path = buf_alloc();
     os_path_join(g->cache_dir, o_basename, output_path);
     ensure_cache_dir(g);
-    if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, buf_ptr(output_path),
-                LLVMObjectFile, &err_msg, g->build_mode == BuildModeDebug))
-    {
-        zig_panic("unable to write object file %s: %s", buf_ptr(output_path), err_msg);
-    }
 
-    validate_inline_fns(g);
+    switch (g->emit_file_type) {
+        case EmitFileTypeBinary:
+            if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, buf_ptr(output_path),
+                        ZigLLVM_EmitBinary, &err_msg, g->build_mode == BuildModeDebug))
+            {
+                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);
+            break;
+
+        case EmitFileTypeAssembly:
+            if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, buf_ptr(output_path),
+                        ZigLLVM_EmitAssembly, &err_msg, g->build_mode == BuildModeDebug))
+            {
+                zig_panic("unable to write assembly file %s: %s", buf_ptr(output_path), err_msg);
+            }
+            validate_inline_fns(g);
+            break;
+
+        case EmitFileTypeLLVMIr:
+            if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, buf_ptr(output_path),
+                        ZigLLVM_EmitLLVMIr, &err_msg, g->build_mode == BuildModeDebug))
+            {
+                zig_panic("unable to write llvm-ir file %s: %s", buf_ptr(output_path), err_msg);
+            }
+            validate_inline_fns(g);
+            break;
 
-    g->link_objects.append(output_path);
+        default:
+            zig_unreachable();
+    }
 }
 
 static const uint8_t int_sizes_in_bits[] = {
src/codegen.hpp
@@ -23,6 +23,7 @@ void codegen_set_llvm_argv(CodeGen *codegen, const char **args, size_t len);
 void codegen_set_is_test(CodeGen *codegen, bool is_test);
 void codegen_set_each_lib_rpath(CodeGen *codegen, bool each_lib_rpath);
 
+void codegen_set_emit_file_type(CodeGen *g, EmitFileType emit_file_type);
 void codegen_set_is_static(CodeGen *codegen, bool is_static);
 void codegen_set_strip(CodeGen *codegen, bool strip);
 void codegen_set_errmsg_color(CodeGen *codegen, ErrColor err_color);
src/main.cpp
@@ -32,6 +32,7 @@ static int usage(const char *arg0) {
         "  --assembly $source           add assembly file to build\n"
         "  --cache-dir $path            override the cache directory\n"
         "  --color $auto|off|on         enable or disable colored error messages\n"
+        "  --emit $filetype             emit a specific file format as compilation output\n"
         "  --enable-timing-info         print timing diagnostics\n"
         "  --libc-include-dir $path     directory where libc stdlib.h resides\n"
         "  --name $name                 override output name\n"
@@ -269,6 +270,7 @@ int main(int argc, char **argv) {
 
     char *arg0 = argv[0];
     Cmd cmd = CmdInvalid;
+    EmitFileType emit_file_type = EmitFileTypeBinary;
     const char *in_file = nullptr;
     const char *out_file = nullptr;
     const char *out_file_h = nullptr;
@@ -535,6 +537,17 @@ int main(int argc, char **argv) {
                         fprintf(stderr, "--color options are 'auto', 'on', or 'off'\n");
                         return usage(arg0);
                     }
+                } else if (strcmp(arg, "--emit") == 0) {
+                    if (strcmp(argv[i], "asm") == 0) {
+                        emit_file_type = EmitFileTypeAssembly;
+                    } else if (strcmp(argv[i], "bin") == 0) {
+                        emit_file_type = EmitFileTypeBinary;
+                    } else if (strcmp(argv[i], "llvm-ir") == 0) {
+                        emit_file_type = EmitFileTypeLLVMIr;
+                    } else {
+                        fprintf(stderr, "--emit options are 'asm', 'bin', or 'llvm-ir'\n");
+                        return usage(arg0);
+                    }
                 } else if (strcmp(arg, "--name") == 0) {
                     out_name = argv[i];
                 } else if (strcmp(arg, "--libc-lib-dir") == 0) {
@@ -815,6 +828,8 @@ int main(int argc, char **argv) {
             add_package(g, cur_pkg, g->root_package);
 
             if (cmd == CmdBuild) {
+                codegen_set_emit_file_type(g, emit_file_type);
+
                 for (size_t i = 0; i < objects.length; i += 1) {
                     codegen_add_object(g, buf_create_from_str(objects.at(i)));
                 }
src/target.cpp
@@ -555,6 +555,14 @@ const char *target_o_file_ext(ZigTarget *target) {
     }
 }
 
+const char *target_asm_file_ext(ZigTarget *target) {
+    return ".s";
+}
+
+const char *target_llvm_ir_file_ext(ZigTarget *target) {
+    return ".ll";
+}
+
 const char *target_exe_file_ext(ZigTarget *target) {
     if (target->os == ZigLLVM_Win32) {
         return ".exe";
src/target.hpp
@@ -73,6 +73,8 @@ void resolve_target_object_format(ZigTarget *target);
 uint32_t target_c_type_size_in_bits(const ZigTarget *target, CIntType id);
 
 const char *target_o_file_ext(ZigTarget *target);
+const char *target_asm_file_ext(ZigTarget *target);
+const char *target_llvm_ir_file_ext(ZigTarget *target);
 const char *target_exe_file_ext(ZigTarget *target);
 
 Buf *target_dynamic_linker(ZigTarget *target);
src/zig_llvm.cpp
@@ -77,7 +77,7 @@ static const bool assertions_on = false;
 #endif
 
 bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,
-        const char *filename, LLVMCodeGenFileType file_type, char **error_message, bool is_debug)
+        const char *filename, ZigLLVM_EmitOutputType output_type, char **error_message, bool is_debug)
 {
     std::error_code EC;
     raw_fd_ostream dest(filename, EC, sys::fs::F_None);
@@ -135,18 +135,24 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM
     MPM.add(createTargetTransformInfoWrapperPass(target_machine->getTargetIRAnalysis()));
     PMBuilder->populateModulePassManager(MPM);
 
+    // Set output pass.
     TargetMachine::CodeGenFileType ft;
-    switch (file_type) {
-        case LLVMAssemblyFile:
-            ft = TargetMachine::CGFT_AssemblyFile;
-            break;
-        default:
-            ft = TargetMachine::CGFT_ObjectFile;
-            break;
-    }
-    if (target_machine->addPassesToEmitFile(MPM, dest, ft)) {
-        *error_message = strdup("TargetMachine can't emit a file of this type");
-        return true;
+    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();
+        }
+
+        if (target_machine->addPassesToEmitFile(MPM, dest, ft)) {
+            *error_message = strdup("TargetMachine can't emit a file of this type");
+            return true;
+        }
     }
 
     // run per function optimization passes
@@ -158,7 +164,11 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM
 
     MPM.run(*module);
 
-    dest.close();
+    if (output_type == ZigLLVM_EmitLLVMIr) {
+        if (LLVMPrintModuleToFile(module_ref, filename, error_message)) {
+            return true;
+        }
+    }
 
     return false;
 }
src/zig_llvm.hpp
@@ -34,8 +34,16 @@ void ZigLLVMInitializeLowerIntrinsicsPass(LLVMPassRegistryRef R);
 char *ZigLLVMGetHostCPUName(void);
 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,
+};
+
 bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,
-        const char *filename, LLVMCodeGenFileType file_type, char **error_message, bool is_debug);
+        const char *filename, ZigLLVM_EmitOutputType output_type, char **error_message, bool is_debug);
 
 LLVMValueRef ZigLLVMBuildCall(LLVMBuilderRef B, LLVMValueRef Fn, LLVMValueRef *Args,
         unsigned NumArgs, unsigned CC, bool always_inline, const char *Name);