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) {
15651565 zig_unreachable();
15661566}
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
15681605static LLVMValueRef gen_arithmetic_bin_op(CodeGen *g, AstNode *source_node,
15691606 LLVMValueRef val1, LLVMValueRef val2,
15701607 TypeTableEntry *op1_type, TypeTableEntry *op2_type,
......@@ -1665,17 +1702,7 @@ static LLVMValueRef gen_arithmetic_bin_op(CodeGen *g, AstNode *source_node,
16651702 }
16661703 case BinOpTypeDiv:
16671704 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);
16791706 case BinOpTypeMod:
16801707 case BinOpTypeAssignMod:
16811708 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) {
821821 fprintf(stderr, "\n");
822822 }
823823
824 int return_code;
825824 Buf ld_stderr = BUF_INIT;
826825 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);
828828 if (err) {
829829 fprintf(stderr, "linker not found: '%s'\n", buf_ptr(g->linker_path));
830830 exit(1);
831831 }
832832
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 }
835841 fprintf(stderr, "%s ", buf_ptr(g->linker_path));
836842 for (int i = 0; i < lj.args.length; i += 1) {
837843 fprintf(stderr, "%s ", lj.args.at(i));
src/main.cpp+4-4
......@@ -395,13 +395,13 @@ int main(int argc, char **argv) {
395395 codegen_add_root_code(g, &root_source_dir, &root_source_name, &root_source_code);
396396 codegen_link(g, "./test");
397397 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) {
401401 fprintf(stderr, "\nTests failed. Use the following command to reproduce the failure:\n");
402402 fprintf(stderr, "./test\n");
403403 }
404 return return_code;
404 return (term.how == TerminationIdClean) ? term.code : -1;
405405 } else {
406406 zig_unreachable();
407407 }
src/os.cpp+35-15
......@@ -48,7 +48,23 @@
4848
4949
5050#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) {
5268 pid_t pid = fork();
5369 if (pid == -1)
5470 zig_panic("fork failed");
......@@ -64,28 +80,30 @@ static void os_spawn_process_posix(const char *exe, ZigList<const char *> &args,
6480 zig_panic("execvp failed: %s", strerror(errno));
6581 } else {
6682 // parent
67 waitpid(pid, return_code, 0);
83 int status;
84 waitpid(pid, &status, 0);
85 populate_termination(term, status);
6886 }
6987}
7088#endif
7189
7290#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) {
7492 Buf stderr_buf = BUF_INIT;
7593 Buf stdout_buf = BUF_INIT;
7694
7795 // 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);
7997 fwrite(buf_ptr(&stderr_buf), 1, buf_len(&stderr_buf), stderr);
8098 fwrite(buf_ptr(&stdout_buf), 1, buf_len(&stdout_buf), stdout);
8199}
82100#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) {
85103#if defined(ZIG_OS_WINDOWS)
86 os_spawn_process_windows(exe, args, return_code);
104 os_spawn_process_windows(exe, args, term);
87105#elif defined(ZIG_OS_POSIX)
88 os_spawn_process_posix(exe, args, return_code);
106 os_spawn_process_posix(exe, args, term);
89107#else
90108#error "missing os_spawn_process implementation"
91109#endif
......@@ -195,10 +213,9 @@ int os_fetch_file(FILE *f, Buf *out_buf) {
195213 zig_unreachable();
196214}
197215
198
199216#if defined(ZIG_OS_POSIX)
200217static 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)
202219{
203220 int stdin_pipe[2];
204221 int stdout_pipe[2];
......@@ -244,7 +261,9 @@ static int os_exec_process_posix(const char *exe, ZigList<const char *> &args,
244261 close(stdout_pipe[1]);
245262 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
249268 os_fetch_file(fdopen(stdout_pipe[0], "rb"), out_stdout);
250269 os_fetch_file(fdopen(stderr_pipe[0], "rb"), out_stderr);
......@@ -269,7 +288,7 @@ static void win32_panic(const char *str) {
269288*/
270289
271290static 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)
273292{
274293 Buf command_line = BUF_INIT;
275294 buf_resize(&command_line, 0);
......@@ -391,7 +410,8 @@ static int os_exec_process_windows(const char *exe, ZigList<const char *> &args,
391410 if (!GetExitCodeProcess(piProcInfo.hProcess, &exit_code)) {
392411 zig_panic("GetExitCodeProcess failed");
393412 }
394 *return_code = exit_code;
413 term->how == TerminationIdClean;
414 term->code = exit_code;
395415
396416 CloseHandle(piProcInfo.hProcess);
397417 CloseHandle(piProcInfo.hThread);
......@@ -401,12 +421,12 @@ static int os_exec_process_windows(const char *exe, ZigList<const char *> &args,
401421#endif
402422
403423int 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)
405425{
406426#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);
408428#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);
410430#else
411431#error "missing os_exec_process implementation"
412432#endif
src/os.hpp+15-2
......@@ -13,10 +13,23 @@
1313
1414#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
1629void 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);
1831int 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
2134void os_path_dirname(Buf *full_path, Buf *out_dirname);
2235void 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 {
14461446}
14471447 )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
14491459}
14501460
14511461//////////////////////////////////////////////////////////////////////////////
......@@ -1627,16 +1637,16 @@ struct type {
16271637static void run_self_hosted_test(bool is_release_mode) {
16281638 Buf zig_stderr = BUF_INIT;
16291639 Buf zig_stdout = BUF_INIT;
1630 int return_code;
16311640 ZigList<const char *> args = {0};
16321641 args.append("test");
16331642 args.append("../test/self_hosted.zig");
16341643 if (is_release_mode) {
16351644 args.append("--release");
16361645 }
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) {
16401650 printf("\nSelf-hosted tests failed:\n");
16411651 printf("./zig");
16421652 for (int i = 0; i < args.length; i += 1) {
......@@ -1694,14 +1704,14 @@ static void run_test(TestCase *test_case) {
16941704
16951705 Buf zig_stderr = BUF_INIT;
16961706 Buf zig_stdout = BUF_INIT;
1697 int return_code;
16981707 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))) {
17001710 fprintf(stderr, "Unable to exec %s: %s\n", zig_exe, err_str(err));
17011711 }
17021712
17031713 if (!test_case->is_parseh && test_case->compile_errors.length) {
1704 if (return_code) {
1714 if (term.how != TerminationIdClean || term.code != 0) {
17051715 for (int i = 0; i < test_case->compile_errors.length; i += 1) {
17061716 const char *err_text = test_case->compile_errors.at(i);
17071717 if (!strstr(buf_ptr(&zig_stderr), err_text)) {
......@@ -1723,8 +1733,8 @@ static void run_test(TestCase *test_case) {
17231733 }
17241734 }
17251735
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");
17281738 print_compiler_invocation(test_case);
17291739 printf("%s\n", buf_ptr(&zig_stderr));
17301740 exit(1);
......@@ -1754,18 +1764,28 @@ static void run_test(TestCase *test_case) {
17541764 } else {
17551765 Buf program_stderr = BUF_INIT;
17561766 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
17591769 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 }
17621782 print_compiler_invocation(test_case);
17631783 print_exe_invocation(test_case);
17641784 exit(1);
17651785 }
17661786 } 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");
17691789 print_compiler_invocation(test_case);
17701790 print_exe_invocation(test_case);
17711791 printf("%s\n", buf_ptr(&program_stderr));
test/self_hosted.zig+9
......@@ -1594,3 +1594,12 @@ fn cast_slice_to_u8_slice() {
15941594 bytes[7] = 0;
15951595 assert(big_thing_slice[1] == 0);
15961596}
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}