From 70bced5dcffccc2f8029d8c3d7f2d18b48d993f5 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 25 Jul 2019 01:47:56 -0400 Subject: [PATCH] implement `@frame` and `@Frame` --- BRANCH_TODO | 4 ++ src/all_types.hpp | 16 +++++-- src/codegen.cpp | 16 +++---- src/ir.cpp | 71 +++++++++++++++++++++-------- src/ir_print.cpp | 17 +++++-- test/stage1/behavior/coroutines.zig | 8 ++++ 6 files changed, 96 insertions(+), 36 deletions(-) create mode 100644 BRANCH_TODO diff --git a/BRANCH_TODO b/BRANCH_TODO new file mode 100644 index 000000000..a22620a62 --- /dev/null +++ b/BRANCH_TODO @@ -0,0 +1,4 @@ + * await + * await of a non async function + * async call on a non async function + * safety for resuming when it is awaiting diff --git a/src/all_types.hpp b/src/all_types.hpp index d67356b17..ebdde4642 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1435,8 +1435,6 @@ enum BuiltinFnId { BuiltinFnIdErrName, BuiltinFnIdBreakpoint, BuiltinFnIdReturnAddress, - BuiltinFnIdFrameAddress, - BuiltinFnIdHandle, BuiltinFnIdEmbedFile, BuiltinFnIdCmpxchgWeak, BuiltinFnIdCmpxchgStrong, @@ -1507,6 +1505,9 @@ enum BuiltinFnId { BuiltinFnIdAtomicLoad, BuiltinFnIdHasDecl, BuiltinFnIdUnionInit, + BuiltinFnIdFrameAddress, + BuiltinFnIdFrameType, + BuiltinFnIdFrameHandle, }; struct BuiltinFnEntry { @@ -2252,7 +2253,8 @@ enum IrInstructionId { IrInstructionIdBreakpoint, IrInstructionIdReturnAddress, IrInstructionIdFrameAddress, - IrInstructionIdHandle, + IrInstructionIdFrameHandle, + IrInstructionIdFrameType, IrInstructionIdAlignOf, IrInstructionIdOverflowOp, IrInstructionIdTestErrSrc, @@ -3038,10 +3040,16 @@ struct IrInstructionFrameAddress { IrInstruction base; }; -struct IrInstructionHandle { +struct IrInstructionFrameHandle { IrInstruction base; }; +struct IrInstructionFrameType { + IrInstruction base; + + IrInstruction *fn; +}; + enum IrOverflowOp { IrOverflowOpAdd, IrOverflowOpSub, diff --git a/src/codegen.cpp b/src/codegen.cpp index 34f4aa1cc..d6f19d6a4 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4457,10 +4457,8 @@ static LLVMValueRef ir_render_frame_address(CodeGen *g, IrExecutable *executable return LLVMBuildPtrToInt(g->builder, ptr_val, g->builtin_types.entry_usize->llvm_type, ""); } -static LLVMValueRef ir_render_handle(CodeGen *g, IrExecutable *executable, - IrInstructionHandle *instruction) -{ - zig_panic("TODO @handle() codegen"); +static LLVMValueRef ir_render_handle(CodeGen *g, IrExecutable *executable, IrInstructionFrameHandle *instruction) { + return g->cur_ret_ptr; } static LLVMValueRef render_shl_with_overflow(CodeGen *g, IrInstructionOverflowOp *instruction) { @@ -5008,6 +5006,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdBitCastSrc: case IrInstructionIdTestErrSrc: case IrInstructionIdUnionInitNamedField: + case IrInstructionIdFrameType: zig_unreachable(); case IrInstructionIdDeclVarGen: @@ -5086,8 +5085,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_return_address(g, executable, (IrInstructionReturnAddress *)instruction); case IrInstructionIdFrameAddress: return ir_render_frame_address(g, executable, (IrInstructionFrameAddress *)instruction); - case IrInstructionIdHandle: - return ir_render_handle(g, executable, (IrInstructionHandle *)instruction); + case IrInstructionIdFrameHandle: + return ir_render_handle(g, executable, (IrInstructionFrameHandle *)instruction); case IrInstructionIdOverflowOp: return ir_render_overflow_op(g, executable, (IrInstructionOverflowOp *)instruction); case IrInstructionIdTestErrGen: @@ -6754,8 +6753,6 @@ static BuiltinFnEntry *create_builtin_fn(CodeGen *g, BuiltinFnId id, const char static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdBreakpoint, "breakpoint", 0); create_builtin_fn(g, BuiltinFnIdReturnAddress, "returnAddress", 0); - create_builtin_fn(g, BuiltinFnIdFrameAddress, "frameAddress", 0); - create_builtin_fn(g, BuiltinFnIdHandle, "handle", 0); create_builtin_fn(g, BuiltinFnIdMemcpy, "memcpy", 3); create_builtin_fn(g, BuiltinFnIdMemset, "memset", 3); create_builtin_fn(g, BuiltinFnIdSizeof, "sizeOf", 1); @@ -6856,6 +6853,9 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdThis, "This", 0); create_builtin_fn(g, BuiltinFnIdHasDecl, "hasDecl", 2); create_builtin_fn(g, BuiltinFnIdUnionInit, "unionInit", 3); + create_builtin_fn(g, BuiltinFnIdFrameHandle, "frame", 0); + create_builtin_fn(g, BuiltinFnIdFrameType, "Frame", 1); + create_builtin_fn(g, BuiltinFnIdFrameAddress, "frameAddress", 0); } static const char *bool_to_str(bool b) { diff --git a/src/ir.cpp b/src/ir.cpp index cb4a90c31..93d559b44 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -755,8 +755,12 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionFrameAddress *) return IrInstructionIdFrameAddress; } -static constexpr IrInstructionId ir_instruction_id(IrInstructionHandle *) { - return IrInstructionIdHandle; +static constexpr IrInstructionId ir_instruction_id(IrInstructionFrameHandle *) { + return IrInstructionIdFrameHandle; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionFrameType *) { + return IrInstructionIdFrameType; } static constexpr IrInstructionId ir_instruction_id(IrInstructionAlignOf *) { @@ -2362,7 +2366,16 @@ static IrInstruction *ir_build_frame_address(IrBuilder *irb, Scope *scope, AstNo } static IrInstruction *ir_build_handle(IrBuilder *irb, Scope *scope, AstNode *source_node) { - IrInstructionHandle *instruction = ir_build_instruction(irb, scope, source_node); + IrInstructionFrameHandle *instruction = ir_build_instruction(irb, scope, source_node); + return &instruction->base; +} + +static IrInstruction *ir_build_frame_type(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *fn) { + IrInstructionFrameType *instruction = ir_build_instruction(irb, scope, source_node); + instruction->fn = fn; + + ir_ref_instruction(fn, irb->current_basic_block); + return &instruction->base; } @@ -3358,11 +3371,6 @@ static ScopeDeferExpr *get_scope_defer_expr(Scope *scope) { return nullptr; } -static bool exec_is_async(IrExecutable *exec) { - ZigFn *fn_entry = exec_fn_entry(exec); - return fn_entry != nullptr && fn_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync; -} - static IrInstruction *ir_gen_async_return(IrBuilder *irb, Scope *scope, AstNode *node, IrInstruction *return_value, bool is_generated_code) { @@ -4278,8 +4286,6 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return irb->codegen->invalid_instruction; } - bool is_async = exec_is_async(irb->exec); - switch (builtin_fn->id) { case BuiltinFnIdInvalid: zig_unreachable(); @@ -4902,16 +4908,21 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return ir_lval_wrap(irb, scope, ir_build_return_address(irb, scope, node), lval, result_loc); case BuiltinFnIdFrameAddress: return ir_lval_wrap(irb, scope, ir_build_frame_address(irb, scope, node), lval, result_loc); - case BuiltinFnIdHandle: + case BuiltinFnIdFrameHandle: if (!irb->exec->fn_entry) { add_node_error(irb->codegen, node, buf_sprintf("@handle() called outside of function definition")); return irb->codegen->invalid_instruction; } - if (!is_async) { - add_node_error(irb->codegen, node, buf_sprintf("@handle() in non-async function")); - return irb->codegen->invalid_instruction; - } return ir_lval_wrap(irb, scope, ir_build_handle(irb, scope, node), lval, result_loc); + case BuiltinFnIdFrameType: { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_instruction) + return arg0_value; + + IrInstruction *frame_type = ir_build_frame_type(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, frame_type, lval, result_loc); + } case BuiltinFnIdAlignOf: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); @@ -21726,8 +21737,25 @@ static IrInstruction *ir_analyze_instruction_frame_address(IrAnalyze *ira, IrIns return result; } -static IrInstruction *ir_analyze_instruction_handle(IrAnalyze *ira, IrInstructionHandle *instruction) { - zig_panic("TODO anlayze @handle()"); +static IrInstruction *ir_analyze_instruction_frame_handle(IrAnalyze *ira, IrInstructionFrameHandle *instruction) { + ZigFn *fn = exec_fn_entry(ira->new_irb.exec); + ir_assert(fn != nullptr, &instruction->base); + + ZigType *frame_type = get_coro_frame_type(ira->codegen, fn); + ZigType *ptr_frame_type = get_pointer_to_type(ira->codegen, frame_type, false); + + IrInstruction *result = ir_build_handle(&ira->new_irb, instruction->base.scope, instruction->base.source_node); + result->value.type = ptr_frame_type; + return result; +} + +static IrInstruction *ir_analyze_instruction_frame_type(IrAnalyze *ira, IrInstructionFrameType *instruction) { + ZigFn *fn = ir_resolve_fn(ira, instruction->fn->child); + if (fn == nullptr) + return ira->codegen->invalid_instruction; + + ZigType *ty = get_coro_frame_type(ira->codegen, fn); + return ir_const_type(ira, &instruction->base, ty); } static IrInstruction *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstructionAlignOf *instruction) { @@ -24355,8 +24383,10 @@ static IrInstruction *ir_analyze_instruction_base(IrAnalyze *ira, IrInstruction return ir_analyze_instruction_return_address(ira, (IrInstructionReturnAddress *)instruction); case IrInstructionIdFrameAddress: return ir_analyze_instruction_frame_address(ira, (IrInstructionFrameAddress *)instruction); - case IrInstructionIdHandle: - return ir_analyze_instruction_handle(ira, (IrInstructionHandle *)instruction); + case IrInstructionIdFrameHandle: + return ir_analyze_instruction_frame_handle(ira, (IrInstructionFrameHandle *)instruction); + case IrInstructionIdFrameType: + return ir_analyze_instruction_frame_type(ira, (IrInstructionFrameType *)instruction); case IrInstructionIdAlignOf: return ir_analyze_instruction_align_of(ira, (IrInstructionAlignOf *)instruction); case IrInstructionIdOverflowOp: @@ -24650,7 +24680,8 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdAlignOf: case IrInstructionIdReturnAddress: case IrInstructionIdFrameAddress: - case IrInstructionIdHandle: + case IrInstructionIdFrameHandle: + case IrInstructionIdFrameType: case IrInstructionIdTestErrSrc: case IrInstructionIdTestErrGen: case IrInstructionIdFnProto: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index e14647ea8..5b3bba227 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -906,8 +906,14 @@ static void ir_print_frame_address(IrPrint *irp, IrInstructionFrameAddress *inst fprintf(irp->f, "@frameAddress()"); } -static void ir_print_handle(IrPrint *irp, IrInstructionHandle *instruction) { - fprintf(irp->f, "@handle()"); +static void ir_print_handle(IrPrint *irp, IrInstructionFrameHandle *instruction) { + fprintf(irp->f, "@frame()"); +} + +static void ir_print_frame_type(IrPrint *irp, IrInstructionFrameType *instruction) { + fprintf(irp->f, "@Frame("); + ir_print_other_instruction(irp, instruction->fn); + fprintf(irp->f, ")"); } static void ir_print_return_address(IrPrint *irp, IrInstructionReturnAddress *instruction) { @@ -1764,8 +1770,11 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdFrameAddress: ir_print_frame_address(irp, (IrInstructionFrameAddress *)instruction); break; - case IrInstructionIdHandle: - ir_print_handle(irp, (IrInstructionHandle *)instruction); + case IrInstructionIdFrameHandle: + ir_print_handle(irp, (IrInstructionFrameHandle *)instruction); + break; + case IrInstructionIdFrameType: + ir_print_frame_type(irp, (IrInstructionFrameType *)instruction); break; case IrInstructionIdAlignOf: ir_print_align_of(irp, (IrInstructionAlignOf *)instruction); diff --git a/test/stage1/behavior/coroutines.zig b/test/stage1/behavior/coroutines.zig index 7188e7af8..33246f761 100644 --- a/test/stage1/behavior/coroutines.zig +++ b/test/stage1/behavior/coroutines.zig @@ -79,15 +79,23 @@ test "local variable in async function" { test "calling an inferred async function" { const S = struct { + var x: i32 = 1; + var other_frame: *@Frame(other) = undefined; + fn doTheTest() void { const p = async first(); + expect(x == 1); + resume other_frame.*; + expect(x == 2); } fn first() void { other(); } fn other() void { + other_frame = @frame(); suspend; + x += 1; } }; S.doTheTest();