diff --git a/BRANCH_TODO b/BRANCH_TODO index fd005a8276..aad837d80b 100644 --- a/BRANCH_TODO +++ b/BRANCH_TODO @@ -34,3 +34,4 @@ Performance optimizations to look into: * enum literals can use small strings * string literals can use small strings * don't need the Sema coercion on condbr condition, it's done with result locations + * remove unreachable_value diff --git a/src/Module.zig b/src/Module.zig index 9fdc1c2c9a..8791452d99 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -1013,7 +1013,7 @@ pub const Scope = struct { .cc = args.cc, .param_types_len = @intCast(u32, args.param_types.len), }); - gz.zir_code.extra.appendSliceAssumeCapacity(mem.bytesAsSlice(u32, mem.sliceAsBytes(args.param_types))); + gz.zir_code.appendRefsAssumeCapacity(args.param_types); const new_index = @intCast(zir.Inst.Index, gz.zir_code.instructions.len); gz.zir_code.instructions.appendAssumeCapacity(.{ @@ -1024,7 +1024,7 @@ pub const Scope = struct { } }, }); gz.instructions.appendAssumeCapacity(new_index); - return zir.Inst.Ref.fromIndex(new_index, gz.zir_code.param_count); + return gz.zir_code.indexToRef(new_index); } pub fn addFnType( @@ -1043,7 +1043,7 @@ pub const Scope = struct { const payload_index = gz.zir_code.addExtraAssumeCapacity(zir.Inst.FnType{ .param_types_len = @intCast(u32, param_types.len), }); - gz.zir_code.extra.appendSliceAssumeCapacity(mem.bytesAsSlice(u32, mem.sliceAsBytes(param_types))); + gz.zir_code.appendRefsAssumeCapacity(param_types); const new_index = @intCast(zir.Inst.Index, gz.zir_code.instructions.len); gz.zir_code.instructions.appendAssumeCapacity(.{ @@ -1054,7 +1054,7 @@ pub const Scope = struct { } }, }); gz.instructions.appendAssumeCapacity(new_index); - return zir.Inst.Ref.fromIndex(new_index, gz.zir_code.param_count); + return gz.zir_code.indexToRef(new_index); } pub fn addCall( @@ -1077,7 +1077,7 @@ pub const Scope = struct { .callee = callee, .args_len = @intCast(u32, args.len), }); - gz.zir_code.extra.appendSliceAssumeCapacity(mem.bytesAsSlice(u32, mem.sliceAsBytes(args))); + gz.zir_code.appendRefsAssumeCapacity(args); const new_index = @intCast(zir.Inst.Index, gz.zir_code.instructions.len); gz.zir_code.instructions.appendAssumeCapacity(.{ @@ -1088,7 +1088,7 @@ pub const Scope = struct { } }, }); gz.instructions.appendAssumeCapacity(new_index); - return zir.Inst.Ref.fromIndex(new_index, gz.zir_code.param_count); + return gz.zir_code.indexToRef(new_index); } /// Note that this returns a `zir.Inst.Index` not a ref. @@ -1160,7 +1160,7 @@ pub const Scope = struct { } }, }); gz.instructions.appendAssumeCapacity(new_index); - return zir.Inst.Ref.fromIndex(new_index, gz.zir_code.param_count); + return gz.zir_code.indexToRef(new_index); } pub fn addArrayTypeSentinel( @@ -1186,7 +1186,7 @@ pub const Scope = struct { } }, }); gz.instructions.appendAssumeCapacity(new_index); - return zir.Inst.Ref.fromIndex(new_index, gz.zir_code.param_count); + return gz.zir_code.indexToRef(new_index); } pub fn addUnTok( @@ -1317,7 +1317,7 @@ pub const Scope = struct { const new_index = @intCast(zir.Inst.Index, gz.zir_code.instructions.len); gz.zir_code.instructions.appendAssumeCapacity(inst); gz.instructions.appendAssumeCapacity(new_index); - return zir.Inst.Ref.fromIndex(new_index, gz.zir_code.param_count); + return gz.zir_code.indexToRef(new_index); } }; @@ -1366,9 +1366,9 @@ pub const WipZirCode = struct { instructions: std.MultiArrayList(zir.Inst) = .{}, string_bytes: std.ArrayListUnmanaged(u8) = .{}, extra: std.ArrayListUnmanaged(u32) = .{}, - /// We need to keep track of this count in order to convert between - /// `zir.Inst.Ref` and `zir.Inst.Index` types. - param_count: u32 = 0, + /// The end of special indexes. `zir.Inst.Ref` subtracts against this number to convert + /// to `zir.Inst.Index`. The default here is correct if there are 0 parameters. + ref_start_index: u32 = zir.Inst.Ref.typed_value_map.len, decl: *Decl, gpa: *Allocator, arena: *Allocator, @@ -1386,20 +1386,43 @@ pub const WipZirCode = struct { wzc.extra.appendAssumeCapacity(switch (field.field_type) { u32 => @field(extra, field.name), zir.Inst.Ref => @enumToInt(@field(extra, field.name)), - else => unreachable, + else => @compileError("bad field type"), }); } return result; } - pub fn refIsNoReturn(wzc: WipZirCode, zir_inst_ref: zir.Inst.Ref) bool { - if (zir_inst_ref == .unreachable_value) return true; - if (zir_inst_ref.toIndex(wzc.param_count)) |zir_inst| { - return wzc.instructions.items(.tag)[zir_inst].isNoReturn(); + pub fn appendRefs(wzc: *WipZirCode, refs: []const zir.Inst.Ref) !void { + const coerced = @bitCast([]const u32, refs); + return wzc.extra.appendSlice(wzc.gpa, coerced); + } + + pub fn appendRefsAssumeCapacity(wzc: *WipZirCode, refs: []const zir.Inst.Ref) void { + const coerced = @bitCast([]const u32, refs); + wzc.extra.appendSliceAssumeCapacity(coerced); + } + + pub fn refIsNoReturn(wzc: WipZirCode, inst_ref: zir.Inst.Ref) bool { + if (inst_ref == .unreachable_value) return true; + if (wzc.refToIndex(inst_ref)) |inst_index| { + return wzc.instructions.items(.tag)[inst_index].isNoReturn(); } return false; } + pub fn indexToRef(wzc: WipZirCode, inst: zir.Inst.Index) zir.Inst.Ref { + return @intToEnum(zir.Inst.Ref, wzc.ref_start_index + inst); + } + + pub fn refToIndex(wzc: WipZirCode, inst: zir.Inst.Ref) ?zir.Inst.Index { + const ref_int = @enumToInt(inst); + if (ref_int >= wzc.ref_start_index) { + return ref_int - wzc.ref_start_index; + } else { + return null; + } + } + pub fn deinit(wzc: *WipZirCode) void { wzc.instructions.deinit(wzc.gpa); wzc.extra.deinit(wzc.gpa); @@ -2075,7 +2098,7 @@ fn astgenAndSemaFn( // The AST params array does not contain anytype and ... parameters. // We must iterate to count how many param types to allocate. const param_count = blk: { - var count: u32 = 0; + var count: usize = 0; var it = fn_proto.iterate(tree); while (it.next()) |param| { if (param.anytype_ellipsis3) |some| if (token_tags[some] == .ellipsis3) break; @@ -2297,7 +2320,7 @@ fn astgenAndSemaFn( .decl = decl, .arena = &decl_arena.allocator, .gpa = mod.gpa, - .param_count = param_count, + .ref_start_index = @intCast(u32, zir.Inst.Ref.typed_value_map.len + param_count), }; defer wip_zir_code.deinit(); @@ -2314,7 +2337,7 @@ fn astgenAndSemaFn( try wip_zir_code.extra.ensureCapacity(mod.gpa, param_count); var params_scope = &gen_scope.base; - var i: u32 = 0; + var i: usize = 0; var it = fn_proto.iterate(tree); while (it.next()) |param| : (i += 1) { const name_token = param.name_token.?; @@ -2325,7 +2348,7 @@ fn astgenAndSemaFn( .gen_zir = &gen_scope, .name = param_name, // Implicit const list first, then implicit arg list. - .inst = zir.Inst.Ref.fromParam(i), + .inst = @intToEnum(zir.Inst.Ref, @intCast(u32, zir.Inst.Ref.typed_value_map.len + i)), .src = decl.tokSrcLoc(name_token), }; params_scope = &sub_scope.base; diff --git a/src/Sema.zig b/src/Sema.zig index 86d18d283c..e2f2022716 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -300,18 +300,28 @@ pub fn analyzeBody(sema: *Sema, block: *Scope.Block, body: []const zir.Inst.Inde } /// TODO when we rework TZIR memory layout, this function will no longer have a possible error. -/// Until then we allocate memory for a new, mutable `ir.Inst` to match what TZIR expects. pub fn resolveInst(sema: *Sema, zir_ref: zir.Inst.Ref) error{OutOfMemory}!*ir.Inst { - if (zir_ref.toTypedValue()) |typed_value| { - return sema.mod.constInst(sema.arena, .unneeded, typed_value); - } + var i: usize = @enumToInt(zir_ref); - const param_count = @intCast(u32, sema.param_inst_list.len); - if (zir_ref.toParam(param_count)) |param| { - return sema.param_inst_list[param]; + // First section of indexes correspond to a set number of constant values. + if (i < zir.Inst.Ref.typed_value_map.len) { + // TODO when we rework TZIR memory layout, this function can be as simple as: + // if (zir_ref < zir.const_inst_list.len + sema.param_count) + // return zir_ref; + // Until then we allocate memory for a new, mutable `ir.Inst` to match what + // TZIR expects. + return sema.mod.constInst(sema.arena, .unneeded, zir.Inst.Ref.typed_value_map[i]); } + i -= zir.Inst.Ref.typed_value_map.len; - return sema.inst_map[zir_ref.toIndex(param_count).?]; + // Next section of indexes correspond to function parameters, if any. + if (i < sema.param_inst_list.len) { + return sema.param_inst_list[i]; + } + i -= sema.param_inst_list.len; + + // Finally, the last section of indexes refers to the map of ZIR=>TZIR. + return sema.inst_map[i]; } fn resolveConstString( @@ -753,8 +763,7 @@ fn zirCompileLog(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerEr const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const extra = sema.code.extraData(zir.Inst.MultiOp, inst_data.payload_index); - const raw_args = sema.code.extra[extra.end..][0..extra.data.operands_len]; - const args = mem.bytesAsSlice(zir.Inst.Ref, mem.sliceAsBytes(raw_args)); + const args = sema.code.refSlice(extra.end, extra.data.operands_len); for (args) |arg_ref, i| { if (i != 0) try writer.print(", ", .{}); @@ -1105,8 +1114,7 @@ fn zirCall( const func_src: LazySrcLoc = .{ .node_offset_call_func = inst_data.src_node }; const call_src = inst_data.src(); const extra = sema.code.extraData(zir.Inst.Call, inst_data.payload_index); - const raw_args = sema.code.extra[extra.end..][0..extra.data.args_len]; - const args = mem.bytesAsSlice(zir.Inst.Ref, mem.sliceAsBytes(raw_args)); + const args = sema.code.refSlice(extra.end, extra.data.args_len); return sema.analyzeCall(block, extra.data.callee, func_src, call_src, modifier, ensure_result_used, args); } @@ -1733,8 +1741,7 @@ fn zirFnType(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index, var_args: b const inst_data = sema.code.instructions.items(.data)[inst].fn_type; const extra = sema.code.extraData(zir.Inst.FnType, inst_data.payload_index); - const raw_param_types = sema.code.extra[extra.end..][0..extra.data.param_types_len]; - const param_types = mem.bytesAsSlice(zir.Inst.Ref, mem.sliceAsBytes(raw_param_types)); + const param_types = sema.code.refSlice(extra.end, extra.data.param_types_len); return sema.fnTypeCommon( block, @@ -1752,8 +1759,7 @@ fn zirFnTypeCc(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index, var_args: const inst_data = sema.code.instructions.items(.data)[inst].fn_type; const extra = sema.code.extraData(zir.Inst.FnTypeCc, inst_data.payload_index); - const raw_param_types = sema.code.extra[extra.end..][0..extra.data.param_types_len]; - const param_types = mem.bytesAsSlice(zir.Inst.Ref, mem.sliceAsBytes(raw_param_types)); + const param_types = sema.code.refSlice(extra.end, extra.data.param_types_len); const cc_tv = try sema.resolveInstConst(block, .todo, extra.data.cc); // TODO once we're capable of importing and analyzing decls from @@ -2768,8 +2774,7 @@ fn zirTypeofPeer(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerEr const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src = inst_data.src(); const extra = sema.code.extraData(zir.Inst.MultiOp, inst_data.payload_index); - const raw_args = sema.code.extra[extra.end..][0..extra.data.operands_len]; - const args = mem.bytesAsSlice(zir.Inst.Ref, mem.sliceAsBytes(raw_args)); + const args = sema.code.refSlice(extra.end, extra.data.operands_len); const inst_list = try sema.gpa.alloc(*ir.Inst, extra.data.operands_len); defer sema.gpa.free(inst_list); diff --git a/src/astgen.zig b/src/astgen.zig index b5eb5b8ec2..4ad7172773 100644 --- a/src/astgen.zig +++ b/src/astgen.zig @@ -926,7 +926,7 @@ fn labeledBlockExpr( // would be better still to elide the ones that are in this list. try block_scope.setBlockBody(block_inst); - return zir.Inst.Ref.fromIndex(block_inst, gz.zir_code.param_count); + return gz.zir_code.indexToRef(block_inst); }, .break_operand => { // All break operands are values that did not use the result location pointer. @@ -939,7 +939,7 @@ fn labeledBlockExpr( // would be better still to elide the ones that are in this list. } try block_scope.setBlockBody(block_inst); - const block_ref = zir.Inst.Ref.fromIndex(block_inst, gz.zir_code.param_count); + const block_ref = gz.zir_code.indexToRef(block_inst); switch (rl) { .ref => return block_ref, else => return rvalue(mod, parent_scope, rl, block_ref, block_node), @@ -991,7 +991,7 @@ fn blockExprStmts( // We need to emit an error if the result is not `noreturn` or `void`, but // we want to avoid adding the ZIR instruction if possible for performance. const maybe_unused_result = try expr(mod, scope, .none, statement); - const elide_check = if (maybe_unused_result.toIndex(gz.zir_code.param_count)) |inst| b: { + const elide_check = if (gz.zir_code.refToIndex(maybe_unused_result)) |inst| b: { // Note that this array becomes invalid after appending more items to it // in the above while loop. const zir_tags = gz.zir_code.instructions.items(.tag); @@ -1292,7 +1292,7 @@ fn varDecl( const expected_len = parent_zir.items.len + init_scope.instructions.items.len - 2; try parent_zir.ensureCapacity(mod.gpa, expected_len); for (init_scope.instructions.items) |src_inst| { - if (zir.Inst.Ref.fromIndex(src_inst, wzc.param_count) == init_scope.rl_ptr) continue; + if (wzc.indexToRef(src_inst) == init_scope.rl_ptr) continue; if (zir_tags[src_inst] == .store_to_block_ptr) { if (zir_datas[src_inst].bin.lhs == init_scope.rl_ptr) continue; } @@ -1525,7 +1525,7 @@ fn ptrType( } const new_index = @intCast(zir.Inst.Index, gz.zir_code.instructions.len); - const result = zir.Inst.Ref.fromIndex(new_index, gz.zir_code.param_count); + const result = gz.zir_code.indexToRef(new_index); gz.zir_code.instructions.appendAssumeCapacity(.{ .tag = .ptr_type, .data = .{ .ptr_type = .{ .flags = .{ @@ -1782,7 +1782,7 @@ fn finishThenElseBlock( } assert(!strat.elide_store_to_block_ptr_instructions); try setCondBrPayload(condbr, cond, then_scope, else_scope); - return zir.Inst.Ref.fromIndex(main_block, wzc.param_count); + return wzc.indexToRef(main_block); }, .break_operand => { if (!wzc.refIsNoReturn(then_result)) { @@ -1818,7 +1818,7 @@ fn finishThenElseBlock( } else { try setCondBrPayload(condbr, cond, then_scope, else_scope); } - const block_ref = zir.Inst.Ref.fromIndex(main_block, wzc.param_count); + const block_ref = wzc.indexToRef(main_block); switch (rl) { .ref => return block_ref, else => return rvalue(mod, parent_scope, rl, block_ref, node), @@ -1981,7 +1981,7 @@ fn boolBinOp( _ = try rhs_scope.addUnNode(.break_flat, rhs, node); try rhs_scope.setBoolBrBody(bool_br); - const block_ref = zir.Inst.Ref.fromIndex(bool_br, gz.zir_code.param_count); + const block_ref = gz.zir_code.indexToRef(bool_br); return rvalue(mod, scope, rl, block_ref, node); } @@ -3092,7 +3092,7 @@ fn asmExpr( try gz.zir_code.extra.ensureCapacity(mod.gpa, gz.zir_code.extra.items.len + args.len + constraints.len); - gz.zir_code.extra.appendSliceAssumeCapacity(mem.bytesAsSlice(u32, mem.sliceAsBytes(args))); + gz.zir_code.appendRefsAssumeCapacity(args); gz.zir_code.extra.appendSliceAssumeCapacity(constraints); return rvalue(mod, scope, rl, result, node); @@ -3164,7 +3164,7 @@ fn asRlPtr( const expected_len = parent_zir.items.len + as_scope.instructions.items.len - 2; try parent_zir.ensureCapacity(mod.gpa, expected_len); for (as_scope.instructions.items) |src_inst| { - if (zir.Inst.Ref.fromIndex(src_inst, wzc.param_count) == as_scope.rl_ptr) continue; + if (wzc.indexToRef(src_inst) == as_scope.rl_ptr) continue; if (zir_tags[src_inst] == .store_to_block_ptr) { if (zir_datas[src_inst].bin.lhs == as_scope.rl_ptr) continue; } @@ -3256,7 +3256,7 @@ fn typeOf( } const result = try gz.addPlNode(.typeof_peer, node, zir.Inst.MultiOp{ .operands_len = @intCast(u32, params.len) }); - try gz.zir_code.extra.appendSlice(gz.zir_code.gpa, mem.bytesAsSlice(u32, mem.sliceAsBytes(items))); + try gz.zir_code.appendRefs(items); return rvalue(mod, scope, rl, result, node); } diff --git a/src/zir.zig b/src/zir.zig index 6bb8de9003..df62d3051a 100644 --- a/src/zir.zig +++ b/src/zir.zig @@ -70,6 +70,11 @@ pub const Code = struct { return code.string_bytes[index..end :0]; } + pub fn refSlice(code: Code, start: usize, len: usize) []Inst.Ref { + const raw_slice = code.extra[start..][0..len]; + return @bitCast([]Inst.Ref, raw_slice); + } + pub fn deinit(code: *Code, gpa: *Allocator) void { code.instructions.deinit(gpa); gpa.free(code.string_bytes); @@ -767,16 +772,17 @@ pub const Inst = struct { /// of the current function or a ZIR instruction. /// /// The first values after the the last tag refer to parameters which may be - /// derived by subtracting typed_value_count. + /// derived by subtracting typed_value_map.len. /// /// All further values refer to ZIR instructions which may be derived by - /// subtracting typed_value_count and the number of parameters. + /// subtracting typed_value_map.len and the number of parameters. /// /// When adding a tag to this enum, consider adding a corresponding entry to /// `simple_types` in astgen. /// - /// This is packed so that it is safe to cast between `[]u32` and `[]Ref`. - pub const Ref = packed enum(u32) { + /// The tag type is specified so that it is safe to bitcast between `[]u32` + /// and `[]Ref`. + pub const Ref = enum(u32) { /// This Ref does not correspond to any ZIR instruction or constant /// value and may instead be used as a sentinel to indicate null. none, @@ -841,8 +847,7 @@ pub const Inst = struct { _, - pub const typed_value_count = @as(u32, typed_value_map.len); - const typed_value_map = std.enums.directEnumArray(Ref, TypedValue, 0, .{ + pub const typed_value_map = std.enums.directEnumArray(Ref, TypedValue, 0, .{ .none = undefined, .u8_type = .{ @@ -1039,36 +1044,6 @@ pub const Inst = struct { .val = Value.initTag(.bool_false), }, }); - - pub fn fromParam(param: u32) Ref { - return @intToEnum(Ref, typed_value_count + param); - } - - pub fn fromIndex(index: Index, param_count: u32) Ref { - return @intToEnum(Ref, typed_value_count + param_count + index); - } - - pub fn toTypedValue(ref: Ref) ?TypedValue { - assert(ref != .none); - if (@enumToInt(ref) >= typed_value_count) return null; - return typed_value_map[@enumToInt(ref)]; - } - - pub fn toParam(ref: Ref, param_count: u32) ?u32 { - assert(ref != .none); - if (@enumToInt(ref) < typed_value_count or - @enumToInt(ref) >= typed_value_count + param_count) - { - return null; - } - return @enumToInt(ref) - typed_value_count; - } - - pub fn toIndex(ref: Ref, param_count: u32) ?Index { - assert(ref != .none); - if (@enumToInt(ref) < typed_value_count + param_count) return null; - return @enumToInt(ref) - typed_value_count - param_count; - } }; /// All instructions have an 8-byte payload, which is contained within @@ -1672,8 +1647,7 @@ const Writer = struct { fn writePlNodeCall(self: *Writer, stream: anytype, inst: Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const extra = self.code.extraData(Inst.Call, inst_data.payload_index); - const raw_args = self.code.extra[extra.end..][0..extra.data.args_len]; - const args = mem.bytesAsSlice(Inst.Ref, mem.sliceAsBytes(raw_args)); + const args = self.code.refSlice(extra.end, extra.data.args_len); try self.writeInstRef(stream, extra.data.callee); try stream.writeAll(", ["); @@ -1767,8 +1741,7 @@ const Writer = struct { ) (@TypeOf(stream).Error || error{OutOfMemory})!void { const inst_data = self.code.instructions.items(.data)[inst].fn_type; const extra = self.code.extraData(Inst.FnType, inst_data.payload_index); - const raw_param_types = self.code.extra[extra.end..][0..extra.data.param_types_len]; - const param_types = mem.bytesAsSlice(Inst.Ref, mem.sliceAsBytes(raw_param_types)); + const param_types = self.code.refSlice(extra.end, extra.data.param_types_len); return self.writeFnTypeCommon(stream, param_types, inst_data.return_type, var_args, .none); } @@ -1793,8 +1766,7 @@ const Writer = struct { ) (@TypeOf(stream).Error || error{OutOfMemory})!void { const inst_data = self.code.instructions.items(.data)[inst].fn_type; const extra = self.code.extraData(Inst.FnTypeCc, inst_data.payload_index); - const raw_param_types = self.code.extra[extra.end..][0..extra.data.param_types_len]; - const param_types = mem.bytesAsSlice(Inst.Ref, mem.sliceAsBytes(raw_param_types)); + const param_types = self.code.refSlice(extra.end, extra.data.param_types_len); const cc = extra.data.cc; return self.writeFnTypeCommon(stream, param_types, inst_data.return_type, var_args, cc); } @@ -1864,10 +1836,10 @@ const Writer = struct { fn writeInstRef(self: *Writer, stream: anytype, ref: Inst.Ref) !void { var i: usize = @enumToInt(ref); - if (i < Inst.Ref.typed_value_count) { + if (i < Inst.Ref.typed_value_map.len) { return stream.print("@{}", .{ref}); } - i -= Inst.Ref.typed_value_count; + i -= Inst.Ref.typed_value_map.len; if (i < self.param_count) { return stream.print("${d}", .{i});