authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-05-07 19:58:02-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2016-05-07 19:58:02-07:00
logeb83111f0258ee41af021091bb78b3b5e5f6f3d3
treefc476002d9c94274cf9ad1a59b95345753604630
parent9d29674711dc4d1b11d4efdad8d0f5a40f56d673

add debug safety for division

See #149

7 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,6 +1565,43 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) {
1565 zig_unreachable();1565 zig_unreachable();
1566}1566}
15671567
1568static 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
1568static LLVMValueRef gen_arithmetic_bin_op(CodeGen *g, AstNode *source_node,1605static LLVMValueRef gen_arithmetic_bin_op(CodeGen *g, AstNode *source_node,
1569 LLVMValueRef val1, LLVMValueRef val2,1606 LLVMValueRef val1, LLVMValueRef val2,
1570 TypeTableEntry *op1_type, TypeTableEntry *op2_type,1607 TypeTableEntry *op1_type, TypeTableEntry *op2_type,
...@@ -1665,17 +1702,7 @@ static LLVMValueRef gen_arithmetic_bin_op(CodeGen *g, AstNode *source_node,...@@ -1665,17 +1702,7 @@ static LLVMValueRef gen_arithmetic_bin_op(CodeGen *g, AstNode *source_node,
1665 }1702 }
1666 case BinOpTypeDiv:1703 case BinOpTypeDiv:
1667 case BinOpTypeAssignDiv:1704 case BinOpTypeAssignDiv:
1668 set_debug_source_node(g, source_node);1705 return gen_div(g, source_node, val1, val2, op1_type);
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 }
1679 case BinOpTypeMod:1706 case BinOpTypeMod:
1680 case BinOpTypeAssignMod:1707 case BinOpTypeAssignMod:
1681 set_debug_source_node(g, source_node);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,17 +821,23 @@ void codegen_link(CodeGen *g, const char *out_file) {
821 fprintf(stderr, "\n");821 fprintf(stderr, "\n");
822 }822 }
823823
824 int return_code;
825 Buf ld_stderr = BUF_INIT;824 Buf ld_stderr = BUF_INIT;
826 Buf ld_stdout = BUF_INIT;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 if (err) {828 if (err) {
829 fprintf(stderr, "linker not found: '%s'\n", buf_ptr(g->linker_path));829 fprintf(stderr, "linker not found: '%s'\n", buf_ptr(g->linker_path));
830 exit(1);830 exit(1);
831 }831 }
832832
833 if (return_code != 0) {833 if (term.how != TerminationIdClean || term.code != 0) {
834 fprintf(stderr, "linker failed with return code %d\n", return_code);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 fprintf(stderr, "%s ", buf_ptr(g->linker_path));841 fprintf(stderr, "%s ", buf_ptr(g->linker_path));
836 for (int i = 0; i < lj.args.length; i += 1) {842 for (int i = 0; i < lj.args.length; i += 1) {
837 fprintf(stderr, "%s ", lj.args.at(i));843 fprintf(stderr, "%s ", lj.args.at(i));
src/main.cpp+4-4
...@@ -395,13 +395,13 @@ int main(int argc, char **argv) {...@@ -395,13 +395,13 @@ int main(int argc, char **argv) {
395 codegen_add_root_code(g, &root_source_dir, &root_source_name, &root_source_code);395 codegen_add_root_code(g, &root_source_dir, &root_source_name, &root_source_code);
396 codegen_link(g, "./test");396 codegen_link(g, "./test");
397 ZigList<const char *> args = {0};397 ZigList<const char *> args = {0};
398 int return_code;398 Termination term;
399 os_spawn_process("./test", args, &return_code);399 os_spawn_process("./test", args, &term);
400 if (return_code != 0) {400 if (term.how != TerminationIdClean || term.code != 0) {
401 fprintf(stderr, "\nTests failed. Use the following command to reproduce the failure:\n");401 fprintf(stderr, "\nTests failed. Use the following command to reproduce the failure:\n");
402 fprintf(stderr, "./test\n");402 fprintf(stderr, "./test\n");
403 }403 }
404 return return_code;404 return (term.how == TerminationIdClean) ? term.code : -1;
405 } else {405 } else {
406 zig_unreachable();406 zig_unreachable();
407 }407 }
src/os.cpp+35-15
...@@ -48,7 +48,23 @@...@@ -48,7 +48,23 @@
4848
4949
50#if defined(ZIG_OS_POSIX)50#if defined(ZIG_OS_POSIX)
51static void os_spawn_process_posix(const char *exe, ZigList<const char *> &args, int *return_code) {51static 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
67static void os_spawn_process_posix(const char *exe, ZigList<const char *> &args, Termination *term) {
52 pid_t pid = fork();68 pid_t pid = fork();
53 if (pid == -1)69 if (pid == -1)
54 zig_panic("fork failed");70 zig_panic("fork failed");
...@@ -64,28 +80,30 @@ static void os_spawn_process_posix(const char *exe, ZigList<const char *> &args,...@@ -64,28 +80,30 @@ static void os_spawn_process_posix(const char *exe, ZigList<const char *> &args,
64 zig_panic("execvp failed: %s", strerror(errno));80 zig_panic("execvp failed: %s", strerror(errno));
65 } else {81 } else {
66 // parent82 // 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#endif88#endif
7189
72#if defined(ZIG_OS_WINDOWS)90#if defined(ZIG_OS_WINDOWS)
73static void os_spawn_process_windows(const char *exe, ZigList<const char *> &args, int *return_code) {91static void os_spawn_process_windows(const char *exe, ZigList<const char *> &args, Termination *term) {
74 Buf stderr_buf = BUF_INIT;92 Buf stderr_buf = BUF_INIT;
75 Buf stdout_buf = BUF_INIT;93 Buf stdout_buf = BUF_INIT;
7694
77 // TODO this is supposed to inherit stdout/stderr instead of capturing it95 // 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 fwrite(buf_ptr(&stderr_buf), 1, buf_len(&stderr_buf), stderr);97 fwrite(buf_ptr(&stderr_buf), 1, buf_len(&stderr_buf), stderr);
80 fwrite(buf_ptr(&stdout_buf), 1, buf_len(&stdout_buf), stdout);98 fwrite(buf_ptr(&stdout_buf), 1, buf_len(&stdout_buf), stdout);
81}99}
82#endif100#endif
83101
84void os_spawn_process(const char *exe, ZigList<const char *> &args, int *return_code) {102void os_spawn_process(const char *exe, ZigList<const char *> &args, Termination *term) {
85#if defined(ZIG_OS_WINDOWS)103#if defined(ZIG_OS_WINDOWS)
86 os_spawn_process_windows(exe, args, return_code);104 os_spawn_process_windows(exe, args, term);
87#elif defined(ZIG_OS_POSIX)105#elif defined(ZIG_OS_POSIX)
88 os_spawn_process_posix(exe, args, return_code);106 os_spawn_process_posix(exe, args, term);
89#else107#else
90#error "missing os_spawn_process implementation"108#error "missing os_spawn_process implementation"
91#endif109#endif
...@@ -195,10 +213,9 @@ int os_fetch_file(FILE *f, Buf *out_buf) {...@@ -195,10 +213,9 @@ int os_fetch_file(FILE *f, Buf *out_buf) {
195 zig_unreachable();213 zig_unreachable();
196}214}
197215
198
199#if defined(ZIG_OS_POSIX)216#if defined(ZIG_OS_POSIX)
200static int os_exec_process_posix(const char *exe, ZigList<const char *> &args,217static 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 int stdin_pipe[2];220 int stdin_pipe[2];
204 int stdout_pipe[2];221 int stdout_pipe[2];
...@@ -244,7 +261,9 @@ static int os_exec_process_posix(const char *exe, ZigList<const char *> &args,...@@ -244,7 +261,9 @@ static int os_exec_process_posix(const char *exe, ZigList<const char *> &args,
244 close(stdout_pipe[1]);261 close(stdout_pipe[1]);
245 close(stderr_pipe[1]);262 close(stderr_pipe[1]);
246263
247 waitpid(pid, return_code, 0);264 int status;
265 waitpid(pid, &status, 0);
266 populate_termination(term, status);
248267
249 os_fetch_file(fdopen(stdout_pipe[0], "rb"), out_stdout);268 os_fetch_file(fdopen(stdout_pipe[0], "rb"), out_stdout);
250 os_fetch_file(fdopen(stderr_pipe[0], "rb"), out_stderr);269 os_fetch_file(fdopen(stderr_pipe[0], "rb"), out_stderr);
...@@ -269,7 +288,7 @@ static void win32_panic(const char *str) {...@@ -269,7 +288,7 @@ static void win32_panic(const char *str) {
269*/288*/
270289
271static int os_exec_process_windows(const char *exe, ZigList<const char *> &args,290static 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 Buf command_line = BUF_INIT;293 Buf command_line = BUF_INIT;
275 buf_resize(&command_line, 0);294 buf_resize(&command_line, 0);
...@@ -391,7 +410,8 @@ static int os_exec_process_windows(const char *exe, ZigList<const char *> &args,...@@ -391,7 +410,8 @@ static int os_exec_process_windows(const char *exe, ZigList<const char *> &args,
391 if (!GetExitCodeProcess(piProcInfo.hProcess, &exit_code)) {410 if (!GetExitCodeProcess(piProcInfo.hProcess, &exit_code)) {
392 zig_panic("GetExitCodeProcess failed");411 zig_panic("GetExitCodeProcess failed");
393 }412 }
394 *return_code = exit_code;413 term->how == TerminationIdClean;
414 term->code = exit_code;
395415
396 CloseHandle(piProcInfo.hProcess);416 CloseHandle(piProcInfo.hProcess);
397 CloseHandle(piProcInfo.hThread);417 CloseHandle(piProcInfo.hThread);
...@@ -401,12 +421,12 @@ static int os_exec_process_windows(const char *exe, ZigList<const char *> &args,...@@ -401,12 +421,12 @@ static int os_exec_process_windows(const char *exe, ZigList<const char *> &args,
401#endif421#endif
402422
403int os_exec_process(const char *exe, ZigList<const char *> &args,423int 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#if defined(ZIG_OS_WINDOWS)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#elif defined(ZIG_OS_POSIX)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#else430#else
411#error "missing os_exec_process implementation"431#error "missing os_exec_process implementation"
412#endif432#endif
src/os.hpp+15-2
...@@ -13,10 +13,23 @@...@@ -13,10 +13,23 @@
1313
14#include <stdio.h>14#include <stdio.h>
1515
16enum TerminationId {
17 TerminationIdClean,
18 TerminationIdSignaled,
19 TerminationIdStopped,
20 TerminationIdUnknown,
21};
22
23struct Termination {
24 TerminationId how;
25 int code;
26};
27
28
16void os_init(void);29void os_init(void);
17void os_spawn_process(const char *exe, ZigList<const char *> &args, int *return_code);30void os_spawn_process(const char *exe, ZigList<const char *> &args, Termination *term);
18int os_exec_process(const char *exe, ZigList<const char *> &args,31int 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);
2033
21void os_path_dirname(Buf *full_path, Buf *out_dirname);34void os_path_dirname(Buf *full_path, Buf *out_dirname);
22void os_path_split(Buf *full_path, Buf *out_dirname, Buf *out_basename);35void 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,6 +1446,16 @@ fn shl(a: u16, b: u16) -> u16 {
1446}1446}
1447 )SOURCE");1447 )SOURCE");
14481448
1449 add_debug_safety_case("integer division by zero", R"SOURCE(
1450pub fn main(args: [][]u8) -> %void {
1451 div0(999, 0);
1452}
1453#static_eval_enable(false)
1454fn div0(a: i32, b: i32) -> i32 {
1455 a / b
1456}
1457 )SOURCE");
1458
1449}1459}
14501460
1451//////////////////////////////////////////////////////////////////////////////1461//////////////////////////////////////////////////////////////////////////////
...@@ -1627,16 +1637,16 @@ struct type {...@@ -1627,16 +1637,16 @@ struct type {
1627static void run_self_hosted_test(bool is_release_mode) {1637static void run_self_hosted_test(bool is_release_mode) {
1628 Buf zig_stderr = BUF_INIT;1638 Buf zig_stderr = BUF_INIT;
1629 Buf zig_stdout = BUF_INIT;1639 Buf zig_stdout = BUF_INIT;
1630 int return_code;
1631 ZigList<const char *> args = {0};1640 ZigList<const char *> args = {0};
1632 args.append("test");1641 args.append("test");
1633 args.append("../test/self_hosted.zig");1642 args.append("../test/self_hosted.zig");
1634 if (is_release_mode) {1643 if (is_release_mode) {
1635 args.append("--release");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);
16381648
1639 if (return_code) {1649 if (term.how != TerminationIdClean) {
1640 printf("\nSelf-hosted tests failed:\n");1650 printf("\nSelf-hosted tests failed:\n");
1641 printf("./zig");1651 printf("./zig");
1642 for (int i = 0; i < args.length; i += 1) {1652 for (int i = 0; i < args.length; i += 1) {
...@@ -1694,14 +1704,14 @@ static void run_test(TestCase *test_case) {...@@ -1694,14 +1704,14 @@ static void run_test(TestCase *test_case) {
16941704
1695 Buf zig_stderr = BUF_INIT;1705 Buf zig_stderr = BUF_INIT;
1696 Buf zig_stdout = BUF_INIT;1706 Buf zig_stdout = BUF_INIT;
1697 int return_code;
1698 int err;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 fprintf(stderr, "Unable to exec %s: %s\n", zig_exe, err_str(err));1710 fprintf(stderr, "Unable to exec %s: %s\n", zig_exe, err_str(err));
1701 }1711 }
17021712
1703 if (!test_case->is_parseh && test_case->compile_errors.length) {1713 if (!test_case->is_parseh && test_case->compile_errors.length) {
1704 if (return_code) {1714 if (term.how != TerminationIdClean || term.code != 0) {
1705 for (int i = 0; i < test_case->compile_errors.length; i += 1) {1715 for (int i = 0; i < test_case->compile_errors.length; i += 1) {
1706 const char *err_text = test_case->compile_errors.at(i);1716 const char *err_text = test_case->compile_errors.at(i);
1707 if (!strstr(buf_ptr(&zig_stderr), err_text)) {1717 if (!strstr(buf_ptr(&zig_stderr), err_text)) {
...@@ -1723,8 +1733,8 @@ static void run_test(TestCase *test_case) {...@@ -1723,8 +1733,8 @@ static void run_test(TestCase *test_case) {
1723 }1733 }
1724 }1734 }
17251735
1726 if (return_code != 0) {1736 if (term.how != TerminationIdClean || term.code != 0) {
1727 printf("\nCompile failed with return code %d:\n", return_code);1737 printf("\nCompile failed:\n");
1728 print_compiler_invocation(test_case);1738 print_compiler_invocation(test_case);
1729 printf("%s\n", buf_ptr(&zig_stderr));1739 printf("%s\n", buf_ptr(&zig_stderr));
1730 exit(1);1740 exit(1);
...@@ -1754,18 +1764,28 @@ static void run_test(TestCase *test_case) {...@@ -1754,18 +1764,28 @@ static void run_test(TestCase *test_case) {
1754 } else {1764 } else {
1755 Buf program_stderr = BUF_INIT;1765 Buf program_stderr = BUF_INIT;
1756 Buf program_stdout = BUF_INIT;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);
17581768
1759 if (test_case->is_debug_safety) {1769 if (test_case->is_debug_safety) {
1760 if (return_code == 0) {1770 int debug_trap_signal = 5;
1761 printf("\nProgram expected to hit debug trap but exited with return code 0\n");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 print_compiler_invocation(test_case);1782 print_compiler_invocation(test_case);
1763 print_exe_invocation(test_case);1783 print_exe_invocation(test_case);
1764 exit(1);1784 exit(1);
1765 }1785 }
1766 } else {1786 } else {
1767 if (return_code != 0) {1787 if (term.how != TerminationIdClean || term.code != 0) {
1768 printf("\nProgram exited with return code %d:\n", return_code);1788 printf("\nProgram exited with error\n");
1769 print_compiler_invocation(test_case);1789 print_compiler_invocation(test_case);
1770 print_exe_invocation(test_case);1790 print_exe_invocation(test_case);
1771 printf("%s\n", buf_ptr(&program_stderr));1791 printf("%s\n", buf_ptr(&program_stderr));
test/self_hosted.zig+9
...@@ -1594,3 +1594,12 @@ fn cast_slice_to_u8_slice() {...@@ -1594,3 +1594,12 @@ fn cast_slice_to_u8_slice() {
1594 bytes[7] = 0;1594 bytes[7] = 0;
1595 assert(big_thing_slice[1] == 0);1595 assert(big_thing_slice[1] == 0);
1596}1596}
1597
1598#attribute("test")
1599fn float_division() {
1600 assert(fdiv32(12.0, 3.0) == 4.0);
1601}
1602#static_eval_enable(false)
1603fn fdiv32(a: f32, b: f32) -> f32 {
1604 a / b
1605}