| ... | ... | @@ -117,7 +117,103 @@ const build_options = @import("build_options"); |
| 117 | 117 | pub const default_branch_quota = 1000; |
| 118 | 118 | pub const default_reference_trace_len = 2; |
| 119 | 119 | |
| 120 | | pub const InstMap = std.AutoHashMapUnmanaged(Zir.Inst.Index, Air.Inst.Ref); |
| 120 | /// Stores the mapping from `Zir.Inst.Index -> Air.Inst.Ref`, which is used by sema to resolve |
| 121 | /// instructions during analysis. |
| 122 | /// Instead of a hash table approach, InstMap is simply a slice that is indexed into using the |
| 123 | /// zir instruction index and a start offset. An index is not pressent in the map if the value |
| 124 | /// at the index is `Air.Inst.Ref.none`. |
| 125 | /// `ensureSpaceForInstructions` can be called to force InstMap to have a mapped range that |
| 126 | /// includes all instructions in a slice. After calling this function, `putAssumeCapacity*` can |
| 127 | /// be called safely for any of the instructions passed in. |
| 128 | pub const InstMap = struct { |
| 129 | items: []Air.Inst.Ref = &[_]Air.Inst.Ref{}, |
| 130 | start: Zir.Inst.Index = 0, |
| 131 | |
| 132 | pub fn deinit(map: InstMap, allocator: mem.Allocator) void { |
| 133 | allocator.free(map.items); |
| 134 | } |
| 135 | |
| 136 | pub fn get(map: InstMap, key: Zir.Inst.Index) ?Air.Inst.Ref { |
| 137 | if (!map.contains(key)) return null; |
| 138 | return map.items[key - map.start]; |
| 139 | } |
| 140 | |
| 141 | pub fn putAssumeCapacity( |
| 142 | map: *InstMap, |
| 143 | key: Zir.Inst.Index, |
| 144 | ref: Air.Inst.Ref, |
| 145 | ) void { |
| 146 | map.items[key - map.start] = ref; |
| 147 | } |
| 148 | |
| 149 | pub fn putAssumeCapacityNoClobber( |
| 150 | map: *InstMap, |
| 151 | key: Zir.Inst.Index, |
| 152 | ref: Air.Inst.Ref, |
| 153 | ) void { |
| 154 | assert(!map.contains(key)); |
| 155 | map.putAssumeCapacity(key, ref); |
| 156 | } |
| 157 | |
| 158 | pub const GetOrPutResult = struct { |
| 159 | value_ptr: *Air.Inst.Ref, |
| 160 | found_existing: bool, |
| 161 | }; |
| 162 | |
| 163 | pub fn getOrPutAssumeCapacity( |
| 164 | map: *InstMap, |
| 165 | key: Zir.Inst.Index, |
| 166 | ) GetOrPutResult { |
| 167 | const index = key - map.start; |
| 168 | return GetOrPutResult{ |
| 169 | .value_ptr = &map.items[index], |
| 170 | .found_existing = map.items[index] != .none, |
| 171 | }; |
| 172 | } |
| 173 | |
| 174 | pub fn remove(map: InstMap, key: Zir.Inst.Index) bool { |
| 175 | if (!map.contains(key)) return false; |
| 176 | map.items[key - map.start] = .none; |
| 177 | return true; |
| 178 | } |
| 179 | |
| 180 | pub fn contains(map: InstMap, key: Zir.Inst.Index) bool { |
| 181 | return map.items[key - map.start] != .none; |
| 182 | } |
| 183 | |
| 184 | pub fn ensureSpaceForInstructions( |
| 185 | map: *InstMap, |
| 186 | allocator: mem.Allocator, |
| 187 | insts: []const Zir.Inst.Index, |
| 188 | ) !void { |
| 189 | const min_max = mem.minMax(Zir.Inst.Index, insts); |
| 190 | const start = min_max.min; |
| 191 | const end = min_max.max; |
| 192 | if (map.start <= start and end < map.items.len + map.start) |
| 193 | return; |
| 194 | |
| 195 | const old_start = if (map.items.len == 0) start else map.start; |
| 196 | var better_capacity = map.items.len; |
| 197 | var better_start = old_start; |
| 198 | while (true) { |
| 199 | const extra_capacity = better_capacity / 2 + 16; |
| 200 | better_capacity += extra_capacity; |
| 201 | better_start -|= @intCast(Zir.Inst.Index, extra_capacity / 2); |
| 202 | if (better_start <= start and end < better_capacity + better_start) |
| 203 | break; |
| 204 | } |
| 205 | |
| 206 | const start_diff = old_start - better_start; |
| 207 | const new_items = try allocator.alloc(Air.Inst.Ref, better_capacity); |
| 208 | mem.set(Air.Inst.Ref, new_items[0..start_diff], .none); |
| 209 | mem.copy(Air.Inst.Ref, new_items[start_diff..], map.items); |
| 210 | mem.set(Air.Inst.Ref, new_items[start_diff + map.items.len ..], .none); |
| 211 | |
| 212 | allocator.free(map.items); |
| 213 | map.items = new_items; |
| 214 | map.start = @intCast(Zir.Inst.Index, better_start); |
| 215 | } |
| 216 | }; |
| 121 | 217 | |
| 122 | 218 | /// This is the context needed to semantically analyze ZIR instructions and |
| 123 | 219 | /// produce AIR instructions. |
| ... | ... | @@ -753,6 +849,8 @@ fn analyzeBodyInner( |
| 753 | 849 | ) CompileError!Zir.Inst.Index { |
| 754 | 850 | // No tracy calls here, to avoid interfering with the tail call mechanism. |
| 755 | 851 | |
| 852 | try sema.inst_map.ensureSpaceForInstructions(sema.gpa, body); |
| 853 | |
| 756 | 854 | const parent_capture_scope = block.wip_capture_scope; |
| 757 | 855 | |
| 758 | 856 | var wip_captures = WipCaptureScope{ |
| ... | ... | @@ -1443,7 +1541,7 @@ fn analyzeBodyInner( |
| 1443 | 1541 | labeled_block.destroy(gpa); |
| 1444 | 1542 | assert(sema.post_hoc_blocks.remove(new_block_inst)); |
| 1445 | 1543 | } |
| 1446 | | try map.put(gpa, inst, block_result); |
| 1544 | map.putAssumeCapacity(inst, block_result); |
| 1447 | 1545 | i += 1; |
| 1448 | 1546 | continue; |
| 1449 | 1547 | } |
| ... | ... | @@ -1622,7 +1720,7 @@ fn analyzeBodyInner( |
| 1622 | 1720 | const extra = sema.code.extraData(Zir.Inst.DeferErrCode, inst_data.payload_index).data; |
| 1623 | 1721 | const defer_body = sema.code.extra[extra.index..][0..extra.len]; |
| 1624 | 1722 | const err_code = try sema.resolveInst(inst_data.err_code); |
| 1625 | | try sema.inst_map.put(sema.gpa, extra.remapped_err_code, err_code); |
| 1723 | sema.inst_map.putAssumeCapacity(extra.remapped_err_code, err_code); |
| 1626 | 1724 | const break_inst = sema.analyzeBodyInner(block, defer_body) catch |err| switch (err) { |
| 1627 | 1725 | error.ComptimeBreak => sema.comptime_break_inst, |
| 1628 | 1726 | else => |e| return e, |
| ... | ... | @@ -1633,7 +1731,7 @@ fn analyzeBodyInner( |
| 1633 | 1731 | }; |
| 1634 | 1732 | if (sema.typeOf(air_inst).isNoReturn()) |
| 1635 | 1733 | break always_noreturn; |
| 1636 | | try map.put(sema.gpa, inst, air_inst); |
| 1734 | map.putAssumeCapacity(inst, air_inst); |
| 1637 | 1735 | i += 1; |
| 1638 | 1736 | } else unreachable; |
| 1639 | 1737 | |
| ... | ... | @@ -5980,7 +6078,7 @@ fn zirCall( |
| 5980 | 6078 | } |
| 5981 | 6079 | |
| 5982 | 6080 | const param_ty_inst = try sema.addType(param_ty); |
| 5983 | | try sema.inst_map.put(sema.gpa, inst, param_ty_inst); |
| 6081 | sema.inst_map.putAssumeCapacity(inst, param_ty_inst); |
| 5984 | 6082 | |
| 5985 | 6083 | const resolved = try sema.resolveBody(block, args_body[arg_start..arg_end], inst); |
| 5986 | 6084 | const resolved_ty = sema.typeOf(resolved); |
| ... | ... | @@ -6368,6 +6466,8 @@ fn analyzeCall( |
| 6368 | 6466 | // which means its parameter type expressions must be resolved in order and used |
| 6369 | 6467 | // to successively coerce the arguments. |
| 6370 | 6468 | const fn_info = sema.code.getFnInfo(module_fn.zir_body_inst); |
| 6469 | try sema.inst_map.ensureSpaceForInstructions(sema.gpa, fn_info.param_body); |
| 6470 | |
| 6371 | 6471 | var arg_i: usize = 0; |
| 6372 | 6472 | for (fn_info.param_body) |inst| { |
| 6373 | 6473 | sema.analyzeInlineCallArg( |
| ... | ... | @@ -6662,7 +6762,7 @@ fn analyzeInlineCallArg( |
| 6662 | 6762 | const casted_arg = try sema.coerce(arg_block, param_ty, uncasted_arg, arg_src); |
| 6663 | 6763 | |
| 6664 | 6764 | if (is_comptime_call) { |
| 6665 | | try sema.inst_map.putNoClobber(sema.gpa, inst, casted_arg); |
| 6765 | sema.inst_map.putAssumeCapacityNoClobber(inst, casted_arg); |
| 6666 | 6766 | const arg_val = sema.resolveConstMaybeUndefVal(arg_block, arg_src, casted_arg, "argument to function being called at comptime must be comptime-known") catch |err| { |
| 6667 | 6767 | if (err == error.AnalysisFail and param_block.comptime_reason != null) try param_block.comptime_reason.?.explain(sema, sema.err); |
| 6668 | 6768 | return err; |
| ... | ... | @@ -6686,13 +6786,13 @@ fn analyzeInlineCallArg( |
| 6686 | 6786 | .val = arg_val, |
| 6687 | 6787 | }; |
| 6688 | 6788 | } else if (zir_tags[inst] == .param_comptime or try sema.typeRequiresComptime(param_ty)) { |
| 6689 | | try sema.inst_map.putNoClobber(sema.gpa, inst, casted_arg); |
| 6789 | sema.inst_map.putAssumeCapacityNoClobber(inst, casted_arg); |
| 6690 | 6790 | } else if (try sema.resolveMaybeUndefVal(casted_arg)) |val| { |
| 6691 | 6791 | // We have a comptime value but we need a runtime value to preserve inlining semantics, |
| 6692 | 6792 | const wrapped = try sema.addConstant(param_ty, try Value.Tag.runtime_value.create(sema.arena, val)); |
| 6693 | | try sema.inst_map.putNoClobber(sema.gpa, inst, wrapped); |
| 6793 | sema.inst_map.putAssumeCapacityNoClobber(inst, wrapped); |
| 6694 | 6794 | } else { |
| 6695 | | try sema.inst_map.putNoClobber(sema.gpa, inst, casted_arg); |
| 6795 | sema.inst_map.putAssumeCapacityNoClobber(inst, casted_arg); |
| 6696 | 6796 | } |
| 6697 | 6797 | |
| 6698 | 6798 | arg_i.* += 1; |
| ... | ... | @@ -6704,7 +6804,7 @@ fn analyzeInlineCallArg( |
| 6704 | 6804 | const param_ty = sema.typeOf(uncasted_arg); |
| 6705 | 6805 | |
| 6706 | 6806 | if (is_comptime_call) { |
| 6707 | | try sema.inst_map.putNoClobber(sema.gpa, inst, uncasted_arg); |
| 6807 | sema.inst_map.putAssumeCapacityNoClobber(inst, uncasted_arg); |
| 6708 | 6808 | const arg_val = sema.resolveConstMaybeUndefVal(arg_block, arg_src, uncasted_arg, "argument to function being called at comptime must be comptime-known") catch |err| { |
| 6709 | 6809 | if (err == error.AnalysisFail and param_block.comptime_reason != null) try param_block.comptime_reason.?.explain(sema, sema.err); |
| 6710 | 6810 | return err; |
| ... | ... | @@ -6728,13 +6828,13 @@ fn analyzeInlineCallArg( |
| 6728 | 6828 | .val = arg_val, |
| 6729 | 6829 | }; |
| 6730 | 6830 | } else if (zir_tags[inst] == .param_anytype_comptime or try sema.typeRequiresComptime(param_ty)) { |
| 6731 | | try sema.inst_map.putNoClobber(sema.gpa, inst, uncasted_arg); |
| 6831 | sema.inst_map.putAssumeCapacityNoClobber(inst, uncasted_arg); |
| 6732 | 6832 | } else if (try sema.resolveMaybeUndefVal(uncasted_arg)) |val| { |
| 6733 | 6833 | // We have a comptime value but we need a runtime value to preserve inlining semantics, |
| 6734 | 6834 | const wrapped = try sema.addConstant(param_ty, try Value.Tag.runtime_value.create(sema.arena, val)); |
| 6735 | | try sema.inst_map.putNoClobber(sema.gpa, inst, wrapped); |
| 6835 | sema.inst_map.putAssumeCapacityNoClobber(inst, wrapped); |
| 6736 | 6836 | } else { |
| 6737 | | try sema.inst_map.putNoClobber(sema.gpa, inst, uncasted_arg); |
| 6837 | sema.inst_map.putAssumeCapacityNoClobber(inst, uncasted_arg); |
| 6738 | 6838 | } |
| 6739 | 6839 | |
| 6740 | 6840 | arg_i.* += 1; |
| ... | ... | @@ -7008,7 +7108,8 @@ fn instantiateGenericCall( |
| 7008 | 7108 | child_block.params.deinit(gpa); |
| 7009 | 7109 | } |
| 7010 | 7110 | |
| 7011 | | try child_sema.inst_map.ensureUnusedCapacity(gpa, @intCast(u32, uncasted_args.len)); |
| 7111 | try child_sema.inst_map.ensureSpaceForInstructions(gpa, fn_info.param_body); |
| 7112 | |
| 7012 | 7113 | var arg_i: usize = 0; |
| 7013 | 7114 | for (fn_info.param_body) |inst| { |
| 7014 | 7115 | var is_comptime = false; |
| ... | ... | @@ -8111,6 +8212,7 @@ fn resolveGenericBody( |
| 8111 | 8212 | block.params.deinit(sema.gpa); |
| 8112 | 8213 | block.params = prev_params; |
| 8113 | 8214 | } |
| 8215 | |
| 8114 | 8216 | const uncasted = sema.resolveBody(block, body, func_inst) catch |err| break :err err; |
| 8115 | 8217 | const result = sema.coerce(block, dest_ty, uncasted, src) catch |err| break :err err; |
| 8116 | 8218 | const val = sema.resolveConstValue(block, src, result, reason) catch |err| break :err err; |
| ... | ... | @@ -8660,7 +8762,7 @@ fn zirParam( |
| 8660 | 8762 | .is_comptime = comptime_syntax, |
| 8661 | 8763 | .name = param_name, |
| 8662 | 8764 | }); |
| 8663 | | try sema.inst_map.putNoClobber(sema.gpa, inst, .generic_poison); |
| 8765 | sema.inst_map.putAssumeCapacityNoClobber(inst, .generic_poison); |
| 8664 | 8766 | return; |
| 8665 | 8767 | }, |
| 8666 | 8768 | else => |e| return e, |
| ... | ... | @@ -8676,7 +8778,7 @@ fn zirParam( |
| 8676 | 8778 | .is_comptime = comptime_syntax, |
| 8677 | 8779 | .name = param_name, |
| 8678 | 8780 | }); |
| 8679 | | try sema.inst_map.putNoClobber(sema.gpa, inst, .generic_poison); |
| 8781 | sema.inst_map.putAssumeCapacityNoClobber(inst, .generic_poison); |
| 8680 | 8782 | return; |
| 8681 | 8783 | }, |
| 8682 | 8784 | else => |e| return e, |
| ... | ... | @@ -8700,7 +8802,7 @@ fn zirParam( |
| 8700 | 8802 | // non-anytype parameter that ended up being a one-possible-type. |
| 8701 | 8803 | // We don't want the parameter to be part of the instantiated function type. |
| 8702 | 8804 | const result = try sema.addConstant(param_ty, opv); |
| 8703 | | try sema.inst_map.put(sema.gpa, inst, result); |
| 8805 | sema.inst_map.putAssumeCapacity(inst, result); |
| 8704 | 8806 | return; |
| 8705 | 8807 | } |
| 8706 | 8808 | } |
| ... | ... | @@ -8715,7 +8817,7 @@ fn zirParam( |
| 8715 | 8817 | // If this is a comptime parameter we can add a constant generic_poison |
| 8716 | 8818 | // since this is also a generic parameter. |
| 8717 | 8819 | const result = try sema.addConstant(param_ty, Value.initTag(.generic_poison)); |
| 8718 | | try sema.inst_map.putNoClobber(sema.gpa, inst, result); |
| 8820 | sema.inst_map.putAssumeCapacityNoClobber(inst, result); |
| 8719 | 8821 | } else { |
| 8720 | 8822 | // Otherwise we need a dummy runtime instruction. |
| 8721 | 8823 | const result_index = @intCast(Air.Inst.Index, sema.air_instructions.len); |
| ... | ... | @@ -8724,7 +8826,7 @@ fn zirParam( |
| 8724 | 8826 | .data = .{ .ty = param_ty }, |
| 8725 | 8827 | }); |
| 8726 | 8828 | const result = Air.indexToRef(result_index); |
| 8727 | | try sema.inst_map.putNoClobber(sema.gpa, inst, result); |
| 8829 | sema.inst_map.putAssumeCapacityNoClobber(inst, result); |
| 8728 | 8830 | } |
| 8729 | 8831 | } |
| 8730 | 8832 | |
| ... | ... | @@ -8763,7 +8865,7 @@ fn zirParamAnytype( |
| 8763 | 8865 | .is_comptime = comptime_syntax, |
| 8764 | 8866 | .name = param_name, |
| 8765 | 8867 | }); |
| 8766 | | try sema.inst_map.put(sema.gpa, inst, .generic_poison); |
| 8868 | sema.inst_map.putAssumeCapacity(inst, .generic_poison); |
| 8767 | 8869 | } |
| 8768 | 8870 | |
| 8769 | 8871 | fn zirAs(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| ... | ... | @@ -11220,7 +11322,7 @@ fn maybeErrorUnwrap(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, op |
| 11220 | 11322 | }; |
| 11221 | 11323 | if (sema.typeOf(air_inst).isNoReturn()) |
| 11222 | 11324 | return true; |
| 11223 | | try sema.inst_map.put(sema.gpa, inst, air_inst); |
| 11325 | sema.inst_map.putAssumeCapacity(inst, air_inst); |
| 11224 | 11326 | } |
| 11225 | 11327 | unreachable; |
| 11226 | 11328 | } |
| ... | ... | @@ -16348,7 +16450,7 @@ fn zirTryPtr(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileErr |
| 16348 | 16450 | // break from an inline loop. In such case we must convert it to |
| 16349 | 16451 | // a runtime break. |
| 16350 | 16452 | fn addRuntimeBreak(sema: *Sema, child_block: *Block, break_data: BreakData) !void { |
| 16351 | | const gop = try sema.inst_map.getOrPut(sema.gpa, break_data.block_inst); |
| 16453 | const gop = sema.inst_map.getOrPutAssumeCapacity(break_data.block_inst); |
| 16352 | 16454 | const labeled_block = if (!gop.found_existing) blk: { |
| 16353 | 16455 | try sema.post_hoc_blocks.ensureUnusedCapacity(sema.gpa, 1); |
| 16354 | 16456 | |