Commit bf7cde62c5

Andrew Kelley <superjoe30@gmail.com>
2016-11-19 02:57:27
IR: support setDebugSafety builtin function
1 parent ed31ae8
src/all_types.hpp
@@ -1449,6 +1449,7 @@ enum IrInstructionId {
     IrInstructionIdToPtrType,
     IrInstructionIdPtrTypeChild,
     IrInstructionIdSetFnTest,
+    IrInstructionIdSetDebugSafety,
     IrInstructionIdArrayType,
     IrInstructionIdSliceType,
     IrInstructionIdAsm,
@@ -1705,6 +1706,13 @@ struct IrInstructionSetFnTest {
     IrInstruction *is_test;
 };
 
+struct IrInstructionSetDebugSafety {
+    IrInstruction base;
+
+    IrInstruction *scope_value;
+    IrInstruction *debug_safety_on;
+};
+
 struct IrInstructionArrayType {
     IrInstruction base;
 
src/ast_render.cpp
@@ -669,6 +669,17 @@ static void render_node(AstRender *ar, AstNode *node) {
                 render_node(ar, node->data.while_expr.body);
                 break;
             }
+        case NodeTypeThisLiteral:
+            {
+                fprintf(ar->f, "this");
+                break;
+            }
+        case NodeTypeBoolLiteral:
+            {
+                const char *bool_str = node->data.bool_literal.value ? "true" : "false";
+                fprintf(ar->f, "%s", bool_str);
+                break;
+            }
         case NodeTypeFnDecl:
         case NodeTypeParamDecl:
         case NodeTypeErrorValueDecl:
@@ -677,10 +688,8 @@ static void render_node(AstRender *ar, AstNode *node) {
         case NodeTypeStructField:
         case NodeTypeStructValueField:
         case NodeTypeUse:
-        case NodeTypeBoolLiteral:
         case NodeTypeNullLiteral:
         case NodeTypeZeroesLiteral:
-        case NodeTypeThisLiteral:
         case NodeTypeIfBoolExpr:
         case NodeTypeIfVarExpr:
         case NodeTypeForExpr:
src/codegen.cpp
@@ -1681,6 +1681,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
         case IrInstructionIdPtrTypeChild:
         case IrInstructionIdFieldPtr:
         case IrInstructionIdSetFnTest:
+        case IrInstructionIdSetDebugSafety:
         case IrInstructionIdArrayType:
         case IrInstructionIdSliceType:
             zig_unreachable();
src/ir.cpp
@@ -201,6 +201,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionSetFnTest *) {
     return IrInstructionIdSetFnTest;
 }
 
+static constexpr IrInstructionId ir_instruction_id(IrInstructionSetDebugSafety *) {
+    return IrInstructionIdSetDebugSafety;
+}
+
 static constexpr IrInstructionId ir_instruction_id(IrInstructionArrayType *) {
     return IrInstructionIdArrayType;
 }
@@ -786,6 +790,19 @@ static IrInstruction *ir_build_set_fn_test(IrBuilder *irb, AstNode *source_node,
     return &instruction->base;
 }
 
+static IrInstruction *ir_build_set_debug_safety(IrBuilder *irb, AstNode *source_node,
+        IrInstruction *scope_value, IrInstruction *debug_safety_on)
+{
+    IrInstructionSetDebugSafety *instruction = ir_build_instruction<IrInstructionSetDebugSafety>(irb, source_node);
+    instruction->scope_value = scope_value;
+    instruction->debug_safety_on = debug_safety_on;
+
+    ir_ref_instruction(scope_value);
+    ir_ref_instruction(debug_safety_on);
+
+    return &instruction->base;
+}
+
 static IrInstruction *ir_build_array_type(IrBuilder *irb, AstNode *source_node, IrInstruction *size,
         IrInstruction *child_type)
 {
@@ -1291,6 +1308,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 BuiltinFnIdSetDebugSafety:
+            {
+                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_debug_safety(irb, node, arg0_value, arg1_value);
+            }
         case BuiltinFnIdMemcpy:
         case BuiltinFnIdMemset:
         case BuiltinFnIdSizeof:
@@ -1325,7 +1356,6 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, AstNode *node) {
         case BuiltinFnIdSetFnVisible:
         case BuiltinFnIdSetFnStaticEval:
         case BuiltinFnIdSetFnNoInline:
-        case BuiltinFnIdSetDebugSafety:
             zig_panic("TODO IR gen more builtin functions");
     }
     zig_unreachable();
@@ -2260,6 +2290,15 @@ static TypeTableEntry *ir_resolve_type(IrAnalyze *ira, IrInstruction *type_value
     return const_val->data.x_type;
 }
 
+static ConstExprValue *ir_resolve_const(IrAnalyze *ira, IrInstruction *value) {
+    if (value->static_value.special != ConstValSpecialStatic) {
+        add_node_error(ira->codegen, value->source_node,
+                buf_sprintf("unable to evaluate constant expression"));
+        return nullptr;
+    }
+    return &value->static_value;
+}
+
 static bool ir_resolve_bool(IrAnalyze *ira, IrInstruction *bool_value, bool *out) {
     if (bool_value == ira->codegen->invalid_instruction)
         return false;
@@ -2273,12 +2312,9 @@ static bool ir_resolve_bool(IrAnalyze *ira, IrInstruction *bool_value, bool *out
         return false;
     }
 
-    ConstExprValue *const_val = &bool_value->static_value;
-    if (const_val->special == ConstValSpecialRuntime) {
-        add_node_error(ira->codegen, bool_value->source_node,
-                buf_sprintf("unable to evaluate constant expression"));
+    ConstExprValue *const_val = ir_resolve_const(ira, bool_value);
+    if (!const_val)
         return false;
-    }
 
     *out = const_val->data.x_bool;
     return true;
@@ -4027,12 +4063,67 @@ static TypeTableEntry *ir_analyze_instruction_set_fn_test(IrAnalyze *ira,
     }
     fn_entry->fn_test_set_node = source_node;
 
-    ira->codegen->test_fn_count += 1;
+    if (fn_entry->is_test)
+        ira->codegen->test_fn_count += 1;
 
     ir_build_const_from(ira, &set_fn_test_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)
+{
+    IrInstruction *target_instruction = set_debug_safety_instruction->scope_value->other;
+    TypeTableEntry *target_type = target_instruction->type_entry;
+    if (target_type->id == TypeTableEntryIdInvalid)
+        return ira->codegen->builtin_types.entry_invalid;
+    ConstExprValue *target_val = ir_resolve_const(ira, target_instruction);
+    if (!target_val)
+        return ira->codegen->builtin_types.entry_invalid;
+
+    BlockContext *target_context;
+    if (target_type->id == TypeTableEntryIdBlock) {
+        target_context = target_val->data.x_block;
+    } else if (target_type->id == TypeTableEntryIdFn) {
+        target_context = target_val->data.x_fn->fn_def_node->data.fn_def.block_context;
+    } else if (target_type->id == TypeTableEntryIdMetaType) {
+        TypeTableEntry *type_arg = target_val->data.x_type;
+        if (type_arg->id == TypeTableEntryIdStruct) {
+            target_context = type_arg->data.structure.block_context;
+        } else if (type_arg->id == TypeTableEntryIdEnum) {
+            target_context = type_arg->data.enumeration.block_context;
+        } else if (type_arg->id == TypeTableEntryIdUnion) {
+            target_context = type_arg->data.unionation.block_context;
+        } else {
+            add_node_error(ira->codegen, target_instruction->source_node,
+                buf_sprintf("expected scope reference, got type '%s'", buf_ptr(&type_arg->name)));
+            return ira->codegen->builtin_types.entry_invalid;
+        }
+    } else {
+        add_node_error(ira->codegen, target_instruction->source_node,
+            buf_sprintf("expected scope reference, got type '%s'", buf_ptr(&target_type->name)));
+        return ira->codegen->builtin_types.entry_invalid;
+    }
+
+    IrInstruction *debug_safety_on_value = set_debug_safety_instruction->debug_safety_on->other;
+    bool want_debug_safety;
+    if (!ir_resolve_bool(ira, debug_safety_on_value, &want_debug_safety))
+        return ira->codegen->builtin_types.entry_invalid;
+
+    AstNode *source_node = set_debug_safety_instruction->base.source_node;
+    if (target_context->safety_set_node) {
+        ErrorMsg *msg = add_node_error(ira->codegen, source_node,
+                buf_sprintf("function test attribute set twice"));
+        add_error_note(ira->codegen, msg, target_context->safety_set_node, buf_sprintf("first set here"));
+        return ira->codegen->builtin_types.entry_invalid;
+    }
+    target_context->safety_set_node = source_node;
+    target_context->safety_off = !want_debug_safety;
+
+    ir_build_const_from(ira, &set_debug_safety_instruction->base, false);
+    return ira->codegen->builtin_types.entry_void;
+}
+
 static TypeTableEntry *ir_analyze_instruction_slice_type(IrAnalyze *ira,
         IrInstructionSliceType *slice_type_instruction)
 {
@@ -4164,6 +4255,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 IrInstructionIdSetDebugSafety:
+            return ir_analyze_instruction_set_debug_safety(ira, (IrInstructionSetDebugSafety *)instruction);
         case IrInstructionIdSliceType:
             return ir_analyze_instruction_slice_type(ira, (IrInstructionSliceType *)instruction);
         case IrInstructionIdAsm:
@@ -4257,6 +4350,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
         case IrInstructionIdReturn:
         case IrInstructionIdUnreachable:
         case IrInstructionIdSetFnTest:
+        case IrInstructionIdSetDebugSafety:
             return true;
         case IrInstructionIdPhi:
         case IrInstructionIdUnOp:
@@ -4854,64 +4948,6 @@ IrInstruction *ir_exec_const_result(IrExecutable *exec) {
 //    return g->builtin_types.entry_void;
 //}
 //
-//static TypeTableEntry *analyze_set_debug_safety(CodeGen *g, ImportTableEntry *import,
-//        BlockContext *parent_context, AstNode *node)
-//{
-//    AstNode **target_node = &node->data.fn_call_expr.params.at(0);
-//    AstNode **value_node = &node->data.fn_call_expr.params.at(1);
-//
-//    TypeTableEntry *target_type = analyze_expression(g, import, parent_context, nullptr, *target_node);
-//    BlockContext *target_context;
-//    ConstExprValue *const_val = &get_resolved_expr(*target_node)->const_val;
-//    if (target_type->id == TypeTableEntryIdInvalid) {
-//        return g->builtin_types.entry_invalid;
-//    }
-//    if (!const_val->ok) {
-//        add_node_error(g, *target_node, buf_sprintf("unable to evaluate constant expression"));
-//        return g->builtin_types.entry_invalid;
-//    }
-//    if (target_type->id == TypeTableEntryIdBlock) {
-//        target_context = const_val->data.x_block;
-//    } else if (target_type->id == TypeTableEntryIdFn) {
-//        target_context = const_val->data.x_fn->fn_def_node->data.fn_def.block_context;
-//    } else if (target_type->id == TypeTableEntryIdMetaType) {
-//        TypeTableEntry *type_arg = const_val->data.x_type;
-//        if (type_arg->id == TypeTableEntryIdStruct) {
-//            target_context = type_arg->data.structure.block_context;
-//        } else if (type_arg->id == TypeTableEntryIdEnum) {
-//            target_context = type_arg->data.enumeration.block_context;
-//        } else if (type_arg->id == TypeTableEntryIdUnion) {
-//            target_context = type_arg->data.unionation.block_context;
-//        } else {
-//            add_node_error(g, *target_node,
-//                buf_sprintf("expected scope reference, got type '%s'", buf_ptr(&type_arg->name)));
-//            return g->builtin_types.entry_invalid;
-//        }
-//    } else {
-//        add_node_error(g, *target_node,
-//            buf_sprintf("expected scope reference, got type '%s'", buf_ptr(&target_type->name)));
-//        return g->builtin_types.entry_invalid;
-//    }
-//
-//    bool want_debug_safety;
-//    bool ok = resolve_const_expr_bool(g, import, parent_context, value_node, &want_debug_safety);
-//    if (!ok) {
-//        return g->builtin_types.entry_invalid;
-//    }
-//
-//    if (target_context->safety_set_node) {
-//        ErrorMsg *msg = add_node_error(g, node, buf_sprintf("debug safety for scope set twice"));
-//        add_error_note(g, msg, target_context->safety_set_node, buf_sprintf("first set here"));
-//        return g->builtin_types.entry_invalid;
-//    }
-//    target_context->safety_set_node = node;
-//
-//    target_context->safety_off = !want_debug_safety;
-//
-//    return g->builtin_types.entry_void;
-//}
-
-
 //static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
 //        TypeTableEntry *expected_type, AstNode *node)
 //{
@@ -5215,8 +5251,6 @@ IrInstruction *ir_exec_const_result(IrExecutable *exec) {
 //            return analyze_set_fn_static_eval(g, import, context, node);
 //        case BuiltinFnIdSetFnVisible:
 //            return analyze_set_fn_visible(g, import, context, node);
-//        case BuiltinFnIdSetDebugSafety:
-//            return analyze_set_debug_safety(g, import, context, node);
 //    }
 //    zig_unreachable();
 //}
src/ir_print.cpp
@@ -409,6 +409,14 @@ static void ir_print_set_fn_test(IrPrint *irp, IrInstructionSetFnTest *instructi
     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);
+    fprintf(irp->f, ", ");
+    ir_print_other_instruction(irp, instruction->debug_safety_on);
+    fprintf(irp->f, ")");
+}
+
 static void ir_print_array_type(IrPrint *irp, IrInstructionArrayType *instruction) {
     fprintf(irp->f, "[");
     ir_print_other_instruction(irp, instruction->size);
@@ -541,6 +549,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
         case IrInstructionIdSetFnTest:
             ir_print_set_fn_test(irp, (IrInstructionSetFnTest *)instruction);
             break;
+        case IrInstructionIdSetDebugSafety:
+            ir_print_set_debug_safety(irp, (IrInstructionSetDebugSafety *)instruction);
+            break;
         case IrInstructionIdArrayType:
             ir_print_array_type(irp, (IrInstructionArrayType *)instruction);
             break;
test/self_hosted2.zig
@@ -1,13 +1,41 @@
-fn add(a: i32, b: i32) -> i32 {
-    a + b
+pub const SYS_write = 1;
+pub const SYS_exit = 60;
+pub const stdout_fileno = 1;
+const text = "hello\n";
+
+export nakedcc fn _start() -> unreachable {
+    myMain();
+}
+
+fn myMain() -> unreachable {
+    write(stdout_fileno, &text[0], text.len);
+    exit(0);
 }
 
-fn assert(ok: bool) {
-    if (!ok) @unreachable();
+pub inline fn syscall1(number: usize, arg1: usize) -> usize {
+    asm volatile ("syscall"
+        : [ret] "={rax}" (-> usize)
+        : [number] "{rax}" (number),
+            [arg1] "{rdi}" (arg1)
+        : "rcx", "r11")
 }
 
-fn testAdd() {
-    @setFnTest(this, true);
+pub inline fn syscall3(number: usize, arg1: usize, arg2: usize, arg3: usize) -> usize {
+    asm volatile ("syscall"
+        : [ret] "={rax}" (-> usize)
+        : [number] "{rax}" (number),
+            [arg1] "{rdi}" (arg1),
+            [arg2] "{rsi}" (arg2),
+            [arg3] "{rdx}" (arg3)
+        : "rcx", "r11")
+}
 
-    assert(add(2, 3) == 5);
+pub fn write(fd: i32, buf: &const u8, count: usize) -> usize {
+    syscall3(SYS_write, usize(fd), usize(buf), count)
 }
+
+pub fn exit(status: i32) -> unreachable {
+    syscall1(SYS_exit, usize(status));
+    @unreachable()
+}
+