From f1b7c76ae9c015eeadeeff6ffcdf1523afce43f5 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 21 Sep 2022 17:03:38 -0700 Subject: [PATCH] LLVM: implement call_async_alloc It's based on a runtime alloca based on the frame size. This allows us to lower async function calls before lowering the callee, and allows updating callees without updating the callers. Sema: add `std.debug.Trace` integration for `unneeded`. This helps us debug when `LazySrcLoc.unneeded` was incorrectly used. --- src/Module.zig | 20 +- src/Sema.zig | 146 ++++++------- src/codegen/llvm.zig | 397 +++++++++++++++++++++++----------- src/codegen/llvm/bindings.zig | 3 + src/link/Coff.zig | 2 +- src/link/Elf.zig | 2 +- src/link/MachO.zig | 2 +- src/link/Plan9.zig | 2 +- 8 files changed, 366 insertions(+), 208 deletions(-) diff --git a/src/Module.zig b/src/Module.zig index 6b3274bf1b20b76313959619739bdefc7d78e8b5..bd713a816409ace001ba06637ab967ecca002dde 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -2178,7 +2178,10 @@ pub const SrcLoc = struct { pub fn span(src_loc: SrcLoc, gpa: Allocator) !Span { switch (src_loc.lazy) { - .unneeded => unreachable, + .unneeded => |t| { + t.dump(); + unreachable; + }, .entire_file => return Span{ .start = 0, .end = 1, .main = 0 }, .byte_abs => |byte_index| return Span{ .start = byte_index, .end = byte_index + 1, .main = byte_index }, @@ -2905,7 +2908,7 @@ pub const LazySrcLoc = union(enum) { /// unreachable. If you are debugging this tag incorrectly being this value, /// look into using reverse-continue with a memory watchpoint to see where the /// value is being set to this tag. - unneeded, + unneeded: std.debug.Trace, /// Means the source location points to an entire file; not any particular /// location within the file. `file_scope` union field will be active. entire_file, @@ -3197,6 +3200,7 @@ pub const LazySrcLoc = union(enum) { for_capture_from_input: i32, pub const nodeOffset = if (TracedOffset.want_tracing) nodeOffsetDebug else nodeOffsetRelease; + pub const un = if (TracedOffset.want_tracing) unneededDebug else unneededRelease; noinline fn nodeOffsetDebug(node_offset: i32) LazySrcLoc { var result: LazySrcLoc = .{ .node_offset = .{ .x = node_offset } }; @@ -3204,10 +3208,20 @@ pub const LazySrcLoc = union(enum) { return result; } - fn nodeOffsetRelease(node_offset: i32) LazySrcLoc { + noinline fn unneededDebug() LazySrcLoc { + var result: LazySrcLoc = .{ .unneeded = .{} }; + result.unneeded.addAddr(@returnAddress(), "init"); + return result; + } + + inline fn nodeOffsetRelease(node_offset: i32) LazySrcLoc { return .{ .node_offset = .{ .x = node_offset } }; } + inline fn unneededRelease() LazySrcLoc { + return .{ .unneeded = .{} }; + } + /// Upgrade to a `SrcLoc` based on the `Decl` provided. pub fn toSrcLoc(lazy: LazySrcLoc, decl: *Decl, mod: *Module) SrcLoc { return switch (lazy) { diff --git a/src/Sema.zig b/src/Sema.zig index 9fb9e4daf70d6105b384ab35861addee0c168a6f..9dce513b24ed99342836e9ba1b8040891af738ae 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1887,7 +1887,7 @@ pub fn setupErrorReturnTrace(sema: *Sema, block: *Block, last_arg_index: usize) var err_trace_block = block.makeSubBlock(); defer err_trace_block.instructions.deinit(gpa); - const src: LazySrcLoc = .unneeded; + const src = LazySrcLoc.un(); // var addrs: [err_return_trace_addr_count]usize = undefined; const err_return_trace_addr_count = 32; @@ -2913,7 +2913,7 @@ fn createAnonymousDeclTypeNamed( // If not then this is a struct type being returned from a non-generic // function and the name doesn't matter since it will later // result in a compile error. - const arg_val = sema.resolveConstMaybeUndefVal(block, .unneeded, arg, "") catch + const arg_val = sema.resolveConstMaybeUndefVal(block, LazySrcLoc.un(), arg, "") catch return sema.createAnonymousDeclTypeNamed(block, src, typed_value, .anon, anon_prefix, null); if (arg_i != 0) try writer.writeByte(','); @@ -3161,7 +3161,7 @@ fn zirEnumDecl( const tag_val_ref = @as(Zir.Inst.Ref, @enumFromInt(sema.code.extra[extra_index])); extra_index += 1; const tag_inst = try sema.resolveInst(tag_val_ref); - last_tag_val = sema.resolveConstValue(block, .unneeded, tag_inst, "") catch |err| switch (err) { + last_tag_val = sema.resolveConstValue(block, LazySrcLoc.un(), tag_inst, "") catch |err| switch (err) { error.NeededSourceLocation => { const value_src = mod.fieldSrcLoc(new_decl_index, .{ .index = field_i, @@ -5834,7 +5834,7 @@ fn zirExport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void break :index_blk maybe_index orelse return sema.failWithBadMemberAccess(block, container_ty, operand_src, decl_name); } else try sema.lookupIdentifier(block, operand_src, decl_name); - const options = sema.resolveExportOptions(block, .unneeded, extra.options) catch |err| switch (err) { + const options = sema.resolveExportOptions(block, LazySrcLoc.un(), extra.options) catch |err| switch (err) { error.NeededSourceLocation => { _ = try sema.resolveExportOptions(block, options_src, extra.options); unreachable; @@ -5861,7 +5861,7 @@ fn zirExportValue(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const options_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; const operand = try sema.resolveInstConst(block, operand_src, extra.operand, "export target must be comptime-known"); - const options = sema.resolveExportOptions(block, .unneeded, extra.options) catch |err| switch (err) { + const options = sema.resolveExportOptions(block, LazySrcLoc.un(), extra.options) catch |err| switch (err) { error.NeededSourceLocation => { _ = try sema.resolveExportOptions(block, options_src, extra.options); unreachable; @@ -6995,7 +6995,7 @@ fn analyzeCall( sema.analyzeInlineCallArg( block, &child_block, - .unneeded, + LazySrcLoc.un(), inst, &new_fn_info, &arg_i, @@ -7141,7 +7141,7 @@ fn analyzeCall( } if (should_memoize and is_comptime_call) { - const result_val = try sema.resolveConstMaybeUndefVal(block, .unneeded, result, ""); + const result_val = try sema.resolveConstMaybeUndefVal(block, LazySrcLoc.un(), result, ""); // TODO: check whether any external comptime memory was mutated by the // comptime function call. If so, then do not memoize the call here. @@ -7171,7 +7171,7 @@ fn analyzeCall( const param_ty = mod.typeToFunc(func_ty).?.param_types[i].toType(); args[i] = sema.analyzeCallArg( block, - .unneeded, + LazySrcLoc.un(), param_ty, uncasted_arg, opts, @@ -7190,7 +7190,7 @@ fn analyzeCall( else => |e| return e, }; } else { - args[i] = sema.coerceVarArgParam(block, uncasted_arg, .unneeded) catch |err| switch (err) { + args[i] = sema.coerceVarArgParam(block, uncasted_arg, LazySrcLoc.un()) catch |err| switch (err) { error.NeededSourceLocation => { const decl = mod.declPtr(block.src_decl); _ = try sema.coerceVarArgParam( @@ -7608,7 +7608,7 @@ fn instantiateGenericCall( } if (is_comptime) { - const casted_arg = sema.analyzeGenericCallArgVal(block, .unneeded, arg_ty.toType(), uncasted_arg, "") catch |err| switch (err) { + const casted_arg = sema.analyzeGenericCallArgVal(block, LazySrcLoc.un(), arg_ty.toType(), uncasted_arg, "") catch |err| switch (err) { error.NeededSourceLocation => { const decl = mod.declPtr(block.src_decl); const arg_src = mod.argSrc(call_src.node_offset.x, decl, arg_i, bound_arg_src); @@ -7741,7 +7741,7 @@ fn instantiateGenericCall( } sema.analyzeGenericCallArg( block, - .unneeded, + LazySrcLoc.un(), uncasted_args[total_i], comptime_args[total_i], runtime_args, @@ -7893,7 +7893,7 @@ fn resolveGenericInstantiationType( } else if (is_anytype) { const arg_ty = sema.typeOf(arg); if (try sema.typeRequiresComptime(arg_ty)) { - const arg_val = sema.resolveConstValue(block, .unneeded, arg, "") catch |err| switch (err) { + const arg_val = sema.resolveConstValue(block, LazySrcLoc.un(), arg, "") catch |err| switch (err) { error.NeededSourceLocation => { const decl = mod.declPtr(block.src_decl); const arg_src = mod.argSrc(call_src.node_offset.x, decl, arg_i, bound_arg_src); @@ -7927,7 +7927,7 @@ fn resolveGenericInstantiationType( child_block.error_return_trace_index = error_return_trace_index; const new_func_inst = try child_sema.resolveBody(&child_block, fn_info.param_body, fn_info.param_body_inst); - const new_func_val = child_sema.resolveConstValue(&child_block, .unneeded, new_func_inst, undefined) catch unreachable; + const new_func_val = child_sema.resolveConstValue(&child_block, LazySrcLoc.un(), new_func_inst, undefined) catch unreachable; const new_func = new_func_val.getFunctionIndex(mod).unwrap().?; assert(new_func == new_module_func); @@ -8091,7 +8091,7 @@ fn zirOptionalType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro fn zirElemTypeIndex(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const mod = sema.mod; const bin = sema.code.instructions.items(.data)[inst].bin; - const indexable_ty = try sema.resolveType(block, .unneeded, bin.lhs); + const indexable_ty = try sema.resolveType(block, LazySrcLoc.un(), bin.lhs); assert(indexable_ty.isIndexable(mod)); // validated by a previous instruction if (indexable_ty.zigTypeTag(mod) == .Struct) { const elem_type = indexable_ty.structFieldType(@intFromEnum(bin.rhs), mod); @@ -8105,7 +8105,7 @@ fn zirElemTypeIndex(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr fn zirElemType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { const mod = sema.mod; const un_node = sema.code.instructions.items(.data)[inst].un_node; - const ptr_ty = try sema.resolveType(block, .unneeded, un_node.operand); + const ptr_ty = try sema.resolveType(block, LazySrcLoc.un(), un_node.operand); assert(ptr_ty.zigTypeTag(mod) == .Pointer); // validated by a previous instruction return sema.addType(ptr_ty.childType(mod)); } @@ -9101,7 +9101,7 @@ fn funcCommon( dest_param_ty.* = param.ty.toIntern(); sema.analyzeParameter( block, - .unneeded, + LazySrcLoc.un(), param, &comptime_bits, i, @@ -9500,7 +9500,7 @@ fn zirParam( if (is_comptime and sema.preallocated_new_func != .none) { // We have a comptime value for this parameter so it should be elided from the // function type of the function instruction in this block. - const coerced_arg = sema.coerce(block, param_ty, arg, .unneeded) catch |err| switch (err) { + const coerced_arg = sema.coerce(block, param_ty, arg, LazySrcLoc.un()) catch |err| switch (err) { error.NeededSourceLocation => { // We are instantiating a generic function and a comptime arg // cannot be coerced to the param type, but since we don't @@ -10169,7 +10169,7 @@ fn zirSliceStart(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! const start_src: LazySrcLoc = .{ .node_offset_slice_start = inst_data.src_node }; const end_src: LazySrcLoc = .{ .node_offset_slice_end = inst_data.src_node }; - return sema.analyzeSlice(block, src, array_ptr, start, .none, .none, .unneeded, ptr_src, start_src, end_src, false); + return sema.analyzeSlice(block, src, array_ptr, start, .none, .none, LazySrcLoc.un(), ptr_src, start_src, end_src, false); } fn zirSliceEnd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -10186,7 +10186,7 @@ fn zirSliceEnd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const start_src: LazySrcLoc = .{ .node_offset_slice_start = inst_data.src_node }; const end_src: LazySrcLoc = .{ .node_offset_slice_end = inst_data.src_node }; - return sema.analyzeSlice(block, src, array_ptr, start, end, .none, .unneeded, ptr_src, start_src, end_src, false); + return sema.analyzeSlice(block, src, array_ptr, start, end, .none, LazySrcLoc.un(), ptr_src, start_src, end_src, false); } fn zirSliceSentinel(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -10223,7 +10223,7 @@ fn zirSliceLength(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const start_src: LazySrcLoc = .{ .node_offset_slice_start = extra.start_src_node_offset }; const end_src: LazySrcLoc = .{ .node_offset_slice_end = inst_data.src_node }; const sentinel_src: LazySrcLoc = if (sentinel == .none) - .unneeded + LazySrcLoc.un() else .{ .node_offset_slice_sentinel = inst_data.src_node }; @@ -10418,7 +10418,7 @@ const SwitchProngAnalysis = struct { const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = switch_node_offset }; if (inline_case_capture != .none) { - const item_val = sema.resolveConstValue(block, .unneeded, inline_case_capture, "") catch unreachable; + const item_val = sema.resolveConstValue(block, LazySrcLoc.un(), inline_case_capture, "") catch unreachable; if (operand_ty.zigTypeTag(mod) == .Union) { const field_index = @as(u32, @intCast(operand_ty.unionTagFieldIndex(item_val, mod).?)); const union_obj = mod.typeToUnion(operand_ty).?; @@ -10477,15 +10477,15 @@ const SwitchProngAnalysis = struct { switch (operand_ty.zigTypeTag(mod)) { .Union => { const union_obj = mod.typeToUnion(operand_ty).?; - const first_item_val = sema.resolveConstValue(block, .unneeded, case_vals[0], "") catch unreachable; + const first_item_val = sema.resolveConstValue(block, LazySrcLoc.un(), case_vals[0], "") catch unreachable; const first_field_index = @as(u32, @intCast(operand_ty.unionTagFieldIndex(first_item_val, mod).?)); const first_field = union_obj.fields.values()[first_field_index]; const field_tys = try sema.arena.alloc(Type, case_vals.len); for (case_vals, field_tys) |item, *field_ty| { - const item_val = sema.resolveConstValue(block, .unneeded, item, "") catch unreachable; - const field_idx = @as(u32, @intCast(operand_ty.unionTagFieldIndex(item_val, sema.mod).?)); + const item_val = sema.resolveConstValue(block, LazySrcLoc.un(), item, "") catch unreachable; + const field_idx: u32 = @intCast(operand_ty.unionTagFieldIndex(item_val, sema.mod).?); field_ty.* = union_obj.fields.values()[field_idx].ty; } @@ -10503,9 +10503,9 @@ const SwitchProngAnalysis = struct { } const case_srcs = try sema.arena.alloc(?LazySrcLoc, case_vals.len); - @memset(case_srcs, .unneeded); + @memset(case_srcs, LazySrcLoc.un()); - break :capture_ty sema.resolvePeerTypes(block, .unneeded, dummy_captures, .{ .override = case_srcs }) catch |err| switch (err) { + break :capture_ty sema.resolvePeerTypes(block, LazySrcLoc.un(), dummy_captures, .{ .override = case_srcs }) catch |err| switch (err) { error.NeededSourceLocation => { // This must be a multi-prong so this must be a `multi_capture` src const multi_idx = raw_capture_src.multi_capture; @@ -10555,7 +10555,7 @@ const SwitchProngAnalysis = struct { .address_space = operand_ptr_info.flags.address_space, }, }); - if (.ok != try sema.coerceInMemoryAllowed(block, capture_ptr_ty, field_ptr_ty, false, sema.mod.getTarget(), .unneeded, .unneeded)) { + if (.ok != try sema.coerceInMemoryAllowed(block, capture_ptr_ty, field_ptr_ty, false, sema.mod.getTarget(), LazySrcLoc.un(), LazySrcLoc.un())) { const multi_idx = raw_capture_src.multi_capture; const src_decl_ptr = sema.mod.declPtr(block.src_decl); const capture_src = raw_capture_src.resolve(mod, src_decl_ptr, switch_node_offset, .none); @@ -10611,7 +10611,7 @@ const SwitchProngAnalysis = struct { // If we can, try to avoid that using in-memory coercions. const first_non_imc = in_mem: { for (field_tys, 0..) |field_ty, i| { - if (.ok != try sema.coerceInMemoryAllowed(block, capture_ty, field_ty, false, sema.mod.getTarget(), .unneeded, .unneeded)) { + if (.ok != try sema.coerceInMemoryAllowed(block, capture_ty, field_ty, false, sema.mod.getTarget(), LazySrcLoc.un(), LazySrcLoc.un())) { break :in_mem i; } } @@ -10633,7 +10633,7 @@ const SwitchProngAnalysis = struct { { const next = first_non_imc + 1; for (field_tys[next..], next..) |field_ty, i| { - if (.ok != try sema.coerceInMemoryAllowed(block, capture_ty, field_ty, false, sema.mod.getTarget(), .unneeded, .unneeded)) { + if (.ok != try sema.coerceInMemoryAllowed(block, capture_ty, field_ty, false, sema.mod.getTarget(), LazySrcLoc.un(), LazySrcLoc.un())) { in_mem_coercible.unset(i); } } @@ -10662,8 +10662,8 @@ const SwitchProngAnalysis = struct { var coerce_block = block.makeSubBlock(); defer coerce_block.instructions.deinit(sema.gpa); - const uncoerced = try coerce_block.addStructFieldVal(spa.operand, @as(u32, @intCast(idx)), field_tys[idx]); - const coerced = sema.coerce(&coerce_block, capture_ty, uncoerced, .unneeded) catch |err| switch (err) { + const uncoerced = try coerce_block.addStructFieldVal(spa.operand, @intCast(idx), field_tys[idx]); + const coerced = sema.coerce(&coerce_block, capture_ty, uncoerced, LazySrcLoc.un()) catch |err| switch (err) { error.NeededSourceLocation => { const multi_idx = raw_capture_src.multi_capture; const src_decl_ptr = sema.mod.declPtr(block.src_decl); @@ -10735,7 +10735,7 @@ const SwitchProngAnalysis = struct { } if (case_vals.len == 1) { - const item_val = sema.resolveConstValue(block, .unneeded, case_vals[0], "") catch unreachable; + const item_val = sema.resolveConstValue(block, LazySrcLoc.un(), case_vals[0], "") catch unreachable; const item_ty = try mod.singleErrorSetType(item_val.getErrorName(mod).unwrap().?); return sema.bitCast(block, item_ty, spa.operand, operand_src, null); } @@ -10743,7 +10743,7 @@ const SwitchProngAnalysis = struct { var names: Module.Fn.InferredErrorSet.NameMap = .{}; try names.ensureUnusedCapacity(sema.arena, case_vals.len); for (case_vals) |err| { - const err_val = sema.resolveConstValue(block, .unneeded, err, "") catch unreachable; + const err_val = sema.resolveConstValue(block, LazySrcLoc.un(), err, "") catch unreachable; names.putAssumeCapacityNoClobber(err_val.getErrorName(mod).unwrap().?, {}); } const error_ty = try mod.errorSetFromUnsortedNames(names.keys()); @@ -11507,7 +11507,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r extra_index += info.body_len; const item = case_vals.items[scalar_i]; - const item_val = sema.resolveConstValue(&child_block, .unneeded, item, "") catch unreachable; + const item_val = sema.resolveConstValue(&child_block, LazySrcLoc.un(), item, "") catch unreachable; if (operand_val.eql(item_val, operand_ty, sema.mod)) { if (err_set) try sema.maybeErrorUnwrapComptime(&child_block, body, operand); return spa.resolveProngComptime( @@ -11541,7 +11541,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r for (items) |item| { // Validation above ensured these will succeed. - const item_val = sema.resolveConstValue(&child_block, .unneeded, item, "") catch unreachable; + const item_val = sema.resolveConstValue(&child_block, LazySrcLoc.un(), item, "") catch unreachable; if (operand_val.eql(item_val, operand_ty, sema.mod)) { if (err_set) try sema.maybeErrorUnwrapComptime(&child_block, body, operand); return spa.resolveProngComptime( @@ -11565,8 +11565,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r case_val_idx += 2; // Validation above ensured these will succeed. - const first_val = sema.resolveConstValue(&child_block, .unneeded, range_items[0], "") catch unreachable; - const last_val = sema.resolveConstValue(&child_block, .unneeded, range_items[1], "") catch unreachable; + const first_val = sema.resolveConstValue(&child_block, LazySrcLoc.un(), range_items[0], "") catch unreachable; + const last_val = sema.resolveConstValue(&child_block, LazySrcLoc.un(), range_items[1], "") catch unreachable; if ((try sema.compareAll(resolved_operand_val, .gte, first_val, operand_ty)) and (try sema.compareAll(resolved_operand_val, .lte, last_val, operand_ty))) { @@ -11676,7 +11676,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r // `item` is already guaranteed to be constant known. const analyze_body = if (union_originally) blk: { - const item_val = sema.resolveConstLazyValue(block, .unneeded, item, "") catch unreachable; + const item_val = sema.resolveConstLazyValue(block, LazySrcLoc.un(), item, "") catch unreachable; const field_ty = maybe_union_ty.unionFieldType(item_val, mod); break :blk field_ty.zigTypeTag(mod) != .NoReturn; } else true; @@ -11746,8 +11746,8 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r const item_first_ref = range_items[0]; const item_last_ref = range_items[1]; - var item = sema.resolveConstValue(block, .unneeded, item_first_ref, undefined) catch unreachable; - const item_last = sema.resolveConstValue(block, .unneeded, item_last_ref, undefined) catch unreachable; + var item = sema.resolveConstValue(block, LazySrcLoc.un(), item_first_ref, undefined) catch unreachable; + const item_last = sema.resolveConstValue(block, LazySrcLoc.un(), item_last_ref, undefined) catch unreachable; while (item.compareScalar(.lte, item_last, operand_ty, mod)) : ({ // Previous validation has resolved any possible lazy values. @@ -11763,7 +11763,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r case_block.instructions.shrinkRetainingCapacity(0); case_block.wip_capture_scope = child_block.wip_capture_scope; - if (emit_bb) sema.emitBackwardBranch(block, .unneeded) catch |err| switch (err) { + if (emit_bb) sema.emitBackwardBranch(block, LazySrcLoc.un()) catch |err| switch (err) { error.NeededSourceLocation => { const case_src = Module.SwitchProngSrc{ .range = .{ .prong = multi_i, .item = range_i } }; const decl = mod.declPtr(case_block.src_decl); @@ -11802,12 +11802,12 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r case_block.wip_capture_scope = child_block.wip_capture_scope; const analyze_body = if (union_originally) blk: { - const item_val = sema.resolveConstValue(block, .unneeded, item, undefined) catch unreachable; + const item_val = sema.resolveConstValue(block, LazySrcLoc.un(), item, undefined) catch unreachable; const field_ty = maybe_union_ty.unionFieldType(item_val, mod); break :blk field_ty.zigTypeTag(mod) != .NoReturn; } else true; - if (emit_bb) sema.emitBackwardBranch(block, .unneeded) catch |err| switch (err) { + if (emit_bb) sema.emitBackwardBranch(block, LazySrcLoc.un()) catch |err| switch (err) { error.NeededSourceLocation => { const case_src = Module.SwitchProngSrc{ .multi = .{ .prong = multi_i, .item = @as(u32, @intCast(item_i)) } }; const decl = mod.declPtr(case_block.src_decl); @@ -11854,7 +11854,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r const analyze_body = if (union_originally) for (items) |item| { - const item_val = sema.resolveConstValue(block, .unneeded, item, "") catch unreachable; + const item_val = sema.resolveConstValue(block, LazySrcLoc.un(), item, "") catch unreachable; const field_ty = maybe_union_ty.unionFieldType(item_val, mod); if (field_ty.zigTypeTag(mod) != .NoReturn) break true; } else false @@ -12345,7 +12345,7 @@ fn resolveSwitchItemVal( // Only if we know for sure we need to report a compile error do we resolve the // full source locations. - const item = sema.coerce(block, coerce_ty, uncoerced_item, .unneeded) catch |err| switch (err) { + const item = sema.coerce(block, coerce_ty, uncoerced_item, LazySrcLoc.un()) catch |err| switch (err) { error.NeededSourceLocation => { const src = switch_prong_src.resolve(mod, mod.declPtr(block.src_decl), switch_node_offset, range_expand); _ = try sema.coerce(block, coerce_ty, uncoerced_item, src); @@ -12354,7 +12354,7 @@ fn resolveSwitchItemVal( else => |e| return e, }; - const maybe_lazy = sema.resolveConstValue(block, .unneeded, item, "") catch |err| switch (err) { + const maybe_lazy = sema.resolveConstValue(block, LazySrcLoc.un(), item, "") catch |err| switch (err) { error.NeededSourceLocation => { const src = switch_prong_src.resolve(mod, mod.declPtr(block.src_decl), switch_node_offset, range_expand); _ = try sema.resolveConstValue(block, src, item, "switch prong values must be comptime-known"); @@ -13479,8 +13479,8 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const elem_default_val = if (lhs_is_tuple) lhs_ty.structFieldDefaultValue(lhs_elem_i, mod) else Value.@"unreachable"; const elem_val = if (elem_default_val.toIntern() == .unreachable_value) try lhs_sub_val.elemValue(mod, lhs_elem_i) else elem_default_val; const elem_val_inst = try sema.addConstant(elem_val); - const coerced_elem_val_inst = try sema.coerce(block, resolved_elem_ty, elem_val_inst, .unneeded); - const coerced_elem_val = try sema.resolveConstMaybeUndefVal(block, .unneeded, coerced_elem_val_inst, ""); + const coerced_elem_val_inst = try sema.coerce(block, resolved_elem_ty, elem_val_inst, LazySrcLoc.un()); + const coerced_elem_val = try sema.resolveConstMaybeUndefVal(block, LazySrcLoc.un(), coerced_elem_val_inst, ""); element_vals[elem_i] = try coerced_elem_val.intern(resolved_elem_ty, mod); } while (elem_i < result_len) : (elem_i += 1) { @@ -13488,8 +13488,8 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const elem_default_val = if (rhs_is_tuple) rhs_ty.structFieldDefaultValue(rhs_elem_i, mod) else Value.@"unreachable"; const elem_val = if (elem_default_val.toIntern() == .unreachable_value) try rhs_sub_val.elemValue(mod, rhs_elem_i) else elem_default_val; const elem_val_inst = try sema.addConstant(elem_val); - const coerced_elem_val_inst = try sema.coerce(block, resolved_elem_ty, elem_val_inst, .unneeded); - const coerced_elem_val = try sema.resolveConstMaybeUndefVal(block, .unneeded, coerced_elem_val_inst, ""); + const coerced_elem_val_inst = try sema.coerce(block, resolved_elem_ty, elem_val_inst, LazySrcLoc.un()); + const coerced_elem_val = try sema.resolveConstMaybeUndefVal(block, LazySrcLoc.un(), coerced_elem_val_inst, ""); element_vals[elem_i] = try coerced_elem_val.intern(resolved_elem_ty, mod); } return sema.addConstantMaybeRef(block, result_ty, (try mod.intern(.{ .aggregate = .{ @@ -18361,7 +18361,7 @@ fn zirRetImplicit( return sema.failWithOwnedErrorMsg(msg); } - return sema.analyzeRet(block, operand, .unneeded); + return sema.analyzeRet(block, operand, LazySrcLoc.un()); } fn zirRetNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index { @@ -19069,7 +19069,7 @@ fn finishStructInit( return sema.makePtrConst(block, alloc); } - sema.requireRuntimeBlock(block, .unneeded, null) catch |err| switch (err) { + sema.requireRuntimeBlock(block, LazySrcLoc.un(), null) catch |err| switch (err) { error.NeededSourceLocation => { const decl = mod.declPtr(block.src_decl); const field_src = mod.initSrc(dest_src.node_offset.x, decl, runtime_index); @@ -19163,7 +19163,7 @@ fn zirStructInitAnon( return sema.addConstantMaybeRef(block, tuple_ty.toType(), tuple_val.toValue(), is_ref); }; - sema.requireRuntimeBlock(block, .unneeded, null) catch |err| switch (err) { + sema.requireRuntimeBlock(block, LazySrcLoc.un(), null) catch |err| switch (err) { error.NeededSourceLocation => { const decl = mod.declPtr(block.src_decl); const field_src = mod.initSrc(src.node_offset.x, decl, runtime_index); @@ -19237,7 +19237,7 @@ fn zirArrayInit( array_ty.structFieldType(i, mod) else array_ty.elemType2(mod); - resolved_args[i] = sema.coerce(block, elem_ty, resolved_arg, .unneeded) catch |err| switch (err) { + resolved_args[i] = sema.coerce(block, elem_ty, resolved_arg, LazySrcLoc.un()) catch |err| switch (err) { error.NeededSourceLocation => { const decl = mod.declPtr(block.src_decl); const elem_src = mod.initSrc(src.node_offset.x, decl, i); @@ -19273,7 +19273,7 @@ fn zirArrayInit( } })).toValue(), is_ref); }; - sema.requireRuntimeBlock(block, .unneeded, null) catch |err| switch (err) { + sema.requireRuntimeBlock(block, LazySrcLoc.un(), null) catch |err| switch (err) { error.NeededSourceLocation => { const decl = mod.declPtr(block.src_decl); const elem_src = mod.initSrc(src.node_offset.x, decl, runtime_index); @@ -19667,7 +19667,7 @@ fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air try sema.resolveTypeLayout(operand_ty); const enum_ty = switch (operand_ty.zigTypeTag(mod)) { .EnumLiteral => { - const val = try sema.resolveConstValue(block, .unneeded, operand, ""); + const val = try sema.resolveConstValue(block, LazySrcLoc.un(), operand, ""); const tag_name = ip.indexToKey(val.toIntern()).enum_literal; return sema.addStrLit(block, ip.stringToSlice(tag_name)); }, @@ -22213,7 +22213,7 @@ fn checkVectorizableBinaryOperands( } fn maybeOptionsSrc(sema: *Sema, block: *Block, base_src: LazySrcLoc, wanted: []const u8) LazySrcLoc { - if (base_src == .unneeded) return .unneeded; + if (base_src == .unneeded) return LazySrcLoc.un(); const mod = sema.mod; return mod.optionsSrc(mod.declPtr(block.src_decl), base_src, wanted); } @@ -23655,7 +23655,7 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void } else if (dest_len == .none and len_val == null) { // Change the dest to a slice, since its type must have the length. const dest_ptr_ptr = try sema.analyzeRef(block, dest_src, new_dest_ptr); - new_dest_ptr = try sema.analyzeSlice(block, dest_src, dest_ptr_ptr, .zero, src_len, .none, .unneeded, dest_src, dest_src, dest_src, false); + new_dest_ptr = try sema.analyzeSlice(block, dest_src, dest_ptr_ptr, .zero, src_len, .none, LazySrcLoc.un(), dest_src, dest_src, dest_src, false); const new_src_ptr_ty = sema.typeOf(new_src_ptr); if (new_src_ptr_ty.isSlice(mod)) { new_src_ptr = try sema.analyzeSlicePtr(block, src_src, new_src_ptr, new_src_ptr_ty); @@ -24221,7 +24221,7 @@ fn zirPrefetch( const ptr = try sema.resolveInst(extra.lhs); try sema.checkPtrOperand(block, ptr_src, sema.typeOf(ptr)); - const options = sema.resolvePrefetchOptions(block, .unneeded, extra.rhs) catch |err| switch (err) { + const options = sema.resolvePrefetchOptions(block, LazySrcLoc.un(), extra.rhs) catch |err| switch (err) { error.NeededSourceLocation => { _ = try sema.resolvePrefetchOptions(block, opts_src, extra.rhs); unreachable; @@ -24330,7 +24330,7 @@ fn zirBuiltinExtern( return sema.failWithOwnedErrorMsg(msg); } - const options = sema.resolveExternOptions(block, .unneeded, extra.rhs) catch |err| switch (err) { + const options = sema.resolveExternOptions(block, LazySrcLoc.un(), extra.rhs) catch |err| switch (err) { error.NeededSourceLocation => { _ = try sema.resolveExternOptions(block, options_src, extra.rhs); unreachable; @@ -25036,7 +25036,7 @@ fn panicWithMsg(sema: *Sema, block: *Block, msg_inst: Air.Inst.Ref) !void { try sema.prepareSimplePanic(block); const panic_func = mod.funcPtrUnwrap(mod.panic_func_index).?; - const panic_fn = try sema.analyzeDeclVal(block, .unneeded, panic_func.owner_decl); + const panic_fn = try sema.analyzeDeclVal(block, LazySrcLoc.un(), panic_func.owner_decl); const null_stack_trace = try sema.addConstant(mod.null_stack_trace.toValue()); const opt_usize_ty = try mod.optionalType(.usize_type); @@ -25530,7 +25530,7 @@ fn fieldPtr( } }, .Type => { - _ = try sema.resolveConstValue(block, .unneeded, object_ptr, ""); + _ = try sema.resolveConstValue(block, LazySrcLoc.un(), object_ptr, ""); const result = try sema.analyzeLoad(block, src, object_ptr, object_ptr_src); const inner = if (is_pointer_to) try sema.analyzeLoad(block, src, result, object_ptr_src) @@ -27026,7 +27026,7 @@ fn coerceExtra( // Function body to function pointer. if (inst_ty.zigTypeTag(mod) == .Fn) { - const fn_val = try sema.resolveConstValue(block, .unneeded, inst, ""); + const fn_val = try sema.resolveConstValue(block, LazySrcLoc.un(), inst, ""); const fn_decl = fn_val.pointerDecl(mod).?; const inst_as_ptr = try sema.analyzeDeclRef(fn_decl); return sema.coerce(block, dest_ty, inst_as_ptr, inst_src); @@ -27366,7 +27366,7 @@ fn coerceExtra( }, .Float, .ComptimeFloat => switch (inst_ty.zigTypeTag(mod)) { .ComptimeFloat => { - const val = try sema.resolveConstValue(block, .unneeded, inst, ""); + const val = try sema.resolveConstValue(block, LazySrcLoc.un(), inst, ""); const result_val = try val.floatCast(dest_ty, mod); return try sema.addConstant(result_val); }, @@ -27430,7 +27430,7 @@ fn coerceExtra( .Enum => switch (inst_ty.zigTypeTag(mod)) { .EnumLiteral => { // enum literal to enum - const val = try sema.resolveConstValue(block, .unneeded, inst, ""); + const val = try sema.resolveConstValue(block, LazySrcLoc.un(), inst, ""); const string = mod.intern_pool.indexToKey(val.toIntern()).enum_literal; const field_index = dest_ty.enumFieldIndex(string, mod) orelse { const msg = msg: { @@ -28544,7 +28544,7 @@ fn coerceVarArgParam( .{}, ), .Fn => blk: { - const fn_val = try sema.resolveConstValue(block, .unneeded, inst, ""); + const fn_val = try sema.resolveConstValue(block, LazySrcLoc.un(), inst, ""); const fn_decl = fn_val.pointerDecl(mod).?; break :blk try sema.analyzeDeclRef(fn_decl); }, @@ -34522,7 +34522,7 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void for (fields, 0..) |zir_field, field_i| { const field_ty: Type = ty: { if (zir_field.type_ref != .none) { - break :ty sema.resolveType(&block_scope, .unneeded, zir_field.type_ref) catch |err| switch (err) { + break :ty sema.resolveType(&block_scope, LazySrcLoc.un(), zir_field.type_ref) catch |err| switch (err) { error.NeededSourceLocation => { const ty_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{ .index = field_i, @@ -34538,7 +34538,7 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void const body = zir.extra[extra_index..][0..zir_field.type_body_len]; extra_index += body.len; const ty_ref = try sema.resolveBody(&block_scope, body, struct_obj.zir_index); - break :ty sema.analyzeAsType(&block_scope, .unneeded, ty_ref) catch |err| switch (err) { + break :ty sema.analyzeAsType(&block_scope, LazySrcLoc.un(), ty_ref) catch |err| switch (err) { error.NeededSourceLocation => { const ty_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{ .index = field_i, @@ -34621,7 +34621,7 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void const body = zir.extra[extra_index..][0..zir_field.align_body_len]; extra_index += body.len; const align_ref = try sema.resolveBody(&block_scope, body, struct_obj.zir_index); - field.abi_align = sema.analyzeAsAlign(&block_scope, .unneeded, align_ref) catch |err| switch (err) { + field.abi_align = sema.analyzeAsAlign(&block_scope, LazySrcLoc.un(), align_ref) catch |err| switch (err) { error.NeededSourceLocation => { const align_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{ .index = field_i, @@ -34649,7 +34649,7 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void extra_index += body.len; const init = try sema.resolveBody(&block_scope, body, struct_obj.zir_index); const field = &struct_obj.fields.values()[field_i]; - const coerced = sema.coerce(&block_scope, field.ty, init, .unneeded) catch |err| switch (err) { + const coerced = sema.coerce(&block_scope, field.ty, init, LazySrcLoc.un()) catch |err| switch (err) { error.NeededSourceLocation => { const init_src = mod.fieldSrcLoc(struct_obj.owner_decl, .{ .index = field_i, @@ -34881,7 +34881,7 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { if (enum_field_vals.capacity() > 0) { const enum_tag_val = if (tag_ref != .none) blk: { - const val = sema.semaUnionFieldVal(&block_scope, .unneeded, int_tag_ty, tag_ref) catch |err| switch (err) { + const val = sema.semaUnionFieldVal(&block_scope, LazySrcLoc.un(), int_tag_ty, tag_ref) catch |err| switch (err) { error.NeededSourceLocation => { const val_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ .index = field_i, @@ -34929,7 +34929,7 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { else if (field_type_ref == .none) Type.noreturn else - sema.resolveType(&block_scope, .unneeded, field_type_ref) catch |err| switch (err) { + sema.resolveType(&block_scope, LazySrcLoc.un(), field_type_ref) catch |err| switch (err) { error.NeededSourceLocation => { const ty_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ .index = field_i, @@ -35038,7 +35038,7 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void { }; if (align_ref != .none) { - gop.value_ptr.abi_align = sema.resolveAlign(&block_scope, .unneeded, align_ref) catch |err| switch (err) { + gop.value_ptr.abi_align = sema.resolveAlign(&block_scope, LazySrcLoc.un(), align_ref) catch |err| switch (err) { error.NeededSourceLocation => { const align_src = mod.fieldSrcLoc(union_obj.owner_decl, .{ .index = field_i, diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 32e1d383ffb199476d9170e4acd6e84cc2367646..c967e0572f997bc1d94178f65fda8643d6e7c99a 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -2348,7 +2348,24 @@ pub const Object = struct { .Null => unreachable, .EnumLiteral => unreachable, - .Frame => @panic("TODO implement lowerDebugType for Frame types"), + .Frame => { + // TODO make this more useful than just a pointer to u8 + // TODO this also does not account for async functions with + // any spilled locals that are aligned to more than 16 bytes + const elem_di_ty = try o.lowerDebugType(Type.u8, .full); + const name = try o.allocTypeName(ty); + defer gpa.free(name); + const ptr_di_ty = dib.createPointerType( + elem_di_ty, + target.ptrBitWidth(), + target.ptrBitWidth() * 2, // alignment + name, + ); + // The recursive call to `lowerDebugType` means we can't use `gop` anymore. + try o.di_type_map.put(gpa, ty.toIntern(), AnnotatedDITypePtr.initFull(ptr_di_ty)); + return ptr_di_ty; + }, + .AnyFrame => @panic("TODO implement lowerDebugType for AnyFrame types"), } } @@ -3045,6 +3062,17 @@ pub const Object = struct { } } + fn lowerAsyncFrameHeader(o: *Object, ret_ty: Type) !*llvm.Type { + const opaque_ptr_ty = o.context.pointerType(0); + const l = asyncFrameLayout(); + var fields: [4]*llvm.Type = undefined; + fields[l.fn_ptr] = opaque_ptr_ty; + fields[l.resume_index] = try o.lowerType(Type.usize); + fields[l.awaiter] = opaque_ptr_ty; + fields[l.ret_val] = try o.lowerType(ret_ty); + return o.context.structType(&fields, fields.len, .False); + } + fn lowerAsyncFrameType( o: *Object, func: *Module.Fn, @@ -4668,6 +4696,200 @@ pub const FuncGen = struct { try llvm_args.append(self.err_ret_trace.?); } + try addCallArgs(self, args, &llvm_args, fn_info); + + const call = self.builder.buildCall( + try o.lowerType(zig_fn_ty), + llvm_fn, + llvm_args.items.ptr, + @intCast(llvm_args.items.len), + toLlvmCallConv(fn_info.cc, target), + attr, + "", + ); + + if (callee_ty.zigTypeTag(mod) == .Pointer) { + // Add argument attributes for function pointer calls. + var it = iterateParamTypes(o, fn_info); + it.llvm_index += @intFromBool(sret); + it.llvm_index += @intFromBool(err_return_tracing); + while (it.next()) |lowering| switch (lowering) { + .byval => { + const param_index = it.zig_index - 1; + const param_ty = fn_info.param_types[param_index].toType(); + if (!isByRef(param_ty, mod)) { + o.addByValParamAttrs(call, param_ty, param_index, fn_info, it.llvm_index - 1); + } + }, + .byref => { + const param_index = it.zig_index - 1; + const param_ty = fn_info.param_types[param_index].toType(); + const param_llvm_ty = try o.lowerType(param_ty); + const alignment = param_ty.abiAlignment(mod); + o.addByRefParamAttrs(call, it.llvm_index - 1, alignment, it.byval_attr, param_llvm_ty); + }, + .byref_mut => { + o.addArgAttr(call, it.llvm_index - 1, "noundef"); + }, + // No attributes needed for these. + .no_bits, + .abi_sized_int, + .multiple_llvm_types, + .as_u16, + .float_array, + .i32_array, + .i64_array, + => continue, + + .slice => { + assert(!it.byval_attr); + const param_ty = fn_info.param_types[it.zig_index - 1].toType(); + const ptr_info = param_ty.ptrInfo(mod); + const llvm_arg_i = it.llvm_index - 2; + + if (math.cast(u5, it.zig_index - 1)) |i| { + if (@as(u1, @truncate(fn_info.noalias_bits >> i)) != 0) { + o.addArgAttr(call, llvm_arg_i, "noalias"); + } + } + if (param_ty.zigTypeTag(mod) != .Optional) { + o.addArgAttr(call, llvm_arg_i, "nonnull"); + } + if (ptr_info.flags.is_const) { + o.addArgAttr(call, llvm_arg_i, "readonly"); + } + const elem_align = ptr_info.flags.alignment.toByteUnitsOptional() orelse + @max(ptr_info.child.toType().abiAlignment(mod), 1); + o.addArgAttrInt(call, llvm_arg_i, "align", elem_align); + }, + }; + } + + if (fn_info.return_type == .noreturn_type and attr != .AlwaysTail) { + return null; + } + + if (self.liveness.isUnused(inst) or !return_type.hasRuntimeBitsIgnoreComptime(mod)) { + return null; + } + + const llvm_ret_ty = try o.lowerType(return_type); + + if (ret_ptr) |rp| { + call.setCallSret(llvm_ret_ty); + if (isByRef(return_type, mod)) { + return rp; + } else { + // our by-ref status disagrees with sret so we must load. + const loaded = self.builder.buildLoad(llvm_ret_ty, rp, ""); + loaded.setAlignment(return_type.abiAlignment(mod)); + return loaded; + } + } + + const abi_ret_ty = try lowerFnRetTy(o, fn_info); + + if (abi_ret_ty != llvm_ret_ty) { + // In this case the function return type is honoring the calling convention by having + // a different LLVM type than the usual one. We solve this here at the callsite + // by using our canonical type, then loading it if necessary. + const alignment = o.target_data.abiAlignmentOfType(abi_ret_ty); + const rp = self.buildAlloca(llvm_ret_ty, alignment); + const store_inst = self.builder.buildStore(call, rp); + store_inst.setAlignment(alignment); + if (isByRef(return_type, mod)) { + return rp; + } else { + const load_inst = self.builder.buildLoad(llvm_ret_ty, rp, ""); + load_inst.setAlignment(alignment); + return load_inst; + } + } + + if (isByRef(return_type, mod)) { + // our by-ref status disagrees with sret so we must allocate, store, + // and return the allocation pointer. + const alignment = return_type.abiAlignment(mod); + const rp = self.buildAlloca(llvm_ret_ty, alignment); + const store_inst = self.builder.buildStore(call, rp); + store_inst.setAlignment(alignment); + return rp; + } else { + return call; + } + } + + fn airCallAsync(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { + _ = inst; + return self.todo("lower call_async", .{}); + } + + fn airCallAsyncAlloc(fg: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { + const o = fg.dg.object; + const mod = o.module; + const ty_pl = fg.air.instructions.items(.data)[inst].ty_pl; + const extra = fg.air.extraData(Air.AsyncCallAlloc, ty_pl.payload); + const args: []const Air.Inst.Ref = @ptrCast(fg.air.extra[extra.end..][0..extra.data.args_len]); + const callee = try fg.resolveInst(extra.data.callee); + const callee_ty = fg.typeOf(extra.data.callee); + const zig_fn_ty = switch (callee_ty.zigTypeTag(mod)) { + .Fn => callee_ty, + .Pointer => callee_ty.childType(mod), + else => unreachable, + }; + const fn_info = mod.typeToFunc(zig_fn_ty).?; + // Remember that we want to lower calls to functions which have not yet + // been semantically analyzed. So we must not call lowerType on the + // frame type. Instead we runtime-call a function to learn the frame + // size of the callee, allocate that many bytes, and then pointer-cast + // it to the anytype->T header that we know based on the type alone. + const target = mod.getTarget(); + const llvm_i8 = o.context.intType(8); + const frame_size = fg.genFrameSize(callee); + const frame_alloca = fg.builder.buildArrayAlloca(llvm_i8, frame_size, ""); + frame_alloca.setAlignment(target.ptrBitWidth() / 4); + const frame_llvm_ty = try o.lowerAsyncFrameHeader(fn_info.return_type.toType()); + const llvm_ptr_ty = fg.context.pointerType(0); + const frame_ptr = fg.builder.buildBitCast(frame_alloca, llvm_ptr_ty, ""); + const l = asyncFrameLayout(); + const fn_ptr_ptr = fg.builder.buildStructGEP(frame_llvm_ty, frame_ptr, l.fn_ptr, ""); + _ = fg.builder.buildStore(callee, fn_ptr_ptr); + + const resume_index_ptr = fg.builder.buildStructGEP(frame_llvm_ty, frame_ptr, l.resume_index, ""); + const llvm_usize = o.context.intType(target.ptrBitWidth()); + const zero = llvm_usize.constNull(); + _ = fg.builder.buildStore(zero, resume_index_ptr); + + const awaiter_ptr = fg.builder.buildStructGEP(frame_llvm_ty, frame_ptr, l.awaiter, ""); + _ = fg.builder.buildStore(zero, awaiter_ptr); + + var llvm_args = std.ArrayList(*llvm.Value).init(fg.gpa); + defer llvm_args.deinit(); + + try addCallArgs(fg, args, &llvm_args, fn_info); + + _ = fg.builder.buildCall( + try o.lowerType(zig_fn_ty), + callee, + llvm_args.items.ptr, + @intCast(llvm_args.items.len), + .Fast, + .Auto, + "", + ); + + return frame_ptr; + } + + fn addCallArgs( + self: *FuncGen, + args: []const Air.Inst.Ref, + llvm_args: *std.ArrayList(*llvm.Value), + fn_info: InternPool.Key.FuncType, + ) !void { + const o = self.dg.object; + const mod = o.module; + const target = mod.getTarget(); var it = iterateParamTypes(o, fn_info); while (it.nextCall(self, args)) |lowering| switch (lowering) { .no_bits => continue, @@ -4824,126 +5046,6 @@ pub const FuncGen = struct { try llvm_args.append(load_inst); }, }; - - const call = self.builder.buildCall( - try o.lowerType(zig_fn_ty), - llvm_fn, - llvm_args.items.ptr, - @as(c_uint, @intCast(llvm_args.items.len)), - toLlvmCallConv(fn_info.cc, target), - attr, - "", - ); - - if (callee_ty.zigTypeTag(mod) == .Pointer) { - // Add argument attributes for function pointer calls. - it = iterateParamTypes(o, fn_info); - it.llvm_index += @intFromBool(sret); - it.llvm_index += @intFromBool(err_return_tracing); - while (it.next()) |lowering| switch (lowering) { - .byval => { - const param_index = it.zig_index - 1; - const param_ty = fn_info.param_types[param_index].toType(); - if (!isByRef(param_ty, mod)) { - o.addByValParamAttrs(call, param_ty, param_index, fn_info, it.llvm_index - 1); - } - }, - .byref => { - const param_index = it.zig_index - 1; - const param_ty = fn_info.param_types[param_index].toType(); - const param_llvm_ty = try o.lowerType(param_ty); - const alignment = param_ty.abiAlignment(mod); - o.addByRefParamAttrs(call, it.llvm_index - 1, alignment, it.byval_attr, param_llvm_ty); - }, - .byref_mut => { - o.addArgAttr(call, it.llvm_index - 1, "noundef"); - }, - // No attributes needed for these. - .no_bits, - .abi_sized_int, - .multiple_llvm_types, - .as_u16, - .float_array, - .i32_array, - .i64_array, - => continue, - - .slice => { - assert(!it.byval_attr); - const param_ty = fn_info.param_types[it.zig_index - 1].toType(); - const ptr_info = param_ty.ptrInfo(mod); - const llvm_arg_i = it.llvm_index - 2; - - if (math.cast(u5, it.zig_index - 1)) |i| { - if (@as(u1, @truncate(fn_info.noalias_bits >> i)) != 0) { - o.addArgAttr(call, llvm_arg_i, "noalias"); - } - } - if (param_ty.zigTypeTag(mod) != .Optional) { - o.addArgAttr(call, llvm_arg_i, "nonnull"); - } - if (ptr_info.flags.is_const) { - o.addArgAttr(call, llvm_arg_i, "readonly"); - } - const elem_align = ptr_info.flags.alignment.toByteUnitsOptional() orelse - @max(ptr_info.child.toType().abiAlignment(mod), 1); - o.addArgAttrInt(call, llvm_arg_i, "align", elem_align); - }, - }; - } - - if (fn_info.return_type == .noreturn_type and attr != .AlwaysTail) { - return null; - } - - if (self.liveness.isUnused(inst) or !return_type.hasRuntimeBitsIgnoreComptime(mod)) { - return null; - } - - const llvm_ret_ty = try o.lowerType(return_type); - - if (ret_ptr) |rp| { - call.setCallSret(llvm_ret_ty); - if (isByRef(return_type, mod)) { - return rp; - } else { - // our by-ref status disagrees with sret so we must load. - const loaded = self.builder.buildLoad(llvm_ret_ty, rp, ""); - loaded.setAlignment(return_type.abiAlignment(mod)); - return loaded; - } - } - - const abi_ret_ty = try lowerFnRetTy(o, fn_info); - - if (abi_ret_ty != llvm_ret_ty) { - // In this case the function return type is honoring the calling convention by having - // a different LLVM type than the usual one. We solve this here at the callsite - // by using our canonical type, then loading it if necessary. - const alignment = o.target_data.abiAlignmentOfType(abi_ret_ty); - const rp = self.buildAlloca(llvm_ret_ty, alignment); - const store_inst = self.builder.buildStore(call, rp); - store_inst.setAlignment(alignment); - if (isByRef(return_type, mod)) { - return rp; - } else { - const load_inst = self.builder.buildLoad(llvm_ret_ty, rp, ""); - load_inst.setAlignment(alignment); - return load_inst; - } - } - - if (isByRef(return_type, mod)) { - // our by-ref status disagrees with sret so we must allocate, store, - // and return the allocation pointer. - const alignment = return_type.abiAlignment(mod); - const rp = self.buildAlloca(llvm_ret_ty, alignment); - const store_inst = self.builder.buildStore(call, rp); - store_inst.setAlignment(alignment); - return rp; - } else { - return call; - } } fn buildSimplePanic(fg: *FuncGen, panic_id: Module.PanicId) !void { @@ -4988,14 +5090,27 @@ pub const FuncGen = struct { _ = fg.builder.buildUnreachable(); } - fn airCallAsync(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { - _ = inst; - return self.todo("lower call_async", .{}); - } + fn genFrameSize(fg: *FuncGen, llvm_fn: *llvm.Value) *llvm.Value { + const o = fg.dg.object; + const mod = o.module; + const target = mod.getTarget(); + const llvm_usize = o.context.intType(target.ptrBitWidth()); + const llvm_ptr_ty = o.context.pointerType(0); + const casted_fn_val = fg.builder.buildBitCast(llvm_fn, llvm_ptr_ty, ""); + const indices: [1]*llvm.Value = .{ + o.context.intType(32).constInt(@bitCast(@as(c_longlong, -1)), .True), + }; + const prefix_ptr = fg.builder.buildInBoundsGEP(llvm_usize, casted_fn_val, &indices, indices.len, ""); + const load_inst = fg.builder.buildLoad(llvm_usize, prefix_ptr, ""); - fn airCallAsyncAlloc(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { - _ = inst; - return self.todo("lower call_async_alloc", .{}); + // Some architectures (e.g SPARCv9) has different alignment + // requirements between a function/usize pointer and also require all + // loads to be aligned. On those architectures, not explicitly setting + // the alignment will lead into @frameSize generating usize-aligned + // load instruction that could crash if the function pointer happens to + // be not usize-aligned. + load_inst.setAlignment(1); + return load_inst; } fn airRet(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { @@ -11452,3 +11567,29 @@ fn constraintAllowsRegister(constraint: []const u8) bool { } } else return false; } + +/// Each field is the index in the LLVM struct. The doc comments describe what +/// the corresponding field of the LLVM struct does; not the field of the +/// AsyncFrameLayout struct. +const AsyncFrameLayout = struct { + /// Points to the return value inside the frame. + ret_val: u16, + /// This field of the frame points to the owner function so that a resume + /// on the frame pointer knows which function to call. + fn_ptr: u16, + /// This field tells which suspension point to resume from next time the function is called. + resume_index: u16, + /// This field tracks which frame is the one awaiting the frame for the purposes of resuming + /// on return. + /// A value of zero means the async function has been started, and there is no awaiter yet. + awaiter: u16, +}; + +fn asyncFrameLayout() AsyncFrameLayout { + return .{ + .fn_ptr = 0, + .resume_index = 1, + .awaiter = 2, + .ret_val = 3, + }; +} diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig index b093588e80c1dcbcd3d515ffa09b9ffefcd55828..2ac0851050be2483c456a8e5a6c973c68a66d035 100644 --- a/src/codegen/llvm/bindings.zig +++ b/src/codegen/llvm/bindings.zig @@ -549,6 +549,9 @@ pub const Builder = opaque { pub const buildAlloca = LLVMBuildAlloca; extern fn LLVMBuildAlloca(*Builder, Ty: *Type, Name: [*:0]const u8) *Value; + pub const buildArrayAlloca = LLVMBuildArrayAlloca; + extern fn LLVMBuildArrayAlloca(*Builder, Ty: *Type, Val: *Value, Name: [*:0]const u8) *Value; + pub const buildStore = LLVMBuildStore; extern fn LLVMBuildStore(*Builder, Val: *Value, Ptr: *Value) *Value; diff --git a/src/link/Coff.zig b/src/link/Coff.zig index a724d4023aa85097930022e3e4db8e2ed64e8e52..03f10ca4eb8681f77867ce017b3634ceee2e7e52 100644 --- a/src/link/Coff.zig +++ b/src/link/Coff.zig @@ -1222,7 +1222,7 @@ fn updateLazySymbolAtom( Module.SrcLoc{ .file_scope = undefined, .parent_decl_node = undefined, - .lazy = .unneeded, + .lazy = Module.LazySrcLoc.un(), }; const res = try codegen.generateLazySymbol( &self.base, diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 8d08b73d6a8c8d1a1ee83f275e4c0bcad9071a3d..8bf1ea7d8aaf737cbad53969322bde055249bb29 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -2743,7 +2743,7 @@ fn updateLazySymbolAtom( Module.SrcLoc{ .file_scope = undefined, .parent_decl_node = undefined, - .lazy = .unneeded, + .lazy = Module.LazySrcLoc.un(), }; const res = try codegen.generateLazySymbol( &self.base, diff --git a/src/link/MachO.zig b/src/link/MachO.zig index 80195a454db5be1864af0994f560745a64d92d75..ae2332b30ab61c1e8c6c802f33393d4f3b767f3c 100644 --- a/src/link/MachO.zig +++ b/src/link/MachO.zig @@ -2084,7 +2084,7 @@ fn updateLazySymbolAtom( Module.SrcLoc{ .file_scope = undefined, .parent_decl_node = undefined, - .lazy = .unneeded, + .lazy = Module.LazySrcLoc.un(), }; const res = try codegen.generateLazySymbol( &self.base, diff --git a/src/link/Plan9.zig b/src/link/Plan9.zig index ad5292aa8859caa3a9551ce3f44260ba3b5852be..0ffa376c046ed1ce2afbda820626f594f1e909d9 100644 --- a/src/link/Plan9.zig +++ b/src/link/Plan9.zig @@ -1051,7 +1051,7 @@ fn updateLazySymbolAtom(self: *Plan9, sym: File.LazySymbol, atom_index: Atom.Ind Module.SrcLoc{ .file_scope = undefined, .parent_decl_node = undefined, - .lazy = .unneeded, + .lazy = Module.LazySrcLoc.un(), }; const res = try codegen.generateLazySymbol( &self.base, -- 2.54.0