diff --git a/src/all_types.hpp b/src/all_types.hpp index c5ab12db35..af885413f7 100644 --- a/src/all_types.hpp +++ b/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; diff --git a/src/codegen.cpp b/src/codegen.cpp index 4852846af0..4a69f40da1 100644 --- a/src/codegen.cpp +++ b/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); + + 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; + + default: + zig_unreachable(); } - - validate_inline_fns(g); - - g->link_objects.append(output_path); } static const uint8_t int_sizes_in_bits[] = { diff --git a/src/codegen.hpp b/src/codegen.hpp index 622edb82d8..b71a7fa651 100644 --- a/src/codegen.hpp +++ b/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); diff --git a/src/main.cpp b/src/main.cpp index 358f1cf255..db463ac92b 100644 --- a/src/main.cpp +++ b/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))); } diff --git a/src/target.cpp b/src/target.cpp index de7509f4ae..75cbf0c64f 100644 --- a/src/target.cpp +++ b/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"; diff --git a/src/target.hpp b/src/target.hpp index 528e42d687..2b678b313d 100644 --- a/src/target.hpp +++ b/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); diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp index 0e1a067bc6..086c1d6f96 100644 --- a/src/zig_llvm.cpp +++ b/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; } diff --git a/src/zig_llvm.hpp b/src/zig_llvm.hpp index e8df900a10..d7c9784e79 100644 --- a/src/zig_llvm.hpp +++ b/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);