diff --git a/src/all_types.hpp b/src/all_types.hpp index 1de1bcdee..2ee119fb8 100644 --- a/src/all_types.hpp +++ b/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; diff --git a/src/ast_render.cpp b/src/ast_render.cpp index 3c59dce99..38f8099ac 100644 --- a/src/ast_render.cpp +++ b/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: diff --git a/src/codegen.cpp b/src/codegen.cpp index 04a46c153..13d783669 100644 --- a/src/codegen.cpp +++ b/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(); diff --git a/src/ir.cpp b/src/ir.cpp index 99be090e8..40377958c 100644 --- a/src/ir.cpp +++ b/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(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(); //} diff --git a/src/ir_print.cpp b/src/ir_print.cpp index a471384cb..93a4c0e58 100644 --- a/src/ir_print.cpp +++ b/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; diff --git a/test/self_hosted2.zig b/test/self_hosted2.zig index ba1171b57..60b12bfac 100644 --- a/test/self_hosted2.zig +++ b/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 assert(ok: bool) { - if (!ok) @unreachable(); +fn myMain() -> unreachable { + write(stdout_fileno, &text[0], text.len); + exit(0); } -fn testAdd() { - @setFnTest(this, true); - - assert(add(2, 3) == 5); +pub inline fn syscall1(number: usize, arg1: usize) -> usize { + asm volatile ("syscall" + : [ret] "={rax}" (-> usize) + : [number] "{rax}" (number), + [arg1] "{rdi}" (arg1) + : "rcx", "r11") } + +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") +} + +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() +} +