Commit a2257e4b81

Andrew Kelley <superjoe30@gmail.com>
2016-11-21 21:36:25
IR: implement setFnVisible builtin
1 parent 052cd44
src/all_types.hpp
@@ -1448,6 +1448,7 @@ enum IrInstructionId {
     IrInstructionIdToPtrType,
     IrInstructionIdPtrTypeChild,
     IrInstructionIdSetFnTest,
+    IrInstructionIdSetFnVisible,
     IrInstructionIdSetDebugSafety,
     IrInstructionIdArrayType,
     IrInstructionIdSliceType,
@@ -1712,6 +1713,13 @@ struct IrInstructionSetFnTest {
     IrInstruction *is_test;
 };
 
+struct IrInstructionSetFnVisible {
+    IrInstruction base;
+
+    IrInstruction *fn_value;
+    IrInstruction *is_visible;
+};
+
 struct IrInstructionSetDebugSafety {
     IrInstruction base;
 
src/codegen.cpp
@@ -1649,6 +1649,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
         case IrInstructionIdPtrTypeChild:
         case IrInstructionIdFieldPtr:
         case IrInstructionIdSetFnTest:
+        case IrInstructionIdSetFnVisible:
         case IrInstructionIdSetDebugSafety:
         case IrInstructionIdArrayType:
         case IrInstructionIdSliceType:
src/ir.cpp
@@ -202,6 +202,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionSetFnTest *) {
     return IrInstructionIdSetFnTest;
 }
 
+static constexpr IrInstructionId ir_instruction_id(IrInstructionSetFnVisible *) {
+    return IrInstructionIdSetFnVisible;
+}
+
 static constexpr IrInstructionId ir_instruction_id(IrInstructionSetDebugSafety *) {
     return IrInstructionIdSetDebugSafety;
 }
@@ -808,6 +812,19 @@ static IrInstruction *ir_build_set_fn_test(IrBuilder *irb, AstNode *source_node,
     return &instruction->base;
 }
 
+static IrInstruction *ir_build_set_fn_visible(IrBuilder *irb, AstNode *source_node, IrInstruction *fn_value,
+        IrInstruction *is_visible)
+{
+    IrInstructionSetFnVisible *instruction = ir_build_instruction<IrInstructionSetFnVisible>(irb, source_node);
+    instruction->fn_value = fn_value;
+    instruction->is_visible = is_visible;
+
+    ir_ref_instruction(fn_value);
+    ir_ref_instruction(is_visible);
+
+    return &instruction->base;
+}
+
 static IrInstruction *ir_build_set_debug_safety(IrBuilder *irb, AstNode *source_node,
         IrInstruction *scope_value, IrInstruction *debug_safety_on)
 {
@@ -1432,6 +1449,20 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, AstNode *node) {
 
                 return ir_build_set_fn_test(irb, node, arg0_value, arg1_value);
             }
+        case BuiltinFnIdSetFnVisible:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, node->block_context);
+                if (arg0_value == irb->codegen->invalid_instruction)
+                    return arg0_value;
+
+                AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
+                IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, node->block_context);
+                if (arg1_value == irb->codegen->invalid_instruction)
+                    return arg1_value;
+
+                return ir_build_set_fn_visible(irb, node, arg0_value, arg1_value);
+            }
         case BuiltinFnIdSetDebugSafety:
             {
                 AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
@@ -1509,7 +1540,6 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, AstNode *node) {
         case BuiltinFnIdDivExact:
         case BuiltinFnIdTruncate:
         case BuiltinFnIdIntType:
-        case BuiltinFnIdSetFnVisible:
         case BuiltinFnIdSetFnStaticEval:
         case BuiltinFnIdSetFnNoInline:
             zig_panic("TODO IR gen more builtin functions");
@@ -4394,6 +4424,43 @@ static TypeTableEntry *ir_analyze_instruction_set_fn_test(IrAnalyze *ira,
     return ira->codegen->builtin_types.entry_void;
 }
 
+static TypeTableEntry *ir_analyze_instruction_set_fn_visible(IrAnalyze *ira,
+        IrInstructionSetFnVisible *set_fn_visible_instruction)
+{
+    IrInstruction *fn_value = set_fn_visible_instruction->fn_value->other;
+    IrInstruction *is_visible_value = set_fn_visible_instruction->is_visible->other;
+
+    FnTableEntry *fn_entry = ir_resolve_fn(ira, fn_value);
+    if (!fn_entry)
+        return ira->codegen->builtin_types.entry_invalid;
+
+    bool want_export;
+    if (!ir_resolve_bool(ira, is_visible_value, &want_export))
+        return ira->codegen->builtin_types.entry_invalid;
+
+    AstNode *source_node = set_fn_visible_instruction->base.source_node;
+    if (fn_entry->fn_export_set_node) {
+        ErrorMsg *msg = add_node_error(ira->codegen, source_node,
+                buf_sprintf("function visibility set twice"));
+        add_error_note(ira->codegen, msg, fn_entry->fn_export_set_node, buf_sprintf("first set here"));
+        return ira->codegen->builtin_types.entry_invalid;
+    }
+    fn_entry->fn_export_set_node = source_node;
+
+    AstNodeFnProto *fn_proto = &fn_entry->proto_node->data.fn_proto;
+    if (fn_proto->top_level_decl.visib_mod != VisibModExport) {
+        ErrorMsg *msg = add_node_error(ira->codegen, source_node,
+            buf_sprintf("function must be marked export to set function visibility"));
+        add_error_note(ira->codegen, msg, fn_entry->proto_node, buf_sprintf("function declared here"));
+        return ira->codegen->builtin_types.entry_invalid;
+    }
+    if (!want_export)
+        LLVMSetLinkage(fn_entry->fn_value, LLVMInternalLinkage);
+
+    ir_build_const_from(ira, &set_fn_visible_instruction->base, false);
+    return ira->codegen->builtin_types.entry_void;
+}
+
 static TypeTableEntry *ir_analyze_instruction_set_debug_safety(IrAnalyze *ira,
         IrInstructionSetDebugSafety *set_debug_safety_instruction)
 {
@@ -4847,6 +4914,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
             return ir_analyze_instruction_ptr_type_child(ira, (IrInstructionPtrTypeChild *)instruction);
         case IrInstructionIdSetFnTest:
             return ir_analyze_instruction_set_fn_test(ira, (IrInstructionSetFnTest *)instruction);
+        case IrInstructionIdSetFnVisible:
+            return ir_analyze_instruction_set_fn_visible(ira, (IrInstructionSetFnVisible *)instruction);
         case IrInstructionIdSetDebugSafety:
             return ir_analyze_instruction_set_debug_safety(ira, (IrInstructionSetDebugSafety *)instruction);
         case IrInstructionIdSliceType:
@@ -4957,6 +5026,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
         case IrInstructionIdReturn:
         case IrInstructionIdUnreachable:
         case IrInstructionIdSetFnTest:
+        case IrInstructionIdSetFnVisible:
         case IrInstructionIdSetDebugSafety:
             return true;
         case IrInstructionIdPhi:
@@ -5523,44 +5593,6 @@ IrInstruction *ir_exec_const_result(IrExecutable *exec) {
 //    return g->builtin_types.entry_void;
 //}
 //
-//static TypeTableEntry *analyze_set_fn_visible(CodeGen *g, ImportTableEntry *import,
-//        BlockContext *context, AstNode *node)
-//{
-//    AstNode **fn_node = &node->data.fn_call_expr.params.at(0);
-//    AstNode **value_node = &node->data.fn_call_expr.params.at(1);
-//
-//    FnTableEntry *fn_entry = resolve_const_expr_fn(g, import, context, fn_node);
-//    if (!fn_entry) {
-//        return g->builtin_types.entry_invalid;
-//    }
-//
-//    bool want_export;
-//    bool ok = resolve_const_expr_bool(g, import, context, value_node, &want_export);
-//    if (!ok) {
-//        return g->builtin_types.entry_invalid;
-//    }
-//
-//    if (fn_entry->fn_export_set_node) {
-//        ErrorMsg *msg = add_node_error(g, node, buf_sprintf("function visibility set twice"));
-//        add_error_note(g, msg, fn_entry->fn_export_set_node, buf_sprintf("first set here"));
-//        return g->builtin_types.entry_invalid;
-//    }
-//    fn_entry->fn_export_set_node = node;
-//
-//    AstNodeFnProto *fn_proto = &fn_entry->proto_node->data.fn_proto;
-//    if (fn_proto->top_level_decl.visib_mod != VisibModExport) {
-//        ErrorMsg *msg = add_node_error(g, node,
-//            buf_sprintf("function must be marked export to set function visibility"));
-//        add_error_note(g, msg, fn_entry->proto_node, buf_sprintf("function declared here"));
-//        return g->builtin_types.entry_void;
-//    }
-//    if (!want_export) {
-//        fn_proto->top_level_decl.visib_mod = VisibModPub;
-//    }
-//
-//    return g->builtin_types.entry_void;
-//}
-//
 //static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
 //        TypeTableEntry *expected_type, AstNode *node)
 //{
@@ -5784,8 +5816,6 @@ IrInstruction *ir_exec_const_result(IrExecutable *exec) {
 //            return analyze_set_fn_no_inline(g, import, context, node);
 //        case BuiltinFnIdSetFnStaticEval:
 //            return analyze_set_fn_static_eval(g, import, context, node);
-//        case BuiltinFnIdSetFnVisible:
-//            return analyze_set_fn_visible(g, import, context, node);
 //    }
 //    zig_unreachable();
 //}
@@ -8310,7 +8340,6 @@ static void analyze_goto_pass2(CodeGen *g, ImportTableEntry *import, AstNode *no
 //        case BuiltinFnIdUnreachable:
 //            zig_panic("moved to ir render");
 //        case BuiltinFnIdSetFnTest:
-//        case BuiltinFnIdSetFnVisible:
 //        case BuiltinFnIdSetFnStaticEval:
 //        case BuiltinFnIdSetFnNoInline:
 //        case BuiltinFnIdSetDebugSafety:
src/ir_print.cpp
@@ -425,6 +425,14 @@ static void ir_print_set_fn_test(IrPrint *irp, IrInstructionSetFnTest *instructi
     fprintf(irp->f, ")");
 }
 
+static void ir_print_set_fn_visible(IrPrint *irp, IrInstructionSetFnVisible *instruction) {
+    fprintf(irp->f, "@setFnVisible(");
+    ir_print_other_instruction(irp, instruction->fn_value);
+    fprintf(irp->f, ", ");
+    ir_print_other_instruction(irp, instruction->is_visible);
+    fprintf(irp->f, ")");
+}
+
 static void ir_print_set_debug_safety(IrPrint *irp, IrInstructionSetDebugSafety *instruction) {
     fprintf(irp->f, "@setDebugSafety(");
     ir_print_other_instruction(irp, instruction->scope_value);
@@ -603,6 +611,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
         case IrInstructionIdSetFnTest:
             ir_print_set_fn_test(irp, (IrInstructionSetFnTest *)instruction);
             break;
+        case IrInstructionIdSetFnVisible:
+            ir_print_set_fn_visible(irp, (IrInstructionSetFnVisible *)instruction);
+            break;
         case IrInstructionIdSetDebugSafety:
             ir_print_set_debug_safety(irp, (IrInstructionSetDebugSafety *)instruction);
             break;
std/compiler_rt.zig
@@ -1,262 +1,263 @@
-const CHAR_BIT = 8;
-const du_int = u64;
-const di_int = i64;
-const si_int = c_int;
-const su_int = c_uint;
-
-const udwords = [2]su_int;
-const low = if (@compileVar("is_big_endian")) 1 else 0;
-const high = 1 - low;
-
-export fn __udivdi3(a: du_int, b: du_int) -> du_int {
-    @setDebugSafety(this, false);
-    return __udivmoddi4(a, b, null);
-}
-
-fn du_int_to_udwords(x: du_int) -> udwords {
-    @setDebugSafety(this, false);
-    return *(&udwords)(&x);
-}
-
-export fn __udivmoddi4(a: du_int, b: du_int, maybe_rem: ?&du_int) -> du_int {
-    @setDebugSafety(this, false);
-
-    const n_uword_bits = @sizeOf(su_int) * CHAR_BIT;
-    const n_udword_bits = @sizeOf(du_int) * CHAR_BIT;
-    var n = du_int_to_udwords(a);
-    var d = du_int_to_udwords(b);
-    var q: udwords = undefined;
-    var r: udwords = undefined;
-    var sr: c_uint = undefined;
-    // special cases, X is unknown, K != 0
-    if (n[high] == 0) {
-        if (d[high] == 0) {
-            // 0 X
-            // ---
-            // 0 X
-            if (const rem ?= maybe_rem) {
-                *rem = n[low] % d[low];
-            }
-            return n[low] / d[low];
-        }
-        // 0 X
-        // ---
-        // K X
-        if (const rem ?= maybe_rem) {
-            *rem = n[low];
-        }
-        return 0;
-    }
-    // n[high] != 0
-    if (d[low] == 0) {
-        if (d[high] == 0) {
-            // K X
-            // ---
-            // 0 0
-            if (var rem ?= maybe_rem) {
-                *rem = n[high] % d[low];
-            }
-            return n[high] / d[low];
-        }
-        // d[high] != 0
-        if (n[low] == 0) {
-            // K 0
-            // ---
-            // K 0
-            if (var rem ?= maybe_rem) {
-                r[high] = n[high] % d[high];
-                r[low] = 0;
-                *rem = *(&du_int)(&r[0]);
-            }
-            return n[high] / d[high];
-        }
-        // K K
-        // ---
-        // K 0
-        // if d is a power of 2
-        if ((d[high] & (d[high] - 1)) == 0) {
-            if (var rem ?= maybe_rem) {
-                r[low] = n[low];
-                r[high] = n[high] & (d[high] - 1);
-                *rem = *(&du_int)(&r[0]);
-            }
-            return n[high] >> @ctz(@typeOf(d[high]), d[high]);
-        }
-        // K K
-        // ---
-        // K 0
-        sr = @clz(su_int, d[high]) - @clz(su_int, n[high]);
-        // 0 <= sr <= n_uword_bits - 2 or sr large
-        if (sr > n_uword_bits - 2) {
-            if (var rem ?= maybe_rem) {
-                *rem = *(&du_int)(&n[0]);
-            }
-            return 0;
-        }
-        sr += 1;
-        // 1 <= sr <= n_uword_bits - 1
-        // q.all = n.all << (n_udword_bits - sr);
-        q[low] = 0;
-        q[high] = n[low] << (n_uword_bits - sr);
-        // r.all = n.all >> sr;
-        r[high] = n[high] >> sr;
-        r[low] = (n[high] << (n_uword_bits - sr)) | (n[low] >> sr);
-    } else {
-        // d[low] != 0
-        if (d[high] == 0) {
-            // K X
-            // ---
-            // 0 K
-            // if d is a power of 2
-            if ((d[low] & (d[low] - 1)) == 0) {
-                if (var rem ?= maybe_rem) {
-                    *rem = n[low] & (d[low] - 1);
-                }
-                if (d[low] == 1) {
-                    return *(&du_int)(&n[0]);
-                }
-                sr = @ctz(@typeOf(d[low]), d[low]);
-                q[high] = n[high] >> sr;
-                q[low] = (n[high] << (n_uword_bits - sr)) | (n[low] >> sr);
-                return *(&du_int)(&q[0]);
-            }
-            // K X
-            // ---
-            // 0 K
-            sr = 1 + n_uword_bits + @clz(su_int, d[low]) - @clz(su_int, n[high]);
-            // 2 <= sr <= n_udword_bits - 1
-            // q.all = n.all << (n_udword_bits - sr);
-            // r.all = n.all >> sr;
-            if (sr == n_uword_bits) {
-                q[low] = 0;
-                q[high] = n[low];
-                r[high] = 0;
-                r[low] = n[high];
-            } else if (sr < n_uword_bits) {
-                // 2 <= sr <= n_uword_bits - 1
-                q[low] = 0;
-                q[high] = n[low] << (n_uword_bits - sr);
-                r[high] = n[high] >> sr;
-                r[low] = (n[high] << (n_uword_bits - sr)) | (n[low] >> sr);
-            } else {
-                // n_uword_bits + 1 <= sr <= n_udword_bits - 1
-                q[low] = n[low] << (n_udword_bits - sr);
-                q[high] = (n[high] << (n_udword_bits - sr)) |
-                    (n[low] >> (sr - n_uword_bits));
-                r[high] = 0;
-                r[low] = n[high] >> (sr - n_uword_bits);
-            }
-        } else {
-            // K X
-            // ---
-            // K K
-            sr = @clz(su_int, d[high]) - @clz(su_int, n[high]);
-            // 0 <= sr <= n_uword_bits - 1 or sr large
-            if (sr > n_uword_bits - 1) {
-                if (var rem ?= maybe_rem) {
-                    *rem = *(&du_int)(&n[0]);
-                }
-                return 0;
-            }
-            sr += 1;
-            // 1 <= sr <= n_uword_bits
-            //  q.all = n.all << (n_udword_bits - sr);
-            q[low] = 0;
-            if (sr == n_uword_bits) {
-                q[high] = n[low];
-                r[high] = 0;
-                r[low] = n[high];
-            } else {
-                q[high] = n[low] << (n_uword_bits - sr);
-                r[high] = n[high] >> sr;
-                r[low] = (n[high] << (n_uword_bits - sr)) | (n[low] >> sr);
-            }
-        }
-    }
-    // Not a special case
-    // q and r are initialized with:
-    // q.all = n.all << (n_udword_bits - sr);
-    // r.all = n.all >> sr;
-    // 1 <= sr <= n_udword_bits - 1
-    var carry: su_int = 0;
-    while (sr > 0) {
-        // r:q = ((r:q)  << 1) | carry
-        r[high] = (r[high] << 1) | (r[low]  >> (n_uword_bits - 1));
-        r[low]  = (r[low]  << 1) | (q[high] >> (n_uword_bits - 1));
-        q[high] = (q[high] << 1) | (q[low]  >> (n_uword_bits - 1));
-        q[low]  = (q[low]  << 1) | carry;
-        // carry = 0;
-        // if (r.all >= d.all)
-        // {
-        //      r.all -= d.all;
-        //      carry = 1;
-        // }
-        const s: di_int = (di_int)(*(&du_int)(&d[0]) - *(&du_int)(&r[0]) - 1) >> (n_udword_bits - 1);
-        carry = su_int(s & 1);
-        *(&du_int)(&r[0]) -= *(&du_int)(&d[0]) & u64(s);
-
-        sr -= 1;
-    }
-    *(&du_int)(&q[0]) = (*(&du_int)(&q[0]) << 1) | u64(carry);
-    if (var rem ?= maybe_rem) {
-        *rem = *(&du_int)(&r[0]);
-    }
-    return *(&du_int)(&q[0]);
-}
-
-export fn __umoddi3(a: du_int, b: du_int) -> du_int {
-    @setDebugSafety(this, false);
-
-    var r: du_int = undefined;
-    __udivmoddi4(a, b, &r);
-    return r;
-}
-
-fn test_umoddi3() {
-    @setFnTest(this, true);
-
-    test_one_umoddi3(0, 1, 0);
-    test_one_umoddi3(2, 1, 0);
-    test_one_umoddi3(0x8000000000000000, 1, 0x0);
-    test_one_umoddi3(0x8000000000000000, 2, 0x0);
-    test_one_umoddi3(0xFFFFFFFFFFFFFFFF, 2, 0x1);
-}
-
-fn test_one_umoddi3(a: du_int, b: du_int, expected_r: du_int) {
-    const r = __umoddi3(a, b);
-    assert(r == expected_r);
-}
-
-fn test_udivmoddi4() {
-    @setFnTest(this, true);
-
-    const cases = [][4]du_int {
-        []du_int{0x0000000000000000, 0x0000000000000001, 0x0000000000000000, 0x0000000000000000},
-        []du_int{0x0000000080000000, 0x0000000100000001, 0x0000000000000000, 0x0000000080000000},
-        []du_int{0x7FFFFFFF00000001, 0x0000000000000001, 0x7FFFFFFF00000001, 0x0000000000000000},
-        []du_int{0x7FFFFFFF7FFFFFFF, 0xFFFFFFFFFFFFFFFF, 0x0000000000000000, 0x7FFFFFFF7FFFFFFF},
-        []du_int{0x8000000000000002, 0xFFFFFFFFFFFFFFFE, 0x0000000000000000, 0x8000000000000002},
-        []du_int{0x80000000FFFFFFFD, 0xFFFFFFFFFFFFFFFD, 0x0000000000000000, 0x80000000FFFFFFFD},
-        []du_int{0xFFFFFFFD00000010, 0xFFFFFFFF80000000, 0x0000000000000000, 0xFFFFFFFD00000010},
-        []du_int{0xFFFFFFFDFFFFFFFF, 0xFFFFFFFF7FFFFFFF, 0x0000000000000000, 0xFFFFFFFDFFFFFFFF},
-        []du_int{0xFFFFFFFE0747AE14, 0xFFFFFFFF0747AE14, 0x0000000000000000, 0xFFFFFFFE0747AE14},
-        []du_int{0xFFFFFFFF00000001, 0xFFFFFFFF078644FA, 0x0000000000000000, 0xFFFFFFFF00000001},
-        []du_int{0xFFFFFFFF80000000, 0xFFFFFFFF00000010, 0x0000000000000001, 0x000000007FFFFFF0},
-        []du_int{0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0x0000000000000001, 0x0000000000000000},
-    };
-
-    for (cases) |case| {
-        test_one_udivmoddi4(case[0], case[1], case[2], case[3]);
-    }
-}
-
-fn test_one_udivmoddi4(a: du_int, b: du_int, expected_q: du_int, expected_r: du_int) {
-    var r: du_int = undefined;
-    const q = __udivmoddi4(a, b, &r);
-    assert(q == expected_q);
-    assert(r == expected_r);
-}
-
-fn assert(b: bool) {
-    if (!b) @unreachable();
-}
+// TODO
+//const CHAR_BIT = 8;
+//const du_int = u64;
+//const di_int = i64;
+//const si_int = c_int;
+//const su_int = c_uint;
+//
+//const udwords = [2]su_int;
+//const low = if (@compileVar("is_big_endian")) 1 else 0;
+//const high = 1 - low;
+//
+//export fn __udivdi3(a: du_int, b: du_int) -> du_int {
+//    @setDebugSafety(this, false);
+//    return __udivmoddi4(a, b, null);
+//}
+//
+//fn du_int_to_udwords(x: du_int) -> udwords {
+//    @setDebugSafety(this, false);
+//    return *(&udwords)(&x);
+//}
+//
+//export fn __udivmoddi4(a: du_int, b: du_int, maybe_rem: ?&du_int) -> du_int {
+//    @setDebugSafety(this, false);
+//
+//    const n_uword_bits = @sizeOf(su_int) * CHAR_BIT;
+//    const n_udword_bits = @sizeOf(du_int) * CHAR_BIT;
+//    var n = du_int_to_udwords(a);
+//    var d = du_int_to_udwords(b);
+//    var q: udwords = undefined;
+//    var r: udwords = undefined;
+//    var sr: c_uint = undefined;
+//    // special cases, X is unknown, K != 0
+//    if (n[high] == 0) {
+//        if (d[high] == 0) {
+//            // 0 X
+//            // ---
+//            // 0 X
+//            if (const rem ?= maybe_rem) {
+//                *rem = n[low] % d[low];
+//            }
+//            return n[low] / d[low];
+//        }
+//        // 0 X
+//        // ---
+//        // K X
+//        if (const rem ?= maybe_rem) {
+//            *rem = n[low];
+//        }
+//        return 0;
+//    }
+//    // n[high] != 0
+//    if (d[low] == 0) {
+//        if (d[high] == 0) {
+//            // K X
+//            // ---
+//            // 0 0
+//            if (var rem ?= maybe_rem) {
+//                *rem = n[high] % d[low];
+//            }
+//            return n[high] / d[low];
+//        }
+//        // d[high] != 0
+//        if (n[low] == 0) {
+//            // K 0
+//            // ---
+//            // K 0
+//            if (var rem ?= maybe_rem) {
+//                r[high] = n[high] % d[high];
+//                r[low] = 0;
+//                *rem = *(&du_int)(&r[0]);
+//            }
+//            return n[high] / d[high];
+//        }
+//        // K K
+//        // ---
+//        // K 0
+//        // if d is a power of 2
+//        if ((d[high] & (d[high] - 1)) == 0) {
+//            if (var rem ?= maybe_rem) {
+//                r[low] = n[low];
+//                r[high] = n[high] & (d[high] - 1);
+//                *rem = *(&du_int)(&r[0]);
+//            }
+//            return n[high] >> @ctz(@typeOf(d[high]), d[high]);
+//        }
+//        // K K
+//        // ---
+//        // K 0
+//        sr = @clz(su_int, d[high]) - @clz(su_int, n[high]);
+//        // 0 <= sr <= n_uword_bits - 2 or sr large
+//        if (sr > n_uword_bits - 2) {
+//            if (var rem ?= maybe_rem) {
+//                *rem = *(&du_int)(&n[0]);
+//            }
+//            return 0;
+//        }
+//        sr += 1;
+//        // 1 <= sr <= n_uword_bits - 1
+//        // q.all = n.all << (n_udword_bits - sr);
+//        q[low] = 0;
+//        q[high] = n[low] << (n_uword_bits - sr);
+//        // r.all = n.all >> sr;
+//        r[high] = n[high] >> sr;
+//        r[low] = (n[high] << (n_uword_bits - sr)) | (n[low] >> sr);
+//    } else {
+//        // d[low] != 0
+//        if (d[high] == 0) {
+//            // K X
+//            // ---
+//            // 0 K
+//            // if d is a power of 2
+//            if ((d[low] & (d[low] - 1)) == 0) {
+//                if (var rem ?= maybe_rem) {
+//                    *rem = n[low] & (d[low] - 1);
+//                }
+//                if (d[low] == 1) {
+//                    return *(&du_int)(&n[0]);
+//                }
+//                sr = @ctz(@typeOf(d[low]), d[low]);
+//                q[high] = n[high] >> sr;
+//                q[low] = (n[high] << (n_uword_bits - sr)) | (n[low] >> sr);
+//                return *(&du_int)(&q[0]);
+//            }
+//            // K X
+//            // ---
+//            // 0 K
+//            sr = 1 + n_uword_bits + @clz(su_int, d[low]) - @clz(su_int, n[high]);
+//            // 2 <= sr <= n_udword_bits - 1
+//            // q.all = n.all << (n_udword_bits - sr);
+//            // r.all = n.all >> sr;
+//            if (sr == n_uword_bits) {
+//                q[low] = 0;
+//                q[high] = n[low];
+//                r[high] = 0;
+//                r[low] = n[high];
+//            } else if (sr < n_uword_bits) {
+//                // 2 <= sr <= n_uword_bits - 1
+//                q[low] = 0;
+//                q[high] = n[low] << (n_uword_bits - sr);
+//                r[high] = n[high] >> sr;
+//                r[low] = (n[high] << (n_uword_bits - sr)) | (n[low] >> sr);
+//            } else {
+//                // n_uword_bits + 1 <= sr <= n_udword_bits - 1
+//                q[low] = n[low] << (n_udword_bits - sr);
+//                q[high] = (n[high] << (n_udword_bits - sr)) |
+//                    (n[low] >> (sr - n_uword_bits));
+//                r[high] = 0;
+//                r[low] = n[high] >> (sr - n_uword_bits);
+//            }
+//        } else {
+//            // K X
+//            // ---
+//            // K K
+//            sr = @clz(su_int, d[high]) - @clz(su_int, n[high]);
+//            // 0 <= sr <= n_uword_bits - 1 or sr large
+//            if (sr > n_uword_bits - 1) {
+//                if (var rem ?= maybe_rem) {
+//                    *rem = *(&du_int)(&n[0]);
+//                }
+//                return 0;
+//            }
+//            sr += 1;
+//            // 1 <= sr <= n_uword_bits
+//            //  q.all = n.all << (n_udword_bits - sr);
+//            q[low] = 0;
+//            if (sr == n_uword_bits) {
+//                q[high] = n[low];
+//                r[high] = 0;
+//                r[low] = n[high];
+//            } else {
+//                q[high] = n[low] << (n_uword_bits - sr);
+//                r[high] = n[high] >> sr;
+//                r[low] = (n[high] << (n_uword_bits - sr)) | (n[low] >> sr);
+//            }
+//        }
+//    }
+//    // Not a special case
+//    // q and r are initialized with:
+//    // q.all = n.all << (n_udword_bits - sr);
+//    // r.all = n.all >> sr;
+//    // 1 <= sr <= n_udword_bits - 1
+//    var carry: su_int = 0;
+//    while (sr > 0) {
+//        // r:q = ((r:q)  << 1) | carry
+//        r[high] = (r[high] << 1) | (r[low]  >> (n_uword_bits - 1));
+//        r[low]  = (r[low]  << 1) | (q[high] >> (n_uword_bits - 1));
+//        q[high] = (q[high] << 1) | (q[low]  >> (n_uword_bits - 1));
+//        q[low]  = (q[low]  << 1) | carry;
+//        // carry = 0;
+//        // if (r.all >= d.all)
+//        // {
+//        //      r.all -= d.all;
+//        //      carry = 1;
+//        // }
+//        const s: di_int = (di_int)(*(&du_int)(&d[0]) - *(&du_int)(&r[0]) - 1) >> (n_udword_bits - 1);
+//        carry = su_int(s & 1);
+//        *(&du_int)(&r[0]) -= *(&du_int)(&d[0]) & u64(s);
+//
+//        sr -= 1;
+//    }
+//    *(&du_int)(&q[0]) = (*(&du_int)(&q[0]) << 1) | u64(carry);
+//    if (var rem ?= maybe_rem) {
+//        *rem = *(&du_int)(&r[0]);
+//    }
+//    return *(&du_int)(&q[0]);
+//}
+//
+//export fn __umoddi3(a: du_int, b: du_int) -> du_int {
+//    @setDebugSafety(this, false);
+//
+//    var r: du_int = undefined;
+//    __udivmoddi4(a, b, &r);
+//    return r;
+//}
+//
+//fn test_umoddi3() {
+//    @setFnTest(this, true);
+//
+//    test_one_umoddi3(0, 1, 0);
+//    test_one_umoddi3(2, 1, 0);
+//    test_one_umoddi3(0x8000000000000000, 1, 0x0);
+//    test_one_umoddi3(0x8000000000000000, 2, 0x0);
+//    test_one_umoddi3(0xFFFFFFFFFFFFFFFF, 2, 0x1);
+//}
+//
+//fn test_one_umoddi3(a: du_int, b: du_int, expected_r: du_int) {
+//    const r = __umoddi3(a, b);
+//    assert(r == expected_r);
+//}
+//
+//fn test_udivmoddi4() {
+//    @setFnTest(this, true);
+//
+//    const cases = [][4]du_int {
+//        []du_int{0x0000000000000000, 0x0000000000000001, 0x0000000000000000, 0x0000000000000000},
+//        []du_int{0x0000000080000000, 0x0000000100000001, 0x0000000000000000, 0x0000000080000000},
+//        []du_int{0x7FFFFFFF00000001, 0x0000000000000001, 0x7FFFFFFF00000001, 0x0000000000000000},
+//        []du_int{0x7FFFFFFF7FFFFFFF, 0xFFFFFFFFFFFFFFFF, 0x0000000000000000, 0x7FFFFFFF7FFFFFFF},
+//        []du_int{0x8000000000000002, 0xFFFFFFFFFFFFFFFE, 0x0000000000000000, 0x8000000000000002},
+//        []du_int{0x80000000FFFFFFFD, 0xFFFFFFFFFFFFFFFD, 0x0000000000000000, 0x80000000FFFFFFFD},
+//        []du_int{0xFFFFFFFD00000010, 0xFFFFFFFF80000000, 0x0000000000000000, 0xFFFFFFFD00000010},
+//        []du_int{0xFFFFFFFDFFFFFFFF, 0xFFFFFFFF7FFFFFFF, 0x0000000000000000, 0xFFFFFFFDFFFFFFFF},
+//        []du_int{0xFFFFFFFE0747AE14, 0xFFFFFFFF0747AE14, 0x0000000000000000, 0xFFFFFFFE0747AE14},
+//        []du_int{0xFFFFFFFF00000001, 0xFFFFFFFF078644FA, 0x0000000000000000, 0xFFFFFFFF00000001},
+//        []du_int{0xFFFFFFFF80000000, 0xFFFFFFFF00000010, 0x0000000000000001, 0x000000007FFFFFF0},
+//        []du_int{0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0x0000000000000001, 0x0000000000000000},
+//    };
+//
+//    for (cases) |case| {
+//        test_one_udivmoddi4(case[0], case[1], case[2], case[3]);
+//    }
+//}
+//
+//fn test_one_udivmoddi4(a: du_int, b: du_int, expected_q: du_int, expected_r: du_int) {
+//    var r: du_int = undefined;
+//    const q = __udivmoddi4(a, b, &r);
+//    assert(q == expected_q);
+//    assert(r == expected_r);
+//}
+//
+//fn assert(b: bool) {
+//    if (!b) @unreachable();
+//}
test/self_hosted2.zig
@@ -1,13 +1,29 @@
 pub const SYS_write = 1;
 pub const SYS_exit = 60;
 pub const stdout_fileno = 1;
-const text = "hello\n";
+
+// normal comment
+/// this is a documentation comment
+/// doc comment line 2
+fn emptyFunctionWithComments() {
+}
+
+export fn disabledExternFn() {
+    @setFnVisible(this, false);
+}
+
+fn runAllTests() {
+    emptyFunctionWithComments();
+    disabledExternFn();
+}
 
 export nakedcc fn _start() -> unreachable {
     myMain();
 }
 
 fn myMain() -> unreachable {
+    runAllTests();
+    const text = "OK\n";
     write(stdout_fileno, &text[0], text.len);
     exit(0);
 }