From 538c0cd2250e08aad07784355b402cfae6145507 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 25 Jul 2019 15:05:55 -0400 Subject: [PATCH] implement `@frameSize` --- BRANCH_TODO | 1 + src/all_types.hpp | 16 +++++++ src/codegen.cpp | 13 ++++++ src/ir.cpp | 70 +++++++++++++++++++++++++++-- src/ir_print.cpp | 20 +++++++++ test/stage1/behavior/coroutines.zig | 26 +++++++++++ 6 files changed, 142 insertions(+), 4 deletions(-) diff --git a/BRANCH_TODO b/BRANCH_TODO index a22620a62..6ea57d217 100644 --- a/BRANCH_TODO +++ b/BRANCH_TODO @@ -2,3 +2,4 @@ * await of a non async function * async call on a non async function * safety for resuming when it is awaiting + * implicit cast of normal function to async function should be allowed when it is inferred to be async diff --git a/src/all_types.hpp b/src/all_types.hpp index ebdde4642..d30b3b8a8 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1508,6 +1508,7 @@ enum BuiltinFnId { BuiltinFnIdFrameAddress, BuiltinFnIdFrameType, BuiltinFnIdFrameHandle, + BuiltinFnIdFrameSize, }; struct BuiltinFnEntry { @@ -2255,6 +2256,8 @@ enum IrInstructionId { IrInstructionIdFrameAddress, IrInstructionIdFrameHandle, IrInstructionIdFrameType, + IrInstructionIdFrameSizeSrc, + IrInstructionIdFrameSizeGen, IrInstructionIdAlignOf, IrInstructionIdOverflowOp, IrInstructionIdTestErrSrc, @@ -3050,6 +3053,19 @@ struct IrInstructionFrameType { IrInstruction *fn; }; +struct IrInstructionFrameSizeSrc { + IrInstruction base; + + IrInstruction *fn; +}; + +struct IrInstructionFrameSizeGen { + IrInstruction base; + + IrInstruction *fn; + IrInstruction *frame_ptr; +}; + enum IrOverflowOp { IrOverflowOpAdd, IrOverflowOpSub, diff --git a/src/codegen.cpp b/src/codegen.cpp index d6f19d6a4..6fc152ad3 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4914,6 +4914,15 @@ static LLVMValueRef ir_render_coro_resume(CodeGen *g, IrExecutable *executable, return nullptr; } +static LLVMValueRef ir_render_frame_size(CodeGen *g, IrExecutable *executable, IrInstructionFrameSizeGen *instruction) { + LLVMValueRef fn_val = ir_llvm_value(g, instruction->fn); + LLVMValueRef frame_ptr = ir_llvm_value(g, instruction->frame_ptr); + LLVMValueRef resume_index_ptr = LLVMBuildStructGEP(g->builder, frame_ptr, coro_resume_index_index, ""); + LLVMValueRef one = LLVMConstInt(g->builtin_types.entry_usize->llvm_type, 1, false); + LLVMBuildStore(g->builder, one, resume_index_ptr); + return ZigLLVMBuildCall(g->builder, fn_val, &frame_ptr, 1, LLVMFastCallConv, ZigLLVM_FnInlineAuto, ""); +} + static void set_debug_location(CodeGen *g, IrInstruction *instruction) { AstNode *source_node = instruction->source_node; Scope *scope = instruction->scope; @@ -5007,6 +5016,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, case IrInstructionIdTestErrSrc: case IrInstructionIdUnionInitNamedField: case IrInstructionIdFrameType: + case IrInstructionIdFrameSizeSrc: zig_unreachable(); case IrInstructionIdDeclVarGen: @@ -5161,6 +5171,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable, return ir_render_suspend_br(g, executable, (IrInstructionSuspendBr *)instruction); case IrInstructionIdCoroResume: return ir_render_coro_resume(g, executable, (IrInstructionCoroResume *)instruction); + case IrInstructionIdFrameSizeGen: + return ir_render_frame_size(g, executable, (IrInstructionFrameSizeGen *)instruction); } zig_unreachable(); } @@ -6856,6 +6868,7 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdFrameHandle, "frame", 0); create_builtin_fn(g, BuiltinFnIdFrameType, "Frame", 1); create_builtin_fn(g, BuiltinFnIdFrameAddress, "frameAddress", 0); + create_builtin_fn(g, BuiltinFnIdFrameSize, "frameSize", 1); } static const char *bool_to_str(bool b) { diff --git a/src/ir.cpp b/src/ir.cpp index 93d559b44..1a62af8ce 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -763,6 +763,14 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionFrameType *) { return IrInstructionIdFrameType; } +static constexpr IrInstructionId ir_instruction_id(IrInstructionFrameSizeSrc *) { + return IrInstructionIdFrameSizeSrc; +} + +static constexpr IrInstructionId ir_instruction_id(IrInstructionFrameSizeGen *) { + return IrInstructionIdFrameSizeGen; +} + static constexpr IrInstructionId ir_instruction_id(IrInstructionAlignOf *) { return IrInstructionIdAlignOf; } @@ -2379,6 +2387,28 @@ static IrInstruction *ir_build_frame_type(IrBuilder *irb, Scope *scope, AstNode return &instruction->base; } +static IrInstruction *ir_build_frame_size_src(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *fn) { + IrInstructionFrameSizeSrc *instruction = ir_build_instruction(irb, scope, source_node); + instruction->fn = fn; + + ir_ref_instruction(fn, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstruction *ir_build_frame_size_gen(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *fn, + IrInstruction *frame_ptr) +{ + IrInstructionFrameSizeGen *instruction = ir_build_instruction(irb, scope, source_node); + instruction->fn = fn; + instruction->frame_ptr = frame_ptr; + + ir_ref_instruction(fn, irb->current_basic_block); + ir_ref_instruction(frame_ptr, irb->current_basic_block); + + return &instruction->base; +} + static IrInstruction *ir_build_overflow_op(IrBuilder *irb, Scope *scope, AstNode *source_node, IrOverflowOp op, IrInstruction *type_value, IrInstruction *op1, IrInstruction *op2, IrInstruction *result_ptr, ZigType *result_ptr_type) @@ -4923,6 +4953,15 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo IrInstruction *frame_type = ir_build_frame_type(irb, scope, node, arg0_value); return ir_lval_wrap(irb, scope, frame_type, lval, result_loc); } + case BuiltinFnIdFrameSize: { + 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_size = ir_build_frame_size_src(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, frame_size, lval, result_loc); + } case BuiltinFnIdAlignOf: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); @@ -21758,6 +21797,28 @@ static IrInstruction *ir_analyze_instruction_frame_type(IrAnalyze *ira, IrInstru return ir_const_type(ira, &instruction->base, ty); } +static IrInstruction *ir_analyze_instruction_frame_size(IrAnalyze *ira, IrInstructionFrameSizeSrc *instruction) { + IrInstruction *fn = instruction->fn->child; + if (type_is_invalid(fn->value.type)) + return ira->codegen->invalid_instruction; + + if (fn->value.type->id != ZigTypeIdFn) { + ir_add_error(ira, fn, + buf_sprintf("expected function, found '%s'", buf_ptr(&fn->value.type->name))); + return ira->codegen->invalid_instruction; + } + + IrInstruction *frame_ptr = ir_resolve_result(ira, &instruction->base, no_result_loc(), + ira->codegen->builtin_types.entry_frame_header, nullptr, true, false); + if (frame_ptr != nullptr && (type_is_invalid(frame_ptr->value.type) || instr_is_unreachable(frame_ptr))) + return frame_ptr; + + IrInstruction *result = ir_build_frame_size_gen(&ira->new_irb, instruction->base.scope, + instruction->base.source_node, fn, frame_ptr); + result->value.type = ira->codegen->builtin_types.entry_usize; + return result; +} + static IrInstruction *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstructionAlignOf *instruction) { Error err; IrInstruction *type_value = instruction->type_value->child; @@ -22348,10 +22409,6 @@ static IrInstruction *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstruct return ira->codegen->invalid_instruction; } - if (fn_type_id.cc == CallingConventionAsync) { - zig_panic("TODO"); - } - return ir_const_type(ira, &instruction->base, get_fn_type(ira->codegen, &fn_type_id)); } @@ -24237,6 +24294,7 @@ static IrInstruction *ir_analyze_instruction_base(IrAnalyze *ira, IrInstruction case IrInstructionIdSliceGen: case IrInstructionIdRefGen: case IrInstructionIdTestErrGen: + case IrInstructionIdFrameSizeGen: zig_unreachable(); case IrInstructionIdReturn: @@ -24387,6 +24445,8 @@ static IrInstruction *ir_analyze_instruction_base(IrAnalyze *ira, IrInstruction return ir_analyze_instruction_frame_handle(ira, (IrInstructionFrameHandle *)instruction); case IrInstructionIdFrameType: return ir_analyze_instruction_frame_type(ira, (IrInstructionFrameType *)instruction); + case IrInstructionIdFrameSizeSrc: + return ir_analyze_instruction_frame_size(ira, (IrInstructionFrameSizeSrc *)instruction); case IrInstructionIdAlignOf: return ir_analyze_instruction_align_of(ira, (IrInstructionAlignOf *)instruction); case IrInstructionIdOverflowOp: @@ -24682,6 +24742,8 @@ bool ir_has_side_effects(IrInstruction *instruction) { case IrInstructionIdFrameAddress: case IrInstructionIdFrameHandle: case IrInstructionIdFrameType: + case IrInstructionIdFrameSizeSrc: + case IrInstructionIdFrameSizeGen: case IrInstructionIdTestErrSrc: case IrInstructionIdTestErrGen: case IrInstructionIdFnProto: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 5b3bba227..7e903ed66 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -916,6 +916,20 @@ static void ir_print_frame_type(IrPrint *irp, IrInstructionFrameType *instructio fprintf(irp->f, ")"); } +static void ir_print_frame_size_src(IrPrint *irp, IrInstructionFrameSizeSrc *instruction) { + fprintf(irp->f, "@frameSize("); + ir_print_other_instruction(irp, instruction->fn); + fprintf(irp->f, ")"); +} + +static void ir_print_frame_size_gen(IrPrint *irp, IrInstructionFrameSizeGen *instruction) { + fprintf(irp->f, "@frameSize("); + ir_print_other_instruction(irp, instruction->fn); + fprintf(irp->f, ","); + ir_print_other_instruction(irp, instruction->frame_ptr); + fprintf(irp->f, ")"); +} + static void ir_print_return_address(IrPrint *irp, IrInstructionReturnAddress *instruction) { fprintf(irp->f, "@returnAddress()"); } @@ -1776,6 +1790,12 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) { case IrInstructionIdFrameType: ir_print_frame_type(irp, (IrInstructionFrameType *)instruction); break; + case IrInstructionIdFrameSizeSrc: + ir_print_frame_size_src(irp, (IrInstructionFrameSizeSrc *)instruction); + break; + case IrInstructionIdFrameSizeGen: + ir_print_frame_size_gen(irp, (IrInstructionFrameSizeGen *)instruction); + break; case IrInstructionIdAlignOf: ir_print_align_of(irp, (IrInstructionAlignOf *)instruction); break; diff --git a/test/stage1/behavior/coroutines.zig b/test/stage1/behavior/coroutines.zig index 33246f761..7af04d37c 100644 --- a/test/stage1/behavior/coroutines.zig +++ b/test/stage1/behavior/coroutines.zig @@ -101,6 +101,32 @@ test "calling an inferred async function" { S.doTheTest(); } +test "@frameSize" { + const S = struct { + fn doTheTest() void { + { + var ptr = @ptrCast(async fn(i32) void, other); + const size = @frameSize(ptr); + expect(size == @sizeOf(@Frame(other))); + } + { + var ptr = @ptrCast(async fn() void, first); + const size = @frameSize(ptr); + expect(size == @sizeOf(@Frame(first))); + } + } + + fn first() void { + other(1); + } + fn other(param: i32) void { + var local: i32 = undefined; + suspend; + } + }; + S.doTheTest(); +} + //test "coroutine suspend, resume" { // seq('a'); // const p = try async testAsyncSeq();