| author | |
| committer | |
| log | eb83111f0258ee41af021091bb78b3b5e5f6f3d3 |
| tree | fc476002d9c94274cf9ad1a59b95345753604630 |
| parent | 9d29674711dc4d1b11d4efdad8d0f5a40f56d673 |
See #1497 files changed, 144 insertions(+), 49 deletions(-)
src/codegen.cpp+38-11| ... | ... | @@ -1565,6 +1565,43 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) { |
| 1565 | 1565 | zig_unreachable(); |
| 1566 | 1566 | } |
| 1567 | 1567 | |
| 1568 | static LLVMValueRef gen_div(CodeGen *g, AstNode *source_node, LLVMValueRef val1, LLVMValueRef val2, | |
| 1569 | TypeTableEntry *type_entry) | |
| 1570 | { | |
| 1571 | set_debug_source_node(g, source_node); | |
| 1572 | ||
| 1573 | if (want_debug_safety(g, source_node)) { | |
| 1574 | LLVMValueRef zero = LLVMConstNull(type_entry->type_ref); | |
| 1575 | LLVMValueRef is_zero_bit; | |
| 1576 | if (type_entry->id == TypeTableEntryIdInt) { | |
| 1577 | is_zero_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, val2, zero, ""); | |
| 1578 | } else if (type_entry->id == TypeTableEntryIdFloat) { | |
| 1579 | is_zero_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, val2, zero, ""); | |
| 1580 | } else { | |
| 1581 | zig_unreachable(); | |
| 1582 | } | |
| 1583 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "DivZeroOk"); | |
| 1584 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "DivZeroFail"); | |
| 1585 | LLVMBuildCondBr(g->builder, is_zero_bit, fail_block, ok_block); | |
| 1586 | ||
| 1587 | LLVMPositionBuilderAtEnd(g->builder, fail_block); | |
| 1588 | gen_debug_safety_crash(g); | |
| 1589 | ||
| 1590 | LLVMPositionBuilderAtEnd(g->builder, ok_block); | |
| 1591 | } | |
| 1592 | ||
| 1593 | if (type_entry->id == TypeTableEntryIdFloat) { | |
| 1594 | return LLVMBuildFDiv(g->builder, val1, val2, ""); | |
| 1595 | } else { | |
| 1596 | assert(type_entry->id == TypeTableEntryIdInt); | |
| 1597 | if (type_entry->data.integral.is_signed) { | |
| 1598 | return LLVMBuildSDiv(g->builder, val1, val2, ""); | |
| 1599 | } else { | |
| 1600 | return LLVMBuildUDiv(g->builder, val1, val2, ""); | |
| 1601 | } | |
| 1602 | } | |
| 1603 | } | |
| 1604 | ||
| 1568 | 1605 | static LLVMValueRef gen_arithmetic_bin_op(CodeGen *g, AstNode *source_node, |
| 1569 | 1606 | LLVMValueRef val1, LLVMValueRef val2, |
| 1570 | 1607 | TypeTableEntry *op1_type, TypeTableEntry *op2_type, |
| ... | ... | @@ -1665,17 +1702,7 @@ static LLVMValueRef gen_arithmetic_bin_op(CodeGen *g, AstNode *source_node, |
| 1665 | 1702 | } |
| 1666 | 1703 | case BinOpTypeDiv: |
| 1667 | 1704 | case BinOpTypeAssignDiv: |
| 1668 | set_debug_source_node(g, source_node); | |
| 1669 | if (op1_type->id == TypeTableEntryIdFloat) { | |
| 1670 | return LLVMBuildFDiv(g->builder, val1, val2, ""); | |
| 1671 | } else { | |
| 1672 | assert(op1_type->id == TypeTableEntryIdInt); | |
| 1673 | if (op1_type->data.integral.is_signed) { | |
| 1674 | return LLVMBuildSDiv(g->builder, val1, val2, ""); | |
| 1675 | } else { | |
| 1676 | return LLVMBuildUDiv(g->builder, val1, val2, ""); | |
| 1677 | } | |
| 1678 | } | |
| 1705 | return gen_div(g, source_node, val1, val2, op1_type); | |
| 1679 | 1706 | case BinOpTypeMod: |
| 1680 | 1707 | case BinOpTypeAssignMod: |
| 1681 | 1708 | set_debug_source_node(g, source_node); |
src/link.cpp+10-4| ... | ... | @@ -821,17 +821,23 @@ void codegen_link(CodeGen *g, const char *out_file) { |
| 821 | 821 | fprintf(stderr, "\n"); |
| 822 | 822 | } |
| 823 | 823 | |
| 824 | int return_code; | |
| 825 | 824 | Buf ld_stderr = BUF_INIT; |
| 826 | 825 | Buf ld_stdout = BUF_INIT; |
| 827 | int err = os_exec_process(buf_ptr(g->linker_path), lj.args, &return_code, &ld_stderr, &ld_stdout); | |
| 826 | Termination term; | |
| 827 | int err = os_exec_process(buf_ptr(g->linker_path), lj.args, &term, &ld_stderr, &ld_stdout); | |
| 828 | 828 | if (err) { |
| 829 | 829 | fprintf(stderr, "linker not found: '%s'\n", buf_ptr(g->linker_path)); |
| 830 | 830 | exit(1); |
| 831 | 831 | } |
| 832 | 832 | |
| 833 | if (return_code != 0) { | |
| 834 | fprintf(stderr, "linker failed with return code %d\n", return_code); | |
| 833 | if (term.how != TerminationIdClean || term.code != 0) { | |
| 834 | if (term.how == TerminationIdClean) { | |
| 835 | fprintf(stderr, "linker failed with return code %d\n", term.code); | |
| 836 | } else if (term.how == TerminationIdSignaled) { | |
| 837 | fprintf(stderr, "linker failed with signal %d\n", term.code); | |
| 838 | } else { | |
| 839 | fprintf(stderr, "linker failed\n"); | |
| 840 | } | |
| 835 | 841 | fprintf(stderr, "%s ", buf_ptr(g->linker_path)); |
| 836 | 842 | for (int i = 0; i < lj.args.length; i += 1) { |
| 837 | 843 | fprintf(stderr, "%s ", lj.args.at(i)); |
src/main.cpp+4-4| ... | ... | @@ -395,13 +395,13 @@ int main(int argc, char **argv) { |
| 395 | 395 | codegen_add_root_code(g, &root_source_dir, &root_source_name, &root_source_code); |
| 396 | 396 | codegen_link(g, "./test"); |
| 397 | 397 | ZigList<const char *> args = {0}; |
| 398 | int return_code; | |
| 399 | os_spawn_process("./test", args, &return_code); | |
| 400 | if (return_code != 0) { | |
| 398 | Termination term; | |
| 399 | os_spawn_process("./test", args, &term); | |
| 400 | if (term.how != TerminationIdClean || term.code != 0) { | |
| 401 | 401 | fprintf(stderr, "\nTests failed. Use the following command to reproduce the failure:\n"); |
| 402 | 402 | fprintf(stderr, "./test\n"); |
| 403 | 403 | } |
| 404 | return return_code; | |
| 404 | return (term.how == TerminationIdClean) ? term.code : -1; | |
| 405 | 405 | } else { |
| 406 | 406 | zig_unreachable(); |
| 407 | 407 | } |
src/os.cpp+35-15| ... | ... | @@ -48,7 +48,23 @@ |
| 48 | 48 | |
| 49 | 49 | |
| 50 | 50 | #if defined(ZIG_OS_POSIX) |
| 51 | static void os_spawn_process_posix(const char *exe, ZigList<const char *> &args, int *return_code) { | |
| 51 | static void populate_termination(Termination *term, int status) { | |
| 52 | if (WIFEXITED(status)) { | |
| 53 | term->how = TerminationIdClean; | |
| 54 | term->code = WEXITSTATUS(status); | |
| 55 | } else if (WIFSIGNALED(status)) { | |
| 56 | term->how = TerminationIdSignaled; | |
| 57 | term->code = WTERMSIG(status); | |
| 58 | } else if (WIFSTOPPED(status)) { | |
| 59 | term->how = TerminationIdStopped; | |
| 60 | term->code = WSTOPSIG(status); | |
| 61 | } else { | |
| 62 | term->how = TerminationIdUnknown; | |
| 63 | term->code = status; | |
| 64 | } | |
| 65 | } | |
| 66 | ||
| 67 | static void os_spawn_process_posix(const char *exe, ZigList<const char *> &args, Termination *term) { | |
| 52 | 68 | pid_t pid = fork(); |
| 53 | 69 | if (pid == -1) |
| 54 | 70 | zig_panic("fork failed"); |
| ... | ... | @@ -64,28 +80,30 @@ static void os_spawn_process_posix(const char *exe, ZigList<const char *> &args, |
| 64 | 80 | zig_panic("execvp failed: %s", strerror(errno)); |
| 65 | 81 | } else { |
| 66 | 82 | // parent |
| 67 | waitpid(pid, return_code, 0); | |
| 83 | int status; | |
| 84 | waitpid(pid, &status, 0); | |
| 85 | populate_termination(term, status); | |
| 68 | 86 | } |
| 69 | 87 | } |
| 70 | 88 | #endif |
| 71 | 89 | |
| 72 | 90 | #if defined(ZIG_OS_WINDOWS) |
| 73 | static void os_spawn_process_windows(const char *exe, ZigList<const char *> &args, int *return_code) { | |
| 91 | static void os_spawn_process_windows(const char *exe, ZigList<const char *> &args, Termination *term) { | |
| 74 | 92 | Buf stderr_buf = BUF_INIT; |
| 75 | 93 | Buf stdout_buf = BUF_INIT; |
| 76 | 94 | |
| 77 | 95 | // TODO this is supposed to inherit stdout/stderr instead of capturing it |
| 78 | os_exec_process(exe, args, return_code, &stderr_buf, &stdout_buf); | |
| 96 | os_exec_process(exe, args, term, &stderr_buf, &stdout_buf); | |
| 79 | 97 | fwrite(buf_ptr(&stderr_buf), 1, buf_len(&stderr_buf), stderr); |
| 80 | 98 | fwrite(buf_ptr(&stdout_buf), 1, buf_len(&stdout_buf), stdout); |
| 81 | 99 | } |
| 82 | 100 | #endif |
| 83 | 101 | |
| 84 | void os_spawn_process(const char *exe, ZigList<const char *> &args, int *return_code) { | |
| 102 | void os_spawn_process(const char *exe, ZigList<const char *> &args, Termination *term) { | |
| 85 | 103 | #if defined(ZIG_OS_WINDOWS) |
| 86 | os_spawn_process_windows(exe, args, return_code); | |
| 104 | os_spawn_process_windows(exe, args, term); | |
| 87 | 105 | #elif defined(ZIG_OS_POSIX) |
| 88 | os_spawn_process_posix(exe, args, return_code); | |
| 106 | os_spawn_process_posix(exe, args, term); | |
| 89 | 107 | #else |
| 90 | 108 | #error "missing os_spawn_process implementation" |
| 91 | 109 | #endif |
| ... | ... | @@ -195,10 +213,9 @@ int os_fetch_file(FILE *f, Buf *out_buf) { |
| 195 | 213 | zig_unreachable(); |
| 196 | 214 | } |
| 197 | 215 | |
| 198 | ||
| 199 | 216 | #if defined(ZIG_OS_POSIX) |
| 200 | 217 | static int os_exec_process_posix(const char *exe, ZigList<const char *> &args, |
| 201 | int *return_code, Buf *out_stderr, Buf *out_stdout) | |
| 218 | Termination *term, Buf *out_stderr, Buf *out_stdout) | |
| 202 | 219 | { |
| 203 | 220 | int stdin_pipe[2]; |
| 204 | 221 | int stdout_pipe[2]; |
| ... | ... | @@ -244,7 +261,9 @@ static int os_exec_process_posix(const char *exe, ZigList<const char *> &args, |
| 244 | 261 | close(stdout_pipe[1]); |
| 245 | 262 | close(stderr_pipe[1]); |
| 246 | 263 | |
| 247 | waitpid(pid, return_code, 0); | |
| 264 | int status; | |
| 265 | waitpid(pid, &status, 0); | |
| 266 | populate_termination(term, status); | |
| 248 | 267 | |
| 249 | 268 | os_fetch_file(fdopen(stdout_pipe[0], "rb"), out_stdout); |
| 250 | 269 | os_fetch_file(fdopen(stderr_pipe[0], "rb"), out_stderr); |
| ... | ... | @@ -269,7 +288,7 @@ static void win32_panic(const char *str) { |
| 269 | 288 | */ |
| 270 | 289 | |
| 271 | 290 | static int os_exec_process_windows(const char *exe, ZigList<const char *> &args, |
| 272 | int *return_code, Buf *out_stderr, Buf *out_stdout) | |
| 291 | Termination *term, Buf *out_stderr, Buf *out_stdout) | |
| 273 | 292 | { |
| 274 | 293 | Buf command_line = BUF_INIT; |
| 275 | 294 | buf_resize(&command_line, 0); |
| ... | ... | @@ -391,7 +410,8 @@ static int os_exec_process_windows(const char *exe, ZigList<const char *> &args, |
| 391 | 410 | if (!GetExitCodeProcess(piProcInfo.hProcess, &exit_code)) { |
| 392 | 411 | zig_panic("GetExitCodeProcess failed"); |
| 393 | 412 | } |
| 394 | *return_code = exit_code; | |
| 413 | term->how == TerminationIdClean; | |
| 414 | term->code = exit_code; | |
| 395 | 415 | |
| 396 | 416 | CloseHandle(piProcInfo.hProcess); |
| 397 | 417 | CloseHandle(piProcInfo.hThread); |
| ... | ... | @@ -401,12 +421,12 @@ static int os_exec_process_windows(const char *exe, ZigList<const char *> &args, |
| 401 | 421 | #endif |
| 402 | 422 | |
| 403 | 423 | int os_exec_process(const char *exe, ZigList<const char *> &args, |
| 404 | int *return_code, Buf *out_stderr, Buf *out_stdout) | |
| 424 | Termination *term, Buf *out_stderr, Buf *out_stdout) | |
| 405 | 425 | { |
| 406 | 426 | #if defined(ZIG_OS_WINDOWS) |
| 407 | return os_exec_process_windows(exe, args, return_code, out_stderr, out_stdout); | |
| 427 | return os_exec_process_windows(exe, args, term, out_stderr, out_stdout); | |
| 408 | 428 | #elif defined(ZIG_OS_POSIX) |
| 409 | return os_exec_process_posix(exe, args, return_code, out_stderr, out_stdout); | |
| 429 | return os_exec_process_posix(exe, args, term, out_stderr, out_stdout); | |
| 410 | 430 | #else |
| 411 | 431 | #error "missing os_exec_process implementation" |
| 412 | 432 | #endif |
src/os.hpp+15-2| ... | ... | @@ -13,10 +13,23 @@ |
| 13 | 13 | |
| 14 | 14 | #include <stdio.h> |
| 15 | 15 | |
| 16 | enum TerminationId { | |
| 17 | TerminationIdClean, | |
| 18 | TerminationIdSignaled, | |
| 19 | TerminationIdStopped, | |
| 20 | TerminationIdUnknown, | |
| 21 | }; | |
| 22 | ||
| 23 | struct Termination { | |
| 24 | TerminationId how; | |
| 25 | int code; | |
| 26 | }; | |
| 27 | ||
| 28 | ||
| 16 | 29 | void os_init(void); |
| 17 | void os_spawn_process(const char *exe, ZigList<const char *> &args, int *return_code); | |
| 30 | void os_spawn_process(const char *exe, ZigList<const char *> &args, Termination *term); | |
| 18 | 31 | int os_exec_process(const char *exe, ZigList<const char *> &args, |
| 19 | int *return_code, Buf *out_stderr, Buf *out_stdout); | |
| 32 | Termination *term, Buf *out_stderr, Buf *out_stdout); | |
| 20 | 33 | |
| 21 | 34 | void os_path_dirname(Buf *full_path, Buf *out_dirname); |
| 22 | 35 | void os_path_split(Buf *full_path, Buf *out_dirname, Buf *out_basename); |
test/run_tests.cpp+33-13| ... | ... | @@ -1446,6 +1446,16 @@ fn shl(a: u16, b: u16) -> u16 { |
| 1446 | 1446 | } |
| 1447 | 1447 | )SOURCE"); |
| 1448 | 1448 | |
| 1449 | add_debug_safety_case("integer division by zero", R"SOURCE( | |
| 1450 | pub fn main(args: [][]u8) -> %void { | |
| 1451 | div0(999, 0); | |
| 1452 | } | |
| 1453 | #static_eval_enable(false) | |
| 1454 | fn div0(a: i32, b: i32) -> i32 { | |
| 1455 | a / b | |
| 1456 | } | |
| 1457 | )SOURCE"); | |
| 1458 | ||
| 1449 | 1459 | } |
| 1450 | 1460 | |
| 1451 | 1461 | ////////////////////////////////////////////////////////////////////////////// |
| ... | ... | @@ -1627,16 +1637,16 @@ struct type { |
| 1627 | 1637 | static void run_self_hosted_test(bool is_release_mode) { |
| 1628 | 1638 | Buf zig_stderr = BUF_INIT; |
| 1629 | 1639 | Buf zig_stdout = BUF_INIT; |
| 1630 | int return_code; | |
| 1631 | 1640 | ZigList<const char *> args = {0}; |
| 1632 | 1641 | args.append("test"); |
| 1633 | 1642 | args.append("../test/self_hosted.zig"); |
| 1634 | 1643 | if (is_release_mode) { |
| 1635 | 1644 | args.append("--release"); |
| 1636 | 1645 | } |
| 1637 | os_exec_process(zig_exe, args, &return_code, &zig_stderr, &zig_stdout); | |
| 1646 | Termination term; | |
| 1647 | os_exec_process(zig_exe, args, &term, &zig_stderr, &zig_stdout); | |
| 1638 | 1648 | |
| 1639 | if (return_code) { | |
| 1649 | if (term.how != TerminationIdClean) { | |
| 1640 | 1650 | printf("\nSelf-hosted tests failed:\n"); |
| 1641 | 1651 | printf("./zig"); |
| 1642 | 1652 | for (int i = 0; i < args.length; i += 1) { |
| ... | ... | @@ -1694,14 +1704,14 @@ static void run_test(TestCase *test_case) { |
| 1694 | 1704 | |
| 1695 | 1705 | Buf zig_stderr = BUF_INIT; |
| 1696 | 1706 | Buf zig_stdout = BUF_INIT; |
| 1697 | int return_code; | |
| 1698 | 1707 | int err; |
| 1699 | if ((err = os_exec_process(zig_exe, test_case->compiler_args, &return_code, &zig_stderr, &zig_stdout))) { | |
| 1708 | Termination term; | |
| 1709 | if ((err = os_exec_process(zig_exe, test_case->compiler_args, &term, &zig_stderr, &zig_stdout))) { | |
| 1700 | 1710 | fprintf(stderr, "Unable to exec %s: %s\n", zig_exe, err_str(err)); |
| 1701 | 1711 | } |
| 1702 | 1712 | |
| 1703 | 1713 | if (!test_case->is_parseh && test_case->compile_errors.length) { |
| 1704 | if (return_code) { | |
| 1714 | if (term.how != TerminationIdClean || term.code != 0) { | |
| 1705 | 1715 | for (int i = 0; i < test_case->compile_errors.length; i += 1) { |
| 1706 | 1716 | const char *err_text = test_case->compile_errors.at(i); |
| 1707 | 1717 | if (!strstr(buf_ptr(&zig_stderr), err_text)) { |
| ... | ... | @@ -1723,8 +1733,8 @@ static void run_test(TestCase *test_case) { |
| 1723 | 1733 | } |
| 1724 | 1734 | } |
| 1725 | 1735 | |
| 1726 | if (return_code != 0) { | |
| 1727 | printf("\nCompile failed with return code %d:\n", return_code); | |
| 1736 | if (term.how != TerminationIdClean || term.code != 0) { | |
| 1737 | printf("\nCompile failed:\n"); | |
| 1728 | 1738 | print_compiler_invocation(test_case); |
| 1729 | 1739 | printf("%s\n", buf_ptr(&zig_stderr)); |
| 1730 | 1740 | exit(1); |
| ... | ... | @@ -1754,18 +1764,28 @@ static void run_test(TestCase *test_case) { |
| 1754 | 1764 | } else { |
| 1755 | 1765 | Buf program_stderr = BUF_INIT; |
| 1756 | 1766 | Buf program_stdout = BUF_INIT; |
| 1757 | os_exec_process(tmp_exe_path, test_case->program_args, &return_code, &program_stderr, &program_stdout); | |
| 1767 | os_exec_process(tmp_exe_path, test_case->program_args, &term, &program_stderr, &program_stdout); | |
| 1758 | 1768 | |
| 1759 | 1769 | if (test_case->is_debug_safety) { |
| 1760 | if (return_code == 0) { | |
| 1761 | printf("\nProgram expected to hit debug trap but exited with return code 0\n"); | |
| 1770 | int debug_trap_signal = 5; | |
| 1771 | if (term.how != TerminationIdSignaled || term.code != debug_trap_signal) { | |
| 1772 | if (term.how == TerminationIdClean) { | |
| 1773 | printf("\nProgram expected to hit debug trap (signal %d) but exited with return code %d\n", | |
| 1774 | debug_trap_signal, term.code); | |
| 1775 | } else if (term.how == TerminationIdSignaled) { | |
| 1776 | printf("\nProgram expected to hit debug trap (signal %d) but signaled with code %d\n", | |
| 1777 | debug_trap_signal, term.code); | |
| 1778 | } else { | |
| 1779 | printf("\nProgram expected to hit debug trap (signal %d) exited in an unexpected way\n", | |
| 1780 | debug_trap_signal); | |
| 1781 | } | |
| 1762 | 1782 | print_compiler_invocation(test_case); |
| 1763 | 1783 | print_exe_invocation(test_case); |
| 1764 | 1784 | exit(1); |
| 1765 | 1785 | } |
| 1766 | 1786 | } else { |
| 1767 | if (return_code != 0) { | |
| 1768 | printf("\nProgram exited with return code %d:\n", return_code); | |
| 1787 | if (term.how != TerminationIdClean || term.code != 0) { | |
| 1788 | printf("\nProgram exited with error\n"); | |
| 1769 | 1789 | print_compiler_invocation(test_case); |
| 1770 | 1790 | print_exe_invocation(test_case); |
| 1771 | 1791 | printf("%s\n", buf_ptr(&program_stderr)); |
test/self_hosted.zig+9| ... | ... | @@ -1594,3 +1594,12 @@ fn cast_slice_to_u8_slice() { |
| 1594 | 1594 | bytes[7] = 0; |
| 1595 | 1595 | assert(big_thing_slice[1] == 0); |
| 1596 | 1596 | } |
| 1597 | ||
| 1598 | #attribute("test") | |
| 1599 | fn float_division() { | |
| 1600 | assert(fdiv32(12.0, 3.0) == 4.0); | |
| 1601 | } | |
| 1602 | #static_eval_enable(false) | |
| 1603 | fn fdiv32(a: f32, b: f32) -> f32 { | |
| 1604 | a / b | |
| 1605 | } |