diff options
| author | Matthew Lugg <mlugg@mlugg.co.uk> | 2024-08-27 06:10:56 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-08-27 06:10:56 +0100 |
| commit | d3c6f7179c7a6086ab9cdbaed231da9a1f0b4dee (patch) | |
| tree | a75bc8abc129a679fce0673165e04fa7283f2823 /src/codegen | |
| parent | d9147b91a601ad2442aaa43f4dba2d01b25d803d (diff) | |
| parent | 4c0f021c2e4270c7392df7250a5d8f2431dcc54f (diff) | |
| download | zig-d3c6f7179c7a6086ab9cdbaed231da9a1f0b4dee.tar.gz zig-d3c6f7179c7a6086ab9cdbaed231da9a1f0b4dee.zip | |
Merge pull request #21214 from mlugg/branch-hint-and-export
Implement `@branchHint` and new `@export` usage
Diffstat (limited to 'src/codegen')
| -rw-r--r-- | src/codegen/c.zig | 45 | ||||
| -rw-r--r-- | src/codegen/llvm.zig | 235 | ||||
| -rw-r--r-- | src/codegen/llvm/Builder.zig | 417 | ||||
| -rw-r--r-- | src/codegen/llvm/ir.zig | 227 | ||||
| -rw-r--r-- | src/codegen/spirv.zig | 50 |
5 files changed, 712 insertions, 262 deletions
diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 754286d80b..8703e9b124 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -626,7 +626,7 @@ pub const DeclGen = struct { } fn fail(dg: *DeclGen, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } { - @setCold(true); + @branchHint(.cold); const zcu = dg.pt.zcu; const src_loc = zcu.navSrcLoc(dg.pass.nav); dg.error_msg = try Zcu.ErrorMsg.create(dg.gpa, src_loc, format, args); @@ -1786,7 +1786,7 @@ pub const DeclGen = struct { else => unreachable, } } - if (fn_val.getFunction(zcu)) |func| if (func.analysisUnordered(ip).is_cold) + if (fn_val.getFunction(zcu)) |func| if (func.analysisUnordered(ip).branch_hint == .cold) try w.writeAll("zig_cold "); if (fn_info.return_type == .noreturn_type) try w.writeAll("zig_noreturn "); @@ -3290,8 +3290,10 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, .prefetch => try airPrefetch(f, inst), .addrspace_cast => return f.fail("TODO: C backend: implement addrspace_cast", .{}), - .@"try" => try airTry(f, inst), - .try_ptr => try airTryPtr(f, inst), + .@"try" => try airTry(f, inst), + .try_cold => try airTry(f, inst), + .try_ptr => try airTryPtr(f, inst), + .try_ptr_cold => try airTryPtr(f, inst), .dbg_stmt => try airDbgStmt(f, inst), .dbg_inline_block => try airDbgInlineBlock(f, inst), @@ -4988,11 +4990,10 @@ fn airCondBr(f: *Function, inst: Air.Inst.Index) !CValue { fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue { const pt = f.object.dg.pt; const zcu = pt.zcu; - const pl_op = f.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; - const condition = try f.resolveInst(pl_op.operand); - try reap(f, inst, &.{pl_op.operand}); - const condition_ty = f.typeOf(pl_op.operand); - const switch_br = f.air.extraData(Air.SwitchBr, pl_op.payload); + const switch_br = f.air.unwrapSwitch(inst); + const condition = try f.resolveInst(switch_br.operand); + try reap(f, inst, &.{switch_br.operand}); + const condition_ty = f.typeOf(switch_br.operand); const writer = f.object.writer(); try writer.writeAll("switch ("); @@ -5013,22 +5014,16 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue { f.object.indent_writer.pushIndent(); const gpa = f.object.dg.gpa; - const liveness = try f.liveness.getSwitchBr(gpa, inst, switch_br.data.cases_len + 1); + const liveness = try f.liveness.getSwitchBr(gpa, inst, switch_br.cases_len + 1); defer gpa.free(liveness.deaths); // On the final iteration we do not need to fix any state. This is because, like in the `else` // branch of a `cond_br`, our parent has to do it for this entire body anyway. - const last_case_i = switch_br.data.cases_len - @intFromBool(switch_br.data.else_body_len == 0); - - var extra_index: usize = switch_br.end; - for (0..switch_br.data.cases_len) |case_i| { - const case = f.air.extraData(Air.SwitchBr.Case, extra_index); - const items = @as([]const Air.Inst.Ref, @ptrCast(f.air.extra[case.end..][0..case.data.items_len])); - const case_body: []const Air.Inst.Index = - @ptrCast(f.air.extra[case.end + items.len ..][0..case.data.body_len]); - extra_index = case.end + case.data.items_len + case_body.len; + const last_case_i = switch_br.cases_len - @intFromBool(switch_br.else_body_len == 0); - for (items) |item| { + var it = switch_br.iterateCases(); + while (it.next()) |case| { + for (case.items) |item| { try f.object.indent_writer.insertNewline(); try writer.writeAll("case "); const item_value = try f.air.value(item, pt); @@ -5046,19 +5041,19 @@ fn airSwitchBr(f: *Function, inst: Air.Inst.Index) !CValue { } try writer.writeByte(' '); - if (case_i != last_case_i) { - try genBodyResolveState(f, inst, liveness.deaths[case_i], case_body, false); + if (case.idx != last_case_i) { + try genBodyResolveState(f, inst, liveness.deaths[case.idx], case.body, false); } else { - for (liveness.deaths[case_i]) |death| { + for (liveness.deaths[case.idx]) |death| { try die(f, inst, death.toRef()); } - try genBody(f, case_body); + try genBody(f, case.body); } // The case body must be noreturn so we don't need to insert a break. } - const else_body: []const Air.Inst.Index = @ptrCast(f.air.extra[extra_index..][0..switch_br.data.else_body_len]); + const else_body = it.elseBody(); try f.object.indent_writer.insertNewline(); if (else_body.len > 0) { // Note that this must be the last case (i.e. the `last_case_i` case was not hit above) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 1d8667ecb2..eec46b8379 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -898,9 +898,9 @@ pub const Object = struct { const i32_2 = try builder.intConst(.i32, 2); const i32_3 = try builder.intConst(.i32, 3); const debug_info_version = try builder.debugModuleFlag( - try builder.debugConstant(i32_2), + try builder.metadataConstant(i32_2), try builder.metadataString("Debug Info Version"), - try builder.debugConstant(i32_3), + try builder.metadataConstant(i32_3), ); switch (comp.config.debug_format) { @@ -908,9 +908,9 @@ pub const Object = struct { .dwarf => |f| { const i32_4 = try builder.intConst(.i32, 4); const dwarf_version = try builder.debugModuleFlag( - try builder.debugConstant(i32_2), + try builder.metadataConstant(i32_2), try builder.metadataString("Dwarf Version"), - try builder.debugConstant(i32_4), + try builder.metadataConstant(i32_4), ); switch (f) { .@"32" => { @@ -921,9 +921,9 @@ pub const Object = struct { }, .@"64" => { const dwarf64 = try builder.debugModuleFlag( - try builder.debugConstant(i32_2), + try builder.metadataConstant(i32_2), try builder.metadataString("DWARF64"), - try builder.debugConstant(.@"1"), + try builder.metadataConstant(.@"1"), ); try builder.debugNamed(try builder.metadataString("llvm.module.flags"), &.{ debug_info_version, @@ -935,9 +935,9 @@ pub const Object = struct { }, .code_view => { const code_view = try builder.debugModuleFlag( - try builder.debugConstant(i32_2), + try builder.metadataConstant(i32_2), try builder.metadataString("CodeView"), - try builder.debugConstant(.@"1"), + try builder.metadataConstant(.@"1"), ); try builder.debugNamed(try builder.metadataString("llvm.module.flags"), &.{ debug_info_version, @@ -1122,12 +1122,12 @@ pub const Object = struct { self.builder.debugForwardReferenceSetType( self.debug_enums_fwd_ref, - try self.builder.debugTuple(self.debug_enums.items), + try self.builder.metadataTuple(self.debug_enums.items), ); self.builder.debugForwardReferenceSetType( self.debug_globals_fwd_ref, - try self.builder.debugTuple(self.debug_globals.items), + try self.builder.metadataTuple(self.debug_globals.items), ); } } @@ -1369,7 +1369,7 @@ pub const Object = struct { _ = try attributes.removeFnAttr(.alignstack); } - if (func_analysis.is_cold) { + if (func_analysis.branch_hint == .cold) { try attributes.addFnAttr(.cold, &o.builder); } else { _ = try attributes.removeFnAttr(.cold); @@ -1978,7 +1978,7 @@ pub const Object = struct { try o.lowerDebugType(int_ty), ty.abiSize(zcu) * 8, (ty.abiAlignment(zcu).toByteUnits() orelse 0) * 8, - try o.builder.debugTuple(enumerators), + try o.builder.metadataTuple(enumerators), ); try o.debug_type_map.put(gpa, ty, debug_enum_type); @@ -2087,7 +2087,7 @@ pub const Object = struct { .none, // Underlying type ty.abiSize(zcu) * 8, (ty.abiAlignment(zcu).toByteUnits() orelse 0) * 8, - try o.builder.debugTuple(&.{ + try o.builder.metadataTuple(&.{ debug_ptr_type, debug_len_type, }), @@ -2167,10 +2167,10 @@ pub const Object = struct { try o.lowerDebugType(ty.childType(zcu)), ty.abiSize(zcu) * 8, (ty.abiAlignment(zcu).toByteUnits() orelse 0) * 8, - try o.builder.debugTuple(&.{ + try o.builder.metadataTuple(&.{ try o.builder.debugSubrange( - try o.builder.debugConstant(try o.builder.intConst(.i64, 0)), - try o.builder.debugConstant(try o.builder.intConst(.i64, ty.arrayLen(zcu))), + try o.builder.metadataConstant(try o.builder.intConst(.i64, 0)), + try o.builder.metadataConstant(try o.builder.intConst(.i64, ty.arrayLen(zcu))), ), }), ); @@ -2210,10 +2210,10 @@ pub const Object = struct { debug_elem_type, ty.abiSize(zcu) * 8, (ty.abiAlignment(zcu).toByteUnits() orelse 0) * 8, - try o.builder.debugTuple(&.{ + try o.builder.metadataTuple(&.{ try o.builder.debugSubrange( - try o.builder.debugConstant(try o.builder.intConst(.i64, 0)), - try o.builder.debugConstant(try o.builder.intConst(.i64, ty.vectorLen(zcu))), + try o.builder.metadataConstant(try o.builder.intConst(.i64, 0)), + try o.builder.metadataConstant(try o.builder.intConst(.i64, ty.vectorLen(zcu))), ), }), ); @@ -2288,7 +2288,7 @@ pub const Object = struct { .none, // Underlying type ty.abiSize(zcu) * 8, (ty.abiAlignment(zcu).toByteUnits() orelse 0) * 8, - try o.builder.debugTuple(&.{ + try o.builder.metadataTuple(&.{ debug_data_type, debug_some_type, }), @@ -2367,7 +2367,7 @@ pub const Object = struct { .none, // Underlying type ty.abiSize(zcu) * 8, (ty.abiAlignment(zcu).toByteUnits() orelse 0) * 8, - try o.builder.debugTuple(&fields), + try o.builder.metadataTuple(&fields), ); o.builder.debugForwardReferenceSetType(debug_fwd_ref, debug_error_union_type); @@ -2447,7 +2447,7 @@ pub const Object = struct { .none, // Underlying type ty.abiSize(zcu) * 8, (ty.abiAlignment(zcu).toByteUnits() orelse 0) * 8, - try o.builder.debugTuple(fields.items), + try o.builder.metadataTuple(fields.items), ); o.builder.debugForwardReferenceSetType(debug_fwd_ref, debug_struct_type); @@ -2520,7 +2520,7 @@ pub const Object = struct { .none, // Underlying type ty.abiSize(zcu) * 8, (ty.abiAlignment(zcu).toByteUnits() orelse 0) * 8, - try o.builder.debugTuple(fields.items), + try o.builder.metadataTuple(fields.items), ); o.builder.debugForwardReferenceSetType(debug_fwd_ref, debug_struct_type); @@ -2561,7 +2561,7 @@ pub const Object = struct { .none, // Underlying type ty.abiSize(zcu) * 8, (ty.abiAlignment(zcu).toByteUnits() orelse 0) * 8, - try o.builder.debugTuple( + try o.builder.metadataTuple( &.{try o.lowerDebugType(Type.fromInterned(union_type.enum_tag_ty))}, ), ); @@ -2623,7 +2623,7 @@ pub const Object = struct { .none, // Underlying type ty.abiSize(zcu) * 8, (ty.abiAlignment(zcu).toByteUnits() orelse 0) * 8, - try o.builder.debugTuple(fields.items), + try o.builder.metadataTuple(fields.items), ); o.builder.debugForwardReferenceSetType(debug_union_fwd_ref, debug_union_type); @@ -2682,7 +2682,7 @@ pub const Object = struct { .none, // Underlying type ty.abiSize(zcu) * 8, (ty.abiAlignment(zcu).toByteUnits() orelse 0) * 8, - try o.builder.debugTuple(&full_fields), + try o.builder.metadataTuple(&full_fields), ); o.builder.debugForwardReferenceSetType(debug_fwd_ref, debug_tagged_union_type); @@ -2735,7 +2735,7 @@ pub const Object = struct { } const debug_function_type = try o.builder.debugSubroutineType( - try o.builder.debugTuple(debug_param_types.items), + try o.builder.metadataTuple(debug_param_types.items), ); try o.debug_type_map.put(gpa, ty, debug_function_type); @@ -4571,7 +4571,7 @@ pub const Object = struct { const bad_value_block = try wip.block(1, "BadValue"); const tag_int_value = wip.arg(0); var wip_switch = - try wip.@"switch"(tag_int_value, bad_value_block, @intCast(enum_type.names.len)); + try wip.@"switch"(tag_int_value, bad_value_block, @intCast(enum_type.names.len), .none); defer wip_switch.finish(&wip); for (0..enum_type.names.len) |field_index| { @@ -4618,7 +4618,7 @@ pub const NavGen = struct { } fn todo(ng: *NavGen, comptime format: []const u8, args: anytype) Error { - @setCold(true); + @branchHint(.cold); assert(ng.err_msg == null); const o = ng.object; const gpa = o.gpa; @@ -4784,7 +4784,7 @@ pub const FuncGen = struct { } fn todo(self: *FuncGen, comptime format: []const u8, args: anytype) Error { - @setCold(true); + @branchHint(.cold); return self.ng.todo(format, args); } @@ -4958,8 +4958,10 @@ pub const FuncGen = struct { .ret_addr => try self.airRetAddr(inst), .frame_addr => try self.airFrameAddress(inst), .cond_br => try self.airCondBr(inst), - .@"try" => try self.airTry(body[i..]), - .try_ptr => try self.airTryPtr(inst), + .@"try" => try self.airTry(body[i..], false), + .try_cold => try self.airTry(body[i..], true), + .try_ptr => try self.airTryPtr(inst, false), + .try_ptr_cold => try self.airTryPtr(inst, true), .intcast => try self.airIntCast(inst), .trunc => try self.airTrunc(inst), .fptrunc => try self.airFptrunc(inst), @@ -5506,6 +5508,7 @@ pub const FuncGen = struct { const panic_nav = ip.getNav(panic_func.owner_nav); const fn_info = zcu.typeToFunc(Type.fromInterned(panic_nav.typeOf(ip))).?; const panic_global = try o.resolveLlvmFunction(panic_func.owner_nav); + _ = try fg.wip.callIntrinsicAssumeCold(); _ = try fg.wip.call( .normal, toLlvmCallConv(fn_info.cc, target), @@ -5794,7 +5797,7 @@ pub const FuncGen = struct { const mixed_block = try self.wip.block(1, "Mixed"); const both_pl_block = try self.wip.block(1, "BothNonNull"); const end_block = try self.wip.block(3, "End"); - var wip_switch = try self.wip.@"switch"(lhs_rhs_ored, mixed_block, 2); + var wip_switch = try self.wip.@"switch"(lhs_rhs_ored, mixed_block, 2, .none); defer wip_switch.finish(&self.wip); try wip_switch.addCase( try o.builder.intConst(llvm_i2, 0b00), @@ -5948,21 +5951,62 @@ pub const FuncGen = struct { const then_body: []const Air.Inst.Index = @ptrCast(self.air.extra[extra.end..][0..extra.data.then_body_len]); const else_body: []const Air.Inst.Index = @ptrCast(self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]); + const Hint = enum { + none, + unpredictable, + then_likely, + else_likely, + then_cold, + else_cold, + }; + const hint: Hint = switch (extra.data.branch_hints.true) { + .none => switch (extra.data.branch_hints.false) { + .none => .none, + .likely => .else_likely, + .unlikely => .then_likely, + .cold => .else_cold, + .unpredictable => .unpredictable, + }, + .likely => switch (extra.data.branch_hints.false) { + .none => .then_likely, + .likely => .unpredictable, + .unlikely => .then_likely, + .cold => .else_cold, + .unpredictable => .unpredictable, + }, + .unlikely => switch (extra.data.branch_hints.false) { + .none => .else_likely, + .likely => .else_likely, + .unlikely => .unpredictable, + .cold => .else_cold, + .unpredictable => .unpredictable, + }, + .cold => .then_cold, + .unpredictable => .unpredictable, + }; + const then_block = try self.wip.block(1, "Then"); const else_block = try self.wip.block(1, "Else"); - _ = try self.wip.brCond(cond, then_block, else_block); + _ = try self.wip.brCond(cond, then_block, else_block, switch (hint) { + .none, .then_cold, .else_cold => .none, + .unpredictable => .unpredictable, + .then_likely => .then_likely, + .else_likely => .else_likely, + }); self.wip.cursor = .{ .block = then_block }; + if (hint == .then_cold) _ = try self.wip.callIntrinsicAssumeCold(); try self.genBodyDebugScope(null, then_body); self.wip.cursor = .{ .block = else_block }; + if (hint == .else_cold) _ = try self.wip.callIntrinsicAssumeCold(); try self.genBodyDebugScope(null, else_body); // No need to reset the insert cursor since this instruction is noreturn. return .none; } - fn airTry(self: *FuncGen, body_tail: []const Air.Inst.Index) !Builder.Value { + fn airTry(self: *FuncGen, body_tail: []const Air.Inst.Index, err_cold: bool) !Builder.Value { const o = self.ng.object; const pt = o.pt; const zcu = pt.zcu; @@ -5975,10 +6019,10 @@ pub const FuncGen = struct { const payload_ty = self.typeOfIndex(inst); const can_elide_load = if (isByRef(payload_ty, zcu)) self.canElideLoad(body_tail) else false; const is_unused = self.liveness.isUnused(inst); - return lowerTry(self, err_union, body, err_union_ty, false, can_elide_load, is_unused); + return lowerTry(self, err_union, body, err_union_ty, false, can_elide_load, is_unused, err_cold); } - fn airTryPtr(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { + fn airTryPtr(self: *FuncGen, inst: Air.Inst.Index, err_cold: bool) !Builder.Value { const o = self.ng.object; const zcu = o.pt.zcu; const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; @@ -5987,7 +6031,7 @@ pub const FuncGen = struct { const body: []const Air.Inst.Index = @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len]); const err_union_ty = self.typeOf(extra.data.ptr).childType(zcu); const is_unused = self.liveness.isUnused(inst); - return lowerTry(self, err_union_ptr, body, err_union_ty, true, true, is_unused); + return lowerTry(self, err_union_ptr, body, err_union_ty, true, true, is_unused, err_cold); } fn lowerTry( @@ -5998,6 +6042,7 @@ pub const FuncGen = struct { operand_is_ptr: bool, can_elide_load: bool, is_unused: bool, + err_cold: bool, ) !Builder.Value { const o = fg.ng.object; const pt = o.pt; @@ -6036,9 +6081,10 @@ pub const FuncGen = struct { const return_block = try fg.wip.block(1, "TryRet"); const continue_block = try fg.wip.block(1, "TryCont"); - _ = try fg.wip.brCond(is_err, return_block, continue_block); + _ = try fg.wip.brCond(is_err, return_block, continue_block, if (err_cold) .none else .else_likely); fg.wip.cursor = .{ .block = return_block }; + if (err_cold) _ = try fg.wip.callIntrinsicAssumeCold(); try fg.genBodyDebugScope(null, body); fg.wip.cursor = .{ .block = continue_block }; @@ -6065,9 +6111,11 @@ pub const FuncGen = struct { fn airSwitchBr(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { const o = self.ng.object; - const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; - const cond = try self.resolveInst(pl_op.operand); - const switch_br = self.air.extraData(Air.SwitchBr, pl_op.payload); + + const switch_br = self.air.unwrapSwitch(inst); + + const cond = try self.resolveInst(switch_br.operand); + const else_block = try self.wip.block(1, "Default"); const llvm_usize = try o.lowerType(Type.usize); const cond_int = if (cond.typeOfWip(&self.wip).isPointer(&o.builder)) @@ -6075,34 +6123,70 @@ pub const FuncGen = struct { else cond; - var extra_index: usize = switch_br.end; - var case_i: u32 = 0; - var llvm_cases_len: u32 = 0; - while (case_i < switch_br.data.cases_len) : (case_i += 1) { - const case = self.air.extraData(Air.SwitchBr.Case, extra_index); - const items: []const Air.Inst.Ref = - @ptrCast(self.air.extra[case.end..][0..case.data.items_len]); - const case_body = self.air.extra[case.end + items.len ..][0..case.data.body_len]; - extra_index = case.end + case.data.items_len + case_body.len; + const llvm_cases_len = llvm_cases_len: { + var len: u32 = 0; + var it = switch_br.iterateCases(); + while (it.next()) |case| len += @intCast(case.items.len); + break :llvm_cases_len len; + }; + + const weights: Builder.Function.Instruction.BrCond.Weights = weights: { + // First pass. If any weights are `.unpredictable`, unpredictable. + // If all are `.none` or `.cold`, none. + var any_likely = false; + for (0..switch_br.cases_len) |case_idx| { + switch (switch_br.getHint(@intCast(case_idx))) { + .none, .cold => {}, + .likely, .unlikely => any_likely = true, + .unpredictable => break :weights .unpredictable, + } + } + switch (switch_br.getElseHint()) { + .none, .cold => {}, + .likely, .unlikely => any_likely = true, + .unpredictable => break :weights .unpredictable, + } + if (!any_likely) break :weights .none; - llvm_cases_len += @intCast(items.len); - } + var weights = try self.gpa.alloc(Builder.Metadata, llvm_cases_len + 1); + defer self.gpa.free(weights); - var wip_switch = try self.wip.@"switch"(cond_int, else_block, llvm_cases_len); - defer wip_switch.finish(&self.wip); + const else_weight: u32 = switch (switch_br.getElseHint()) { + .unpredictable => unreachable, + .none, .cold => 1000, + .likely => 2000, + .unlikely => 1, + }; + weights[0] = try o.builder.metadataConstant(try o.builder.intConst(.i32, else_weight)); + + var weight_idx: usize = 1; + var it = switch_br.iterateCases(); + while (it.next()) |case| { + const weight_val: u32 = switch (switch_br.getHint(case.idx)) { + .unpredictable => unreachable, + .none, .cold => 1000, + .likely => 2000, + .unlikely => 1, + }; + const weight_meta = try o.builder.metadataConstant(try o.builder.intConst(.i32, weight_val)); + @memset(weights[weight_idx..][0..case.items.len], weight_meta); + weight_idx += case.items.len; + } + + assert(weight_idx == weights.len); - extra_index = switch_br.end; - case_i = 0; - while (case_i < switch_br.data.cases_len) : (case_i += 1) { - const case = self.air.extraData(Air.SwitchBr.Case, extra_index); - const items: []const Air.Inst.Ref = - @ptrCast(self.air.extra[case.end..][0..case.data.items_len]); - const case_body: []const Air.Inst.Index = @ptrCast(self.air.extra[case.end + items.len ..][0..case.data.body_len]); - extra_index = case.end + case.data.items_len + case_body.len; + const branch_weights_str = try o.builder.metadataString("branch_weights"); + const tuple = try o.builder.strTuple(branch_weights_str, weights); + break :weights @enumFromInt(@intFromEnum(tuple)); + }; - const case_block = try self.wip.block(@intCast(items.len), "Case"); + var wip_switch = try self.wip.@"switch"(cond_int, else_block, llvm_cases_len, weights); + defer wip_switch.finish(&self.wip); - for (items) |item| { + var it = switch_br.iterateCases(); + while (it.next()) |case| { + const case_block = try self.wip.block(@intCast(case.items.len), "Case"); + for (case.items) |item| { const llvm_item = (try self.resolveInst(item)).toConst().?; const llvm_int_item = if (llvm_item.typeOf(&o.builder).isPointer(&o.builder)) try o.builder.castConst(.ptrtoint, llvm_item, llvm_usize) @@ -6110,13 +6194,14 @@ pub const FuncGen = struct { llvm_item; try wip_switch.addCase(llvm_int_item, case_block, &self.wip); } - self.wip.cursor = .{ .block = case_block }; - try self.genBodyDebugScope(null, case_body); + if (switch_br.getHint(case.idx) == .cold) _ = try self.wip.callIntrinsicAssumeCold(); + try self.genBodyDebugScope(null, case.body); } + const else_body = it.elseBody(); self.wip.cursor = .{ .block = else_block }; - const else_body: []const Air.Inst.Index = @ptrCast(self.air.extra[extra_index..][0..switch_br.data.else_body_len]); + if (switch_br.getElseHint() == .cold) _ = try self.wip.callIntrinsicAssumeCold(); if (else_body.len != 0) { try self.genBodyDebugScope(null, else_body); } else { @@ -7748,7 +7833,7 @@ pub const FuncGen = struct { const fail_block = try fg.wip.block(1, "OverflowFail"); const ok_block = try fg.wip.block(1, "OverflowOk"); - _ = try fg.wip.brCond(overflow_bit, fail_block, ok_block); + _ = try fg.wip.brCond(overflow_bit, fail_block, ok_block, .none); fg.wip.cursor = .{ .block = fail_block }; try fg.buildSimplePanic(.integer_overflow); @@ -9389,7 +9474,7 @@ pub const FuncGen = struct { self.wip.cursor = .{ .block = loop_block }; const it_ptr = try self.wip.phi(.ptr, ""); const end = try self.wip.icmp(.ne, it_ptr.toValue(), end_ptr, ""); - _ = try self.wip.brCond(end, body_block, end_block); + _ = try self.wip.brCond(end, body_block, end_block, .none); self.wip.cursor = .{ .block = body_block }; const elem_abi_align = elem_ty.abiAlignment(zcu); @@ -9427,7 +9512,7 @@ pub const FuncGen = struct { const cond = try self.cmp(.normal, .neq, Type.usize, len, usize_zero); const memset_block = try self.wip.block(1, "MemsetTrapSkip"); const end_block = try self.wip.block(2, "MemsetTrapEnd"); - _ = try self.wip.brCond(cond, memset_block, end_block); + _ = try self.wip.brCond(cond, memset_block, end_block, .none); self.wip.cursor = .{ .block = memset_block }; _ = try self.wip.callMemSet(dest_ptr, dest_ptr_align, fill_byte, len, access_kind); _ = try self.wip.br(end_block); @@ -9462,7 +9547,7 @@ pub const FuncGen = struct { const cond = try self.cmp(.normal, .neq, Type.usize, len, usize_zero); const memcpy_block = try self.wip.block(1, "MemcpyTrapSkip"); const end_block = try self.wip.block(2, "MemcpyTrapEnd"); - _ = try self.wip.brCond(cond, memcpy_block, end_block); + _ = try self.wip.brCond(cond, memcpy_block, end_block, .none); self.wip.cursor = .{ .block = memcpy_block }; _ = try self.wip.callMemCpy( dest_ptr, @@ -9632,7 +9717,7 @@ pub const FuncGen = struct { const valid_block = try self.wip.block(@intCast(names.len), "Valid"); const invalid_block = try self.wip.block(1, "Invalid"); const end_block = try self.wip.block(2, "End"); - var wip_switch = try self.wip.@"switch"(operand, invalid_block, @intCast(names.len)); + var wip_switch = try self.wip.@"switch"(operand, invalid_block, @intCast(names.len), .none); defer wip_switch.finish(&self.wip); for (0..names.len) |name_index| { @@ -9708,7 +9793,7 @@ pub const FuncGen = struct { const named_block = try wip.block(@intCast(enum_type.names.len), "Named"); const unnamed_block = try wip.block(1, "Unnamed"); const tag_int_value = wip.arg(0); - var wip_switch = try wip.@"switch"(tag_int_value, unnamed_block, @intCast(enum_type.names.len)); + var wip_switch = try wip.@"switch"(tag_int_value, unnamed_block, @intCast(enum_type.names.len), .none); defer wip_switch.finish(&wip); for (0..enum_type.names.len) |field_index| { @@ -9858,7 +9943,7 @@ pub const FuncGen = struct { const cond = try self.wip.icmp(.ult, i, llvm_vector_len, ""); const loop_then = try self.wip.block(1, "ReduceLoopThen"); - _ = try self.wip.brCond(cond, loop_then, loop_exit); + _ = try self.wip.brCond(cond, loop_then, loop_exit, .none); { self.wip.cursor = .{ .block = loop_then }; diff --git a/src/codegen/llvm/Builder.zig b/src/codegen/llvm/Builder.zig index 90da3bdd7a..9ada51acad 100644 --- a/src/codegen/llvm/Builder.zig +++ b/src/codegen/llvm/Builder.zig @@ -4817,12 +4817,22 @@ pub const Function = struct { cond: Value, then: Block.Index, @"else": Block.Index, + weights: Weights, + pub const Weights = enum(u32) { + // We can do this as metadata indices 0 and 1 are reserved. + none = 0, + unpredictable = 1, + /// These values should be converted to `Metadata` to be used + /// in a `prof` annotation providing branch weights. + _, + }; }; pub const Switch = struct { val: Value, default: Block.Index, cases_len: u32, + weights: BrCond.Weights, //case_vals: [cases_len]Constant, //case_blocks: [cases_len]Block.Index, }; @@ -4969,7 +4979,8 @@ pub const Function = struct { }; pub const Info = packed struct(u32) { call_conv: CallConv, - _: u22 = undefined, + has_op_bundle_cold: bool, + _: u21 = undefined, }; }; @@ -5036,6 +5047,7 @@ pub const Function = struct { FunctionAttributes, Type, Value, + Instruction.BrCond.Weights, => @enumFromInt(value), MemoryAccessInfo, Instruction.Alloca.Info, @@ -5201,6 +5213,7 @@ pub const WipFunction = struct { cond: Value, then: Block.Index, @"else": Block.Index, + weights: enum { none, unpredictable, then_likely, else_likely }, ) Allocator.Error!Instruction.Index { assert(cond.typeOfWip(self) == .i1); try self.ensureUnusedExtraCapacity(1, Instruction.BrCond, 0); @@ -5210,6 +5223,22 @@ pub const WipFunction = struct { .cond = cond, .then = then, .@"else" = @"else", + .weights = switch (weights) { + .none => .none, + .unpredictable => .unpredictable, + .then_likely, .else_likely => w: { + const branch_weights_str = try self.builder.metadataString("branch_weights"); + const unlikely_const = try self.builder.metadataConstant(try self.builder.intConst(.i32, 1)); + const likely_const = try self.builder.metadataConstant(try self.builder.intConst(.i32, 2000)); + const weight_vals: [2]Metadata = switch (weights) { + .none, .unpredictable => unreachable, + .then_likely => .{ likely_const, unlikely_const }, + .else_likely => .{ unlikely_const, likely_const }, + }; + const tuple = try self.builder.strTuple(branch_weights_str, &weight_vals); + break :w @enumFromInt(@intFromEnum(tuple)); + }, + }, }), }); then.ptr(self).branches += 1; @@ -5248,6 +5277,7 @@ pub const WipFunction = struct { val: Value, default: Block.Index, cases_len: u32, + weights: Instruction.BrCond.Weights, ) Allocator.Error!WipSwitch { try self.ensureUnusedExtraCapacity(1, Instruction.Switch, cases_len * 2); const instruction = try self.addInst(null, .{ @@ -5256,6 +5286,7 @@ pub const WipFunction = struct { .val = val, .default = default, .cases_len = cases_len, + .weights = weights, }), }); _ = self.extra.addManyAsSliceAssumeCapacity(cases_len * 2); @@ -5896,6 +5927,20 @@ pub const WipFunction = struct { args: []const Value, name: []const u8, ) Allocator.Error!Value { + return self.callInner(kind, call_conv, function_attributes, ty, callee, args, name, false); + } + + fn callInner( + self: *WipFunction, + kind: Instruction.Call.Kind, + call_conv: CallConv, + function_attributes: FunctionAttributes, + ty: Type, + callee: Value, + args: []const Value, + name: []const u8, + has_op_bundle_cold: bool, + ) Allocator.Error!Value { const ret_ty = ty.functionReturn(self.builder); assert(ty.isFunction(self.builder)); assert(callee.typeOfWip(self).isPointer(self.builder)); @@ -5918,7 +5963,10 @@ pub const WipFunction = struct { .tail_fast => .@"tail call fast", }, .data = self.addExtraAssumeCapacity(Instruction.Call{ - .info = .{ .call_conv = call_conv }, + .info = .{ + .call_conv = call_conv, + .has_op_bundle_cold = has_op_bundle_cold, + }, .attributes = function_attributes, .ty = ty, .callee = callee, @@ -5964,6 +6012,20 @@ pub const WipFunction = struct { ); } + pub fn callIntrinsicAssumeCold(self: *WipFunction) Allocator.Error!Value { + const intrinsic = try self.builder.getIntrinsic(.assume, &.{}); + return self.callInner( + .normal, + CallConv.default, + .none, + intrinsic.typeOf(self.builder), + intrinsic.toValue(self.builder), + &.{try self.builder.intValue(.i1, 1)}, + "", + true, + ); + } + pub fn callMemCpy( self: *WipFunction, dst: Value, @@ -6040,7 +6102,7 @@ pub const WipFunction = struct { break :blk metadata; }, - .constant => |constant| try self.builder.debugConstant(constant), + .constant => |constant| try self.builder.metadataConstant(constant), .metadata => |metadata| metadata, }; } @@ -6099,6 +6161,7 @@ pub const WipFunction = struct { FunctionAttributes, Type, Value, + Instruction.BrCond.Weights, => @intFromEnum(value), MemoryAccessInfo, Instruction.Alloca.Info, @@ -6380,6 +6443,7 @@ pub const WipFunction = struct { .cond = instructions.map(extra.cond), .then = extra.then, .@"else" = extra.@"else", + .weights = extra.weights, }); }, .call, @@ -6522,6 +6586,7 @@ pub const WipFunction = struct { .val = instructions.map(extra.data.val), .default = extra.data.default, .cases_len = extra.data.cases_len, + .weights = extra.data.weights, }); wip_extra.appendSlice(case_vals); wip_extra.appendSlice(case_blocks); @@ -6744,6 +6809,7 @@ pub const WipFunction = struct { FunctionAttributes, Type, Value, + Instruction.BrCond.Weights, => @intFromEnum(value), MemoryAccessInfo, Instruction.Alloca.Info, @@ -6792,6 +6858,7 @@ pub const WipFunction = struct { FunctionAttributes, Type, Value, + Instruction.BrCond.Weights, => @enumFromInt(value), MemoryAccessInfo, Instruction.Alloca.Info, @@ -7697,6 +7764,7 @@ pub const MetadataString = enum(u32) { pub const Metadata = enum(u32) { none = 0, + empty_tuple = 1, _, const first_forward_reference = 1 << 29; @@ -7734,6 +7802,7 @@ pub const Metadata = enum(u32) { enumerator_signed_negative, subrange, tuple, + str_tuple, module_flag, expression, local_var, @@ -7779,6 +7848,7 @@ pub const Metadata = enum(u32) { .enumerator_signed_negative, .subrange, .tuple, + .str_tuple, .module_flag, .local_var, .parameter, @@ -8043,6 +8113,13 @@ pub const Metadata = enum(u32) { // elements: [elements_len]Metadata }; + pub const StrTuple = struct { + str: MetadataString, + elements_len: u32, + + // elements: [elements_len]Metadata + }; + pub const ModuleFlag = struct { behavior: Metadata, name: MetadataString, @@ -8355,7 +8432,7 @@ pub const Metadata = enum(u32) { }; pub fn init(options: Options) Allocator.Error!Builder { - var self = Builder{ + var self: Builder = .{ .gpa = options.allocator, .strip = options.strip, @@ -8454,7 +8531,9 @@ pub fn init(options: Options) Allocator.Error!Builder { assert(try self.intConst(.i32, 0) == .@"0"); assert(try self.intConst(.i32, 1) == .@"1"); assert(try self.noneConst(.token) == .none); - if (!self.strip) assert(try self.debugNone() == .none); + + assert(try self.metadataNone() == .none); + assert(try self.metadataTuple(&.{}) == .empty_tuple); try self.metadata_string_indices.append(self.gpa, 0); assert(try self.metadataString("") == .none); @@ -9683,6 +9762,13 @@ pub fn printUnbuffered( extra.then.toInst(&function).fmt(function_index, self), extra.@"else".toInst(&function).fmt(function_index, self), }); + switch (extra.weights) { + .none => {}, + .unpredictable => try writer.writeAll(", !unpredictable !{}"), + _ => try writer.print("{}", .{ + try metadata_formatter.fmt(", !prof ", @as(Metadata, @enumFromInt(@intFromEnum(extra.weights)))), + }), + } }, .call, .@"call fast", @@ -9727,6 +9813,9 @@ pub fn printUnbuffered( }); } try writer.writeByte(')'); + if (extra.data.info.has_op_bundle_cold) { + try writer.writeAll(" [ \"cold\"() ]"); + } const call_function_attributes = extra.data.attributes.func(self); if (call_function_attributes != .none) try writer.print(" #{d}", .{ (try attribute_groups.getOrPutValue( @@ -9937,6 +10026,13 @@ pub fn printUnbuffered( }, ); try writer.writeAll(" ]"); + switch (extra.data.weights) { + .none => {}, + .unpredictable => try writer.writeAll(", !unpredictable !{}"), + _ => try writer.print("{}", .{ + try metadata_formatter.fmt(", !prof ", @as(Metadata, @enumFromInt(@intFromEnum(extra.data.weights)))), + }), + } }, .va_arg => |tag| { const extra = function.extraData(Function.Instruction.VaArg, instruction.data); @@ -10285,6 +10381,17 @@ pub fn printUnbuffered( }); try writer.writeAll("}\n"); }, + .str_tuple => { + var extra = self.metadataExtraDataTrail(Metadata.StrTuple, metadata_item.data); + const elements = extra.trail.next(extra.data.elements_len, Metadata, self); + try writer.print("!{{{[str]%}", .{ + .str = try metadata_formatter.fmt("", extra.data.str), + }); + for (elements) |element| try writer.print("{[element]%}", .{ + .element = try metadata_formatter.fmt("", element), + }); + try writer.writeAll("}\n"); + }, .module_flag => { const extra = self.metadataExtraData(Metadata.ModuleFlag, metadata_item.data); try writer.print("!{{{[behavior]%}{[name]%}{[constant]%}}}\n", .{ @@ -11797,9 +11904,9 @@ pub fn debugNamed(self: *Builder, name: MetadataString, operands: []const Metada self.debugNamedAssumeCapacity(name, operands); } -fn debugNone(self: *Builder) Allocator.Error!Metadata { +fn metadataNone(self: *Builder) Allocator.Error!Metadata { try self.ensureUnusedMetadataCapacity(1, NoExtra, 0); - return self.debugNoneAssumeCapacity(); + return self.metadataNoneAssumeCapacity(); } pub fn debugFile( @@ -12088,12 +12195,21 @@ pub fn debugExpression( return self.debugExpressionAssumeCapacity(elements); } -pub fn debugTuple( +pub fn metadataTuple( self: *Builder, elements: []const Metadata, ) Allocator.Error!Metadata { try self.ensureUnusedMetadataCapacity(1, Metadata.Tuple, elements.len); - return self.debugTupleAssumeCapacity(elements); + return self.metadataTupleAssumeCapacity(elements); +} + +pub fn strTuple( + self: *Builder, + str: MetadataString, + elements: []const Metadata, +) Allocator.Error!Metadata { + try self.ensureUnusedMetadataCapacity(1, Metadata.StrTuple, elements.len); + return self.strTupleAssumeCapacity(str, elements); } pub fn debugModuleFlag( @@ -12164,9 +12280,9 @@ pub fn debugGlobalVarExpression( return self.debugGlobalVarExpressionAssumeCapacity(variable, expression); } -pub fn debugConstant(self: *Builder, value: Constant) Allocator.Error!Metadata { +pub fn metadataConstant(self: *Builder, value: Constant) Allocator.Error!Metadata { try self.ensureUnusedMetadataCapacity(1, NoExtra, 0); - return self.debugConstantAssumeCapacity(value); + return self.metadataConstantAssumeCapacity(value); } pub fn debugForwardReferenceSetType(self: *Builder, fwd_ref: Metadata, ty: Metadata) void { @@ -12261,8 +12377,7 @@ fn debugNamedAssumeCapacity(self: *Builder, name: MetadataString, operands: []co }; } -pub fn debugNoneAssumeCapacity(self: *Builder) Metadata { - assert(!self.strip); +pub fn metadataNoneAssumeCapacity(self: *Builder) Metadata { return self.metadataSimpleAssumeCapacity(.none, .{}); } @@ -12738,11 +12853,10 @@ fn debugExpressionAssumeCapacity( return @enumFromInt(gop.index); } -fn debugTupleAssumeCapacity( +fn metadataTupleAssumeCapacity( self: *Builder, elements: []const Metadata, ) Metadata { - assert(!self.strip); const Key = struct { elements: []const Metadata, }; @@ -12785,6 +12899,55 @@ fn debugTupleAssumeCapacity( return @enumFromInt(gop.index); } +fn strTupleAssumeCapacity( + self: *Builder, + str: MetadataString, + elements: []const Metadata, +) Metadata { + const Key = struct { + str: MetadataString, + elements: []const Metadata, + }; + const Adapter = struct { + builder: *const Builder, + pub fn hash(_: @This(), key: Key) u32 { + var hasher = comptime std.hash.Wyhash.init(std.hash.uint32(@intFromEnum(Metadata.Tag.tuple))); + hasher.update(std.mem.sliceAsBytes(key.elements)); + return @truncate(hasher.final()); + } + + pub fn eql(ctx: @This(), lhs_key: Key, _: void, rhs_index: usize) bool { + if (.str_tuple != ctx.builder.metadata_items.items(.tag)[rhs_index]) return false; + const rhs_data = ctx.builder.metadata_items.items(.data)[rhs_index]; + var rhs_extra = ctx.builder.metadataExtraDataTrail(Metadata.StrTuple, rhs_data); + return rhs_extra.data.str == lhs_key.str and std.mem.eql( + Metadata, + lhs_key.elements, + rhs_extra.trail.next(rhs_extra.data.elements_len, Metadata, ctx.builder), + ); + } + }; + + const gop = self.metadata_map.getOrPutAssumeCapacityAdapted( + Key{ .str = str, .elements = elements }, + Adapter{ .builder = self }, + ); + + if (!gop.found_existing) { + gop.key_ptr.* = {}; + gop.value_ptr.* = {}; + self.metadata_items.appendAssumeCapacity(.{ + .tag = .str_tuple, + .data = self.addMetadataExtraAssumeCapacity(Metadata.StrTuple{ + .str = str, + .elements_len = @intCast(elements.len), + }), + }); + self.metadata_extra.appendSliceAssumeCapacity(@ptrCast(elements)); + } + return @enumFromInt(gop.index); +} + fn debugModuleFlagAssumeCapacity( self: *Builder, behavior: Metadata, @@ -12875,8 +13038,7 @@ fn debugGlobalVarExpressionAssumeCapacity( }); } -fn debugConstantAssumeCapacity(self: *Builder, constant: Constant) Metadata { - assert(!self.strip); +fn metadataConstantAssumeCapacity(self: *Builder, constant: Constant) Metadata { const Adapter = struct { builder: *const Builder, pub fn hash(_: @This(), key: Constant) u32 { @@ -13755,15 +13917,18 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co } // METADATA_KIND_BLOCK - if (!self.strip) { + { const MetadataKindBlock = ir.MetadataKindBlock; var metadata_kind_block = try module_block.enterSubBlock(MetadataKindBlock, true); - inline for (@typeInfo(ir.MetadataKind).Enum.fields) |field| { - try metadata_kind_block.writeAbbrev(MetadataKindBlock.Kind{ - .id = field.value, - .name = field.name, - }); + inline for (@typeInfo(ir.FixedMetadataKind).Enum.fields) |field| { + // don't include `dbg` in stripped functions + if (!(self.strip and std.mem.eql(u8, field.name, "dbg"))) { + try metadata_kind_block.writeAbbrev(MetadataKindBlock.Kind{ + .id = field.value, + .name = field.name, + }); + } } try metadata_kind_block.end(); @@ -13808,14 +13973,14 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co const metadata_adapter = MetadataAdapter.init(self, constant_adapter); // METADATA_BLOCK - if (!self.strip) { + { const MetadataBlock = ir.MetadataBlock; var metadata_block = try module_block.enterSubBlock(MetadataBlock, true); const MetadataBlockWriter = @TypeOf(metadata_block); // Emit all MetadataStrings - { + if (self.metadata_string_map.count() > 1) { const strings_offset, const strings_size = blk: { var strings_offset: u32 = 0; var strings_size: u32 = 0; @@ -14046,7 +14211,7 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co else -%val << 1 | 1); } - try metadata_block.writeUnabbrev(MetadataBlock.Enumerator.id, record.items); + try metadata_block.writeUnabbrev(@intFromEnum(MetadataBlock.Enumerator.id), record.items); continue; }; try metadata_block.writeAbbrevAdapted(MetadataBlock.Enumerator{ @@ -14085,6 +14250,22 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co .elements = elements, }, metadata_adapter); }, + .str_tuple => { + var extra = self.metadataExtraDataTrail(Metadata.StrTuple, data); + + const elements = extra.trail.next(extra.data.elements_len, Metadata, self); + + const all_elems = try self.gpa.alloc(Metadata, elements.len + 1); + defer self.gpa.free(all_elems); + all_elems[0] = @enumFromInt(metadata_adapter.getMetadataStringIndex(extra.data.str)); + for (elements, all_elems[1..]) |elem, *out_elem| { + out_elem.* = @enumFromInt(metadata_adapter.getMetadataIndex(elem)); + } + + try metadata_block.writeAbbrev(MetadataBlock.Node{ + .elements = all_elems, + }); + }, .module_flag => { const extra = self.metadataExtraData(Metadata.ModuleFlag, data); try metadata_block.writeAbbrev(MetadataBlock.Node{ @@ -14177,7 +14358,7 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co try metadata_block.writeAbbrev(MetadataBlock.GlobalDeclAttachment{ .value = @enumFromInt(constant_adapter.getConstantIndex(global.toConst())), - .kind = ir.MetadataKind.dbg, + .kind = .dbg, .metadata = @enumFromInt(metadata_adapter.getMetadataIndex(global_ptr.dbg) - 1), }); } @@ -14186,6 +14367,18 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co try metadata_block.end(); } + // OPERAND_BUNDLE_TAGS_BLOCK + { + const OperandBundleTags = ir.OperandBundleTags; + var operand_bundle_tags_block = try module_block.enterSubBlock(OperandBundleTags, true); + + try operand_bundle_tags_block.writeAbbrev(OperandBundleTags.OperandBundleTag{ + .tag = "cold", + }); + + try operand_bundle_tags_block.end(); + } + // Block info { const BlockInfo = ir.BlockInfo; @@ -14220,20 +14413,7 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co constant_adapter: ConstantAdapter, metadata_adapter: MetadataAdapter, func: *const Function, - instruction_index: u32 = 0, - - pub fn init( - const_adapter: ConstantAdapter, - meta_adapter: MetadataAdapter, - func: *const Function, - ) @This() { - return .{ - .constant_adapter = const_adapter, - .metadata_adapter = meta_adapter, - .func = func, - .instruction_index = 0, - }; - } + instruction_index: Function.Instruction.Index, pub fn get(adapter: @This(), value: anytype, comptime field_name: []const u8) @TypeOf(value) { _ = field_name; @@ -14254,7 +14434,6 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co .instruction => |instruction| instruction.valueIndex(adapter.func) + adapter.firstInstr(), .constant => |constant| adapter.constant_adapter.getConstantIndex(constant), .metadata => |metadata| { - assert(!adapter.func.strip); const real_metadata = metadata.unwrap(adapter.metadata_adapter.builder); if (@intFromEnum(real_metadata) < Metadata.first_local_metadata) return adapter.metadata_adapter.getMetadataIndex(real_metadata) - 1; @@ -14282,19 +14461,12 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co } pub fn offset(adapter: @This()) u32 { - return @as( - Function.Instruction.Index, - @enumFromInt(adapter.instruction_index), - ).valueIndex(adapter.func) + adapter.firstInstr(); + return adapter.instruction_index.valueIndex(adapter.func) + adapter.firstInstr(); } fn firstInstr(adapter: @This()) u32 { return adapter.constant_adapter.numConstants(); } - - pub fn next(adapter: *@This()) void { - adapter.instruction_index += 1; - } }; for (self.functions.items, 0..) |func, func_index| { @@ -14307,7 +14479,12 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co try function_block.writeAbbrev(FunctionBlock.DeclareBlocks{ .num_blocks = func.blocks.len }); - var adapter = FunctionAdapter.init(constant_adapter, metadata_adapter, &func); + var adapter: FunctionAdapter = .{ + .constant_adapter = constant_adapter, + .metadata_adapter = metadata_adapter, + .func = &func, + .instruction_index = @enumFromInt(0), + }; // Emit function level metadata block if (!func.strip and func.debug_values.len > 0) { @@ -14330,21 +14507,27 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co var has_location = false; var block_incoming_len: u32 = undefined; - for (0..func.instructions.len) |instr_index| { - const tag = tags[instr_index]; - + for (tags, datas, 0..) |tag, data, instr_index| { + adapter.instruction_index = @enumFromInt(instr_index); record.clearRetainingCapacity(); switch (tag) { - .block => block_incoming_len = datas[instr_index], - .arg => {}, + .arg => continue, + .block => { + block_incoming_len = data; + continue; + }, .@"unreachable" => try function_block.writeAbbrev(FunctionBlock.Unreachable{}), .call, .@"musttail call", .@"notail call", .@"tail call", => |kind| { - var extra = func.extraDataTrail(Function.Instruction.Call, datas[instr_index]); + var extra = func.extraDataTrail(Function.Instruction.Call, data); + + if (extra.data.info.has_op_bundle_cold) { + try function_block.writeAbbrev(FunctionBlock.ColdOperandBundle{}); + } const call_conv = extra.data.info.call_conv; const args = extra.trail.next(extra.data.args_len, Value, &func); @@ -14367,7 +14550,11 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co .@"notail call fast", .@"tail call fast", => |kind| { - var extra = func.extraDataTrail(Function.Instruction.Call, datas[instr_index]); + var extra = func.extraDataTrail(Function.Instruction.Call, data); + + if (extra.data.info.has_op_bundle_cold) { + try function_block.writeAbbrev(FunctionBlock.ColdOperandBundle{}); + } const call_conv = extra.data.info.call_conv; const args = extra.trail.next(extra.data.args_len, Value, &func); @@ -14405,7 +14592,7 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co .srem, .ashr, => |kind| { - const extra = func.extraData(Function.Instruction.Binary, datas[instr_index]); + const extra = func.extraData(Function.Instruction.Binary, data); try function_block.writeAbbrev(FunctionBlock.Binary{ .opcode = kind.toBinaryOpcode(), .lhs = adapter.getOffsetValueIndex(extra.lhs), @@ -14417,7 +14604,7 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co .@"lshr exact", .@"ashr exact", => |kind| { - const extra = func.extraData(Function.Instruction.Binary, datas[instr_index]); + const extra = func.extraData(Function.Instruction.Binary, data); try function_block.writeAbbrev(FunctionBlock.BinaryExact{ .opcode = kind.toBinaryOpcode(), .lhs = adapter.getOffsetValueIndex(extra.lhs), @@ -14437,7 +14624,7 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co .@"shl nuw", .@"shl nuw nsw", => |kind| { - const extra = func.extraData(Function.Instruction.Binary, datas[instr_index]); + const extra = func.extraData(Function.Instruction.Binary, data); try function_block.writeAbbrev(FunctionBlock.BinaryNoWrap{ .opcode = kind.toBinaryOpcode(), .lhs = adapter.getOffsetValueIndex(extra.lhs), @@ -14468,7 +14655,7 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co .@"frem fast", .@"fsub fast", => |kind| { - const extra = func.extraData(Function.Instruction.Binary, datas[instr_index]); + const extra = func.extraData(Function.Instruction.Binary, data); try function_block.writeAbbrev(FunctionBlock.BinaryFast{ .opcode = kind.toBinaryOpcode(), .lhs = adapter.getOffsetValueIndex(extra.lhs), @@ -14479,7 +14666,7 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co .alloca, .@"alloca inalloca", => |kind| { - const extra = func.extraData(Function.Instruction.Alloca, datas[instr_index]); + const extra = func.extraData(Function.Instruction.Alloca, data); const alignment = extra.info.alignment.toLlvm(); try function_block.writeAbbrev(FunctionBlock.Alloca{ .inst_type = extra.type, @@ -14508,7 +14695,7 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co .sext, .zext, => |kind| { - const extra = func.extraData(Function.Instruction.Cast, datas[instr_index]); + const extra = func.extraData(Function.Instruction.Cast, data); try function_block.writeAbbrev(FunctionBlock.Cast{ .val = adapter.getOffsetValueIndex(extra.val), .type_index = extra.type, @@ -14542,7 +14729,7 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co .@"icmp ule", .@"icmp ult", => |kind| { - const extra = func.extraData(Function.Instruction.Binary, datas[instr_index]); + const extra = func.extraData(Function.Instruction.Binary, data); try function_block.writeAbbrev(FunctionBlock.Cmp{ .lhs = adapter.getOffsetValueIndex(extra.lhs), .rhs = adapter.getOffsetValueIndex(extra.rhs), @@ -14566,7 +14753,7 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co .@"fcmp fast une", .@"fcmp fast uno", => |kind| { - const extra = func.extraData(Function.Instruction.Binary, datas[instr_index]); + const extra = func.extraData(Function.Instruction.Binary, data); try function_block.writeAbbrev(FunctionBlock.CmpFast{ .lhs = adapter.getOffsetValueIndex(extra.lhs), .rhs = adapter.getOffsetValueIndex(extra.rhs), @@ -14575,14 +14762,14 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co }); }, .fneg => try function_block.writeAbbrev(FunctionBlock.FNeg{ - .val = adapter.getOffsetValueIndex(@enumFromInt(datas[instr_index])), + .val = adapter.getOffsetValueIndex(@enumFromInt(data)), }), .@"fneg fast" => try function_block.writeAbbrev(FunctionBlock.FNegFast{ - .val = adapter.getOffsetValueIndex(@enumFromInt(datas[instr_index])), + .val = adapter.getOffsetValueIndex(@enumFromInt(data)), .fast_math = FastMath.fast, }), .extractvalue => { - var extra = func.extraDataTrail(Function.Instruction.ExtractValue, datas[instr_index]); + var extra = func.extraDataTrail(Function.Instruction.ExtractValue, data); const indices = extra.trail.next(extra.data.indices_len, u32, &func); try function_block.writeAbbrev(FunctionBlock.ExtractValue{ .val = adapter.getOffsetValueIndex(extra.data.val), @@ -14590,7 +14777,7 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co }); }, .insertvalue => { - var extra = func.extraDataTrail(Function.Instruction.InsertValue, datas[instr_index]); + var extra = func.extraDataTrail(Function.Instruction.InsertValue, data); const indices = extra.trail.next(extra.data.indices_len, u32, &func); try function_block.writeAbbrev(FunctionBlock.InsertValue{ .val = adapter.getOffsetValueIndex(extra.data.val), @@ -14599,14 +14786,14 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co }); }, .extractelement => { - const extra = func.extraData(Function.Instruction.ExtractElement, datas[instr_index]); + const extra = func.extraData(Function.Instruction.ExtractElement, data); try function_block.writeAbbrev(FunctionBlock.ExtractElement{ .val = adapter.getOffsetValueIndex(extra.val), .index = adapter.getOffsetValueIndex(extra.index), }); }, .insertelement => { - const extra = func.extraData(Function.Instruction.InsertElement, datas[instr_index]); + const extra = func.extraData(Function.Instruction.InsertElement, data); try function_block.writeAbbrev(FunctionBlock.InsertElement{ .val = adapter.getOffsetValueIndex(extra.val), .elem = adapter.getOffsetValueIndex(extra.elem), @@ -14614,7 +14801,7 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co }); }, .select => { - const extra = func.extraData(Function.Instruction.Select, datas[instr_index]); + const extra = func.extraData(Function.Instruction.Select, data); try function_block.writeAbbrev(FunctionBlock.Select{ .lhs = adapter.getOffsetValueIndex(extra.lhs), .rhs = adapter.getOffsetValueIndex(extra.rhs), @@ -14622,7 +14809,7 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co }); }, .@"select fast" => { - const extra = func.extraData(Function.Instruction.Select, datas[instr_index]); + const extra = func.extraData(Function.Instruction.Select, data); try function_block.writeAbbrev(FunctionBlock.SelectFast{ .lhs = adapter.getOffsetValueIndex(extra.lhs), .rhs = adapter.getOffsetValueIndex(extra.rhs), @@ -14631,7 +14818,7 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co }); }, .shufflevector => { - const extra = func.extraData(Function.Instruction.ShuffleVector, datas[instr_index]); + const extra = func.extraData(Function.Instruction.ShuffleVector, data); try function_block.writeAbbrev(FunctionBlock.ShuffleVector{ .lhs = adapter.getOffsetValueIndex(extra.lhs), .rhs = adapter.getOffsetValueIndex(extra.rhs), @@ -14641,7 +14828,7 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co .getelementptr, .@"getelementptr inbounds", => |kind| { - var extra = func.extraDataTrail(Function.Instruction.GetElementPtr, datas[instr_index]); + var extra = func.extraDataTrail(Function.Instruction.GetElementPtr, data); const indices = extra.trail.next(extra.data.indices_len, Value, &func); try function_block.writeAbbrevAdapted( FunctionBlock.GetElementPtr{ @@ -14654,7 +14841,7 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co ); }, .load => { - const extra = func.extraData(Function.Instruction.Load, datas[instr_index]); + const extra = func.extraData(Function.Instruction.Load, data); try function_block.writeAbbrev(FunctionBlock.Load{ .ptr = adapter.getOffsetValueIndex(extra.ptr), .ty = extra.type, @@ -14663,7 +14850,7 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co }); }, .@"load atomic" => { - const extra = func.extraData(Function.Instruction.Load, datas[instr_index]); + const extra = func.extraData(Function.Instruction.Load, data); try function_block.writeAbbrev(FunctionBlock.LoadAtomic{ .ptr = adapter.getOffsetValueIndex(extra.ptr), .ty = extra.type, @@ -14674,7 +14861,7 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co }); }, .store => { - const extra = func.extraData(Function.Instruction.Store, datas[instr_index]); + const extra = func.extraData(Function.Instruction.Store, data); try function_block.writeAbbrev(FunctionBlock.Store{ .ptr = adapter.getOffsetValueIndex(extra.ptr), .val = adapter.getOffsetValueIndex(extra.val), @@ -14683,7 +14870,7 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co }); }, .@"store atomic" => { - const extra = func.extraData(Function.Instruction.Store, datas[instr_index]); + const extra = func.extraData(Function.Instruction.Store, data); try function_block.writeAbbrev(FunctionBlock.StoreAtomic{ .ptr = adapter.getOffsetValueIndex(extra.ptr), .val = adapter.getOffsetValueIndex(extra.val), @@ -14695,11 +14882,11 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co }, .br => { try function_block.writeAbbrev(FunctionBlock.BrUnconditional{ - .block = datas[instr_index], + .block = data, }); }, .br_cond => { - const extra = func.extraData(Function.Instruction.BrCond, datas[instr_index]); + const extra = func.extraData(Function.Instruction.BrCond, data); try function_block.writeAbbrev(FunctionBlock.BrConditional{ .then_block = @intFromEnum(extra.then), .else_block = @intFromEnum(extra.@"else"), @@ -14707,7 +14894,7 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co }); }, .@"switch" => { - var extra = func.extraDataTrail(Function.Instruction.Switch, datas[instr_index]); + var extra = func.extraDataTrail(Function.Instruction.Switch, data); try record.ensureUnusedCapacity(self.gpa, 3 + extra.data.cases_len * 2); @@ -14730,7 +14917,7 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co try function_block.writeUnabbrev(12, record.items); }, .va_arg => { - const extra = func.extraData(Function.Instruction.VaArg, datas[instr_index]); + const extra = func.extraData(Function.Instruction.VaArg, data); try function_block.writeAbbrev(FunctionBlock.VaArg{ .list_type = extra.list.typeOf(@enumFromInt(func_index), self), .list = adapter.getOffsetValueIndex(extra.list), @@ -14740,7 +14927,7 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co .phi, .@"phi fast", => |kind| { - var extra = func.extraDataTrail(Function.Instruction.Phi, datas[instr_index]); + var extra = func.extraDataTrail(Function.Instruction.Phi, data); const vals = extra.trail.next(block_incoming_len, Value, &func); const blocks = extra.trail.next(block_incoming_len, Function.Block.Index, &func); @@ -14764,11 +14951,11 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co try function_block.writeUnabbrev(16, record.items); }, .ret => try function_block.writeAbbrev(FunctionBlock.Ret{ - .val = adapter.getOffsetValueIndex(@enumFromInt(datas[instr_index])), + .val = adapter.getOffsetValueIndex(@enumFromInt(data)), }), .@"ret void" => try function_block.writeAbbrev(FunctionBlock.RetVoid{}), .atomicrmw => { - const extra = func.extraData(Function.Instruction.AtomicRmw, datas[instr_index]); + const extra = func.extraData(Function.Instruction.AtomicRmw, data); try function_block.writeAbbrev(FunctionBlock.AtomicRmw{ .ptr = adapter.getOffsetValueIndex(extra.ptr), .val = adapter.getOffsetValueIndex(extra.val), @@ -14782,7 +14969,7 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co .cmpxchg, .@"cmpxchg weak", => |kind| { - const extra = func.extraData(Function.Instruction.CmpXchg, datas[instr_index]); + const extra = func.extraData(Function.Instruction.CmpXchg, data); try function_block.writeAbbrev(FunctionBlock.CmpXchg{ .ptr = adapter.getOffsetValueIndex(extra.ptr), @@ -14797,7 +14984,7 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co }); }, .fence => { - const info: MemoryAccessInfo = @bitCast(datas[instr_index]); + const info: MemoryAccessInfo = @bitCast(data); try function_block.writeAbbrev(FunctionBlock.Fence{ .ordering = info.success_ordering, .sync_scope = info.sync_scope, @@ -14806,7 +14993,7 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co } if (!func.strip) { - if (func.debug_locations.get(@enumFromInt(instr_index))) |debug_location| { + if (func.debug_locations.get(adapter.instruction_index)) |debug_location| { switch (debug_location) { .no_location => has_location = false, .location => |location| { @@ -14823,8 +15010,6 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co try function_block.writeAbbrev(FunctionBlock.DebugLocAgain{}); } } - - adapter.next(); } // VALUE_SYMTAB @@ -14850,18 +15035,48 @@ pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]co } // METADATA_ATTACHMENT_BLOCK - if (!func.strip) blk: { - const dbg = func.global.ptrConst(self).dbg; - - if (dbg == .none) break :blk; - + { const MetadataAttachmentBlock = ir.MetadataAttachmentBlock; var metadata_attach_block = try function_block.enterSubBlock(MetadataAttachmentBlock, false); - try metadata_attach_block.writeAbbrev(MetadataAttachmentBlock.AttachmentSingle{ - .kind = ir.MetadataKind.dbg, - .metadata = @enumFromInt(metadata_adapter.getMetadataIndex(dbg) - 1), - }); + dbg: { + if (func.strip) break :dbg; + const dbg = func.global.ptrConst(self).dbg; + if (dbg == .none) break :dbg; + try metadata_attach_block.writeAbbrev(MetadataAttachmentBlock.AttachmentGlobalSingle{ + .kind = .dbg, + .metadata = @enumFromInt(metadata_adapter.getMetadataIndex(dbg) - 1), + }); + } + + var instr_index: u32 = 0; + for (func.instructions.items(.tag), func.instructions.items(.data)) |instr_tag, data| switch (instr_tag) { + .arg, .block => {}, // not an actual instruction + else => { + instr_index += 1; + }, + .br_cond, .@"switch" => { + const weights = switch (instr_tag) { + .br_cond => func.extraData(Function.Instruction.BrCond, data).weights, + .@"switch" => func.extraData(Function.Instruction.Switch, data).weights, + else => unreachable, + }; + switch (weights) { + .none => {}, + .unpredictable => try metadata_attach_block.writeAbbrev(MetadataAttachmentBlock.AttachmentInstructionSingle{ + .inst = instr_index, + .kind = .unpredictable, + .metadata = @enumFromInt(metadata_adapter.getMetadataIndex(.empty_tuple) - 1), + }), + _ => try metadata_attach_block.writeAbbrev(MetadataAttachmentBlock.AttachmentInstructionSingle{ + .inst = instr_index, + .kind = .prof, + .metadata = @enumFromInt(metadata_adapter.getMetadataIndex(@enumFromInt(@intFromEnum(weights))) - 1), + }), + } + instr_index += 1; + }, + }; try metadata_attach_block.end(); } diff --git a/src/codegen/llvm/ir.zig b/src/codegen/llvm/ir.zig index 6a7c6c6857..4d7effdaaf 100644 --- a/src/codegen/llvm/ir.zig +++ b/src/codegen/llvm/ir.zig @@ -20,8 +20,142 @@ const ColumnAbbrev = AbbrevOp{ .vbr = 8 }; const BlockAbbrev = AbbrevOp{ .vbr = 6 }; -pub const MetadataKind = enum(u1) { +/// Unused tags are commented out so that they are omitted in the generated +/// bitcode, which scans over this enum using reflection. +pub const FixedMetadataKind = enum(u8) { dbg = 0, + //tbaa = 1, + prof = 2, + //fpmath = 3, + //range = 4, + //@"tbaa.struct" = 5, + //@"invariant.load" = 6, + //@"alias.scope" = 7, + //@"noalias" = 8, + //nontemporal = 9, + //@"llvm.mem.parallel_loop_access" = 10, + //nonnull = 11, + //dereferenceable = 12, + //dereferenceable_or_null = 13, + //@"make.implicit" = 14, + unpredictable = 15, + //@"invariant.group" = 16, + //@"align" = 17, + //@"llvm.loop" = 18, + //type = 19, + //section_prefix = 20, + //absolute_symbol = 21, + //associated = 22, + //callees = 23, + //irr_loop = 24, + //@"llvm.access.group" = 25, + //callback = 26, + //@"llvm.preserve.access.index" = 27, + //vcall_visibility = 28, + //noundef = 29, + //annotation = 30, + //nosanitize = 31, + //func_sanitize = 32, + //exclude = 33, + //memprof = 34, + //callsite = 35, + //kcfi_type = 36, + //pcsections = 37, + //DIAssignID = 38, + //@"coro.outside.frame" = 39, +}; + +pub const MetadataCode = enum(u8) { + /// MDSTRING: [values] + STRING_OLD = 1, + /// VALUE: [type num, value num] + VALUE = 2, + /// NODE: [n x md num] + NODE = 3, + /// STRING: [values] + NAME = 4, + /// DISTINCT_NODE: [n x md num] + DISTINCT_NODE = 5, + /// [n x [id, name]] + KIND = 6, + /// [distinct, line, col, scope, inlined-at?] + LOCATION = 7, + /// OLD_NODE: [n x (type num, value num)] + OLD_NODE = 8, + /// OLD_FN_NODE: [n x (type num, value num)] + OLD_FN_NODE = 9, + /// NAMED_NODE: [n x mdnodes] + NAMED_NODE = 10, + /// [m x [value, [n x [id, mdnode]]] + ATTACHMENT = 11, + /// [distinct, tag, vers, header, n x md num] + GENERIC_DEBUG = 12, + /// [distinct, count, lo] + SUBRANGE = 13, + /// [isUnsigned|distinct, value, name] + ENUMERATOR = 14, + /// [distinct, tag, name, size, align, enc] + BASIC_TYPE = 15, + /// [distinct, filename, directory, checksumkind, checksum] + FILE = 16, + /// [distinct, ...] + DERIVED_TYPE = 17, + /// [distinct, ...] + COMPOSITE_TYPE = 18, + /// [distinct, flags, types, cc] + SUBROUTINE_TYPE = 19, + /// [distinct, ...] + COMPILE_UNIT = 20, + /// [distinct, ...] + SUBPROGRAM = 21, + /// [distinct, scope, file, line, column] + LEXICAL_BLOCK = 22, + ///[distinct, scope, file, discriminator] + LEXICAL_BLOCK_FILE = 23, + /// [distinct, scope, file, name, line, exportSymbols] + NAMESPACE = 24, + /// [distinct, scope, name, type, ...] + TEMPLATE_TYPE = 25, + /// [distinct, scope, name, type, value, ...] + TEMPLATE_VALUE = 26, + /// [distinct, ...] + GLOBAL_VAR = 27, + /// [distinct, ...] + LOCAL_VAR = 28, + /// [distinct, n x element] + EXPRESSION = 29, + /// [distinct, name, file, line, ...] + OBJC_PROPERTY = 30, + /// [distinct, tag, scope, entity, line, name] + IMPORTED_ENTITY = 31, + /// [distinct, scope, name, ...] + MODULE = 32, + /// [distinct, macinfo, line, name, value] + MACRO = 33, + /// [distinct, macinfo, line, file, ...] + MACRO_FILE = 34, + /// [count, offset] blob([lengths][chars]) + STRINGS = 35, + /// [valueid, n x [id, mdnode]] + GLOBAL_DECL_ATTACHMENT = 36, + /// [distinct, var, expr] + GLOBAL_VAR_EXPR = 37, + /// [offset] + INDEX_OFFSET = 38, + /// [bitpos] + INDEX = 39, + /// [distinct, scope, name, file, line] + LABEL = 40, + /// [distinct, name, size, align,...] + STRING_TYPE = 41, + /// [distinct, scope, name, variable,...] + COMMON_BLOCK = 44, + /// [distinct, count, lo, up, stride] + GENERIC_SUBRANGE = 45, + /// [n x [type num, value num]] + ARG_LIST = 46, + /// [distinct, ...] + ASSIGN_ID = 47, }; pub const Identification = struct { @@ -622,16 +756,29 @@ pub const MetadataAttachmentBlock = struct { pub const id = 16; pub const abbrevs = [_]type{ - AttachmentSingle, + AttachmentGlobalSingle, + AttachmentInstructionSingle, }; - pub const AttachmentSingle = struct { + pub const AttachmentGlobalSingle = struct { pub const ops = [_]AbbrevOp{ - .{ .literal = 11 }, + .{ .literal = @intFromEnum(MetadataCode.ATTACHMENT) }, .{ .fixed = 1 }, MetadataAbbrev, }; - kind: MetadataKind, + kind: FixedMetadataKind, + metadata: Builder.Metadata, + }; + + pub const AttachmentInstructionSingle = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = @intFromEnum(MetadataCode.ATTACHMENT) }, + ValueAbbrev, + .{ .fixed = 5 }, + MetadataAbbrev, + }; + inst: u32, + kind: FixedMetadataKind, metadata: Builder.Metadata, }; }; @@ -666,7 +813,7 @@ pub const MetadataBlock = struct { pub const Strings = struct { pub const ops = [_]AbbrevOp{ - .{ .literal = 35 }, + .{ .literal = @intFromEnum(MetadataCode.STRINGS) }, .{ .vbr = 6 }, .{ .vbr = 6 }, .blob, @@ -678,7 +825,7 @@ pub const MetadataBlock = struct { pub const File = struct { pub const ops = [_]AbbrevOp{ - .{ .literal = 16 }, + .{ .literal = @intFromEnum(MetadataCode.FILE) }, .{ .literal = 0 }, // is distinct MetadataAbbrev, // filename MetadataAbbrev, // directory @@ -692,7 +839,7 @@ pub const MetadataBlock = struct { pub const CompileUnit = struct { pub const ops = [_]AbbrevOp{ - .{ .literal = 20 }, + .{ .literal = @intFromEnum(MetadataCode.COMPILE_UNIT) }, .{ .literal = 1 }, // is distinct .{ .literal = std.dwarf.LANG.C99 }, // source language MetadataAbbrev, // file @@ -726,7 +873,7 @@ pub const MetadataBlock = struct { pub const Subprogram = struct { pub const ops = [_]AbbrevOp{ - .{ .literal = 21 }, + .{ .literal = @intFromEnum(MetadataCode.SUBPROGRAM) }, .{ .literal = 0b111 }, // is distinct | has sp flags | has flags MetadataAbbrev, // scope MetadataAbbrev, // name @@ -763,7 +910,7 @@ pub const MetadataBlock = struct { pub const LexicalBlock = struct { pub const ops = [_]AbbrevOp{ - .{ .literal = 22 }, + .{ .literal = @intFromEnum(MetadataCode.LEXICAL_BLOCK) }, .{ .literal = 0 }, // is distinct MetadataAbbrev, // scope MetadataAbbrev, // file @@ -779,7 +926,7 @@ pub const MetadataBlock = struct { pub const Location = struct { pub const ops = [_]AbbrevOp{ - .{ .literal = 7 }, + .{ .literal = @intFromEnum(MetadataCode.LOCATION) }, .{ .literal = 0 }, // is distinct LineAbbrev, // line ColumnAbbrev, // column @@ -796,7 +943,7 @@ pub const MetadataBlock = struct { pub const BasicType = struct { pub const ops = [_]AbbrevOp{ - .{ .literal = 15 }, + .{ .literal = @intFromEnum(MetadataCode.BASIC_TYPE) }, .{ .literal = 0 }, // is distinct .{ .literal = std.dwarf.TAG.base_type }, // tag MetadataAbbrev, // name @@ -813,7 +960,7 @@ pub const MetadataBlock = struct { pub const CompositeType = struct { pub const ops = [_]AbbrevOp{ - .{ .literal = 18 }, + .{ .literal = @intFromEnum(MetadataCode.COMPOSITE_TYPE) }, .{ .literal = 0 | 0x2 }, // is distinct | is not used in old type ref .{ .fixed = 32 }, // tag MetadataAbbrev, // name @@ -852,7 +999,7 @@ pub const MetadataBlock = struct { pub const DerivedType = struct { pub const ops = [_]AbbrevOp{ - .{ .literal = 17 }, + .{ .literal = @intFromEnum(MetadataCode.DERIVED_TYPE) }, .{ .literal = 0 }, // is distinct .{ .fixed = 32 }, // tag MetadataAbbrev, // name @@ -880,7 +1027,7 @@ pub const MetadataBlock = struct { pub const SubroutineType = struct { pub const ops = [_]AbbrevOp{ - .{ .literal = 19 }, + .{ .literal = @intFromEnum(MetadataCode.SUBROUTINE_TYPE) }, .{ .literal = 0 | 0x2 }, // is distinct | has no old type refs .{ .literal = 0 }, // flags MetadataAbbrev, // types @@ -891,7 +1038,7 @@ pub const MetadataBlock = struct { }; pub const Enumerator = struct { - pub const id = 14; + pub const id: MetadataCode = .ENUMERATOR; pub const Flags = packed struct(u3) { distinct: bool = false, @@ -900,7 +1047,7 @@ pub const MetadataBlock = struct { }; pub const ops = [_]AbbrevOp{ - .{ .literal = Enumerator.id }, + .{ .literal = @intFromEnum(Enumerator.id) }, .{ .fixed = @bitSizeOf(Flags) }, // flags .{ .vbr = 6 }, // bit width MetadataAbbrev, // name @@ -915,7 +1062,7 @@ pub const MetadataBlock = struct { pub const Subrange = struct { pub const ops = [_]AbbrevOp{ - .{ .literal = 13 }, + .{ .literal = @intFromEnum(MetadataCode.SUBRANGE) }, .{ .literal = 0b10 }, // is distinct | version MetadataAbbrev, // count MetadataAbbrev, // lower bound @@ -929,7 +1076,7 @@ pub const MetadataBlock = struct { pub const Expression = struct { pub const ops = [_]AbbrevOp{ - .{ .literal = 29 }, + .{ .literal = @intFromEnum(MetadataCode.EXPRESSION) }, .{ .literal = 0 | (3 << 1) }, // is distinct | version MetadataArrayAbbrev, // elements }; @@ -939,7 +1086,7 @@ pub const MetadataBlock = struct { pub const Node = struct { pub const ops = [_]AbbrevOp{ - .{ .literal = 3 }, + .{ .literal = @intFromEnum(MetadataCode.NODE) }, MetadataArrayAbbrev, // elements }; @@ -948,7 +1095,7 @@ pub const MetadataBlock = struct { pub const LocalVar = struct { pub const ops = [_]AbbrevOp{ - .{ .literal = 28 }, + .{ .literal = @intFromEnum(MetadataCode.LOCAL_VAR) }, .{ .literal = 0b10 }, // is distinct | has alignment MetadataAbbrev, // scope MetadataAbbrev, // name @@ -970,7 +1117,7 @@ pub const MetadataBlock = struct { pub const Parameter = struct { pub const ops = [_]AbbrevOp{ - .{ .literal = 28 }, + .{ .literal = @intFromEnum(MetadataCode.LOCAL_VAR) }, .{ .literal = 0b10 }, // is distinct | has alignment MetadataAbbrev, // scope MetadataAbbrev, // name @@ -993,7 +1140,7 @@ pub const MetadataBlock = struct { pub const GlobalVar = struct { pub const ops = [_]AbbrevOp{ - .{ .literal = 27 }, + .{ .literal = @intFromEnum(MetadataCode.GLOBAL_VAR) }, .{ .literal = 0b101 }, // is distinct | version MetadataAbbrev, // scope MetadataAbbrev, // name @@ -1020,7 +1167,7 @@ pub const MetadataBlock = struct { pub const GlobalVarExpression = struct { pub const ops = [_]AbbrevOp{ - .{ .literal = 37 }, + .{ .literal = @intFromEnum(MetadataCode.GLOBAL_VAR_EXPR) }, .{ .literal = 0 }, // is distinct MetadataAbbrev, // variable MetadataAbbrev, // expression @@ -1032,7 +1179,7 @@ pub const MetadataBlock = struct { pub const Constant = struct { pub const ops = [_]AbbrevOp{ - .{ .literal = 2 }, + .{ .literal = @intFromEnum(MetadataCode.VALUE) }, MetadataAbbrev, // type MetadataAbbrev, // value }; @@ -1043,7 +1190,7 @@ pub const MetadataBlock = struct { pub const Name = struct { pub const ops = [_]AbbrevOp{ - .{ .literal = 4 }, + .{ .literal = @intFromEnum(MetadataCode.NAME) }, .{ .array_fixed = 8 }, // name }; @@ -1052,7 +1199,7 @@ pub const MetadataBlock = struct { pub const NamedNode = struct { pub const ops = [_]AbbrevOp{ - .{ .literal = 10 }, + .{ .literal = @intFromEnum(MetadataCode.NAMED_NODE) }, MetadataArrayAbbrev, // elements }; @@ -1061,18 +1208,32 @@ pub const MetadataBlock = struct { pub const GlobalDeclAttachment = struct { pub const ops = [_]AbbrevOp{ - .{ .literal = 36 }, + .{ .literal = @intFromEnum(MetadataCode.GLOBAL_DECL_ATTACHMENT) }, ValueAbbrev, // value id .{ .fixed = 1 }, // kind MetadataAbbrev, // elements }; value: Builder.Constant, - kind: MetadataKind, + kind: FixedMetadataKind, metadata: Builder.Metadata, }; }; +pub const OperandBundleTags = struct { + pub const id = 21; + + pub const abbrevs = [_]type{OperandBundleTag}; + + pub const OperandBundleTag = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 1 }, + .array_char6, + }; + tag: []const u8, + }; +}; + pub const FunctionMetadataBlock = struct { pub const id = 15; @@ -1132,6 +1293,7 @@ pub const FunctionBlock = struct { Fence, DebugLoc, DebugLocAgain, + ColdOperandBundle, }; pub const DeclareBlocks = struct { @@ -1644,6 +1806,13 @@ pub const FunctionBlock = struct { .{ .literal = 33 }, }; }; + + pub const ColdOperandBundle = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 55 }, + .{ .literal = 0 }, + }; + }; }; pub const FunctionValueSymbolTable = struct { diff --git a/src/codegen/spirv.zig b/src/codegen/spirv.zig index adab565508..d1df1ba77e 100644 --- a/src/codegen/spirv.zig +++ b/src/codegen/spirv.zig @@ -410,7 +410,7 @@ const NavGen = struct { } pub fn fail(self: *NavGen, comptime format: []const u8, args: anytype) Error { - @setCold(true); + @branchHint(.cold); const zcu = self.pt.zcu; const src_loc = zcu.navSrcLoc(self.owner_nav); assert(self.error_msg == null); @@ -6173,11 +6173,10 @@ const NavGen = struct { const pt = self.pt; const zcu = pt.zcu; const target = self.getTarget(); - const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; - const cond_ty = self.typeOf(pl_op.operand); - const cond = try self.resolve(pl_op.operand); + const switch_br = self.air.unwrapSwitch(inst); + const cond_ty = self.typeOf(switch_br.operand); + const cond = try self.resolve(switch_br.operand); var cond_indirect = try self.convertToIndirect(cond_ty, cond); - const switch_br = self.air.extraData(Air.SwitchBr, pl_op.payload); const cond_words: u32 = switch (cond_ty.zigTypeTag(zcu)) { .Bool, .ErrorSet => 1, @@ -6204,18 +6203,15 @@ const NavGen = struct { else => return self.todo("implement switch for type {s}", .{@tagName(cond_ty.zigTypeTag(zcu))}), }; - const num_cases = switch_br.data.cases_len; + const num_cases = switch_br.cases_len; // Compute the total number of arms that we need. // Zig switches are grouped by condition, so we need to loop through all of them const num_conditions = blk: { - var extra_index: usize = switch_br.end; var num_conditions: u32 = 0; - for (0..num_cases) |_| { - const case = self.air.extraData(Air.SwitchBr.Case, extra_index); - const case_body = self.air.extra[case.end + case.data.items_len ..][0..case.data.body_len]; - extra_index = case.end + case.data.items_len + case_body.len; - num_conditions += case.data.items_len; + var it = switch_br.iterateCases(); + while (it.next()) |case| { + num_conditions += @intCast(case.items.len); } break :blk num_conditions; }; @@ -6244,17 +6240,12 @@ const NavGen = struct { // Emit each of the cases { - var extra_index: usize = switch_br.end; - for (0..num_cases) |case_i| { + var it = switch_br.iterateCases(); + while (it.next()) |case| { // SPIR-V needs a literal here, which' width depends on the case condition. - const case = self.air.extraData(Air.SwitchBr.Case, extra_index); - const items: []const Air.Inst.Ref = @ptrCast(self.air.extra[case.end..][0..case.data.items_len]); - const case_body = self.air.extra[case.end + items.len ..][0..case.data.body_len]; - extra_index = case.end + case.data.items_len + case_body.len; - - const label = case_labels.at(case_i); + const label = case_labels.at(case.idx); - for (items) |item| { + for (case.items) |item| { const value = (try self.air.value(item, pt)) orelse unreachable; const int_val: u64 = switch (cond_ty.zigTypeTag(zcu)) { .Bool, .Int => if (cond_ty.isSignedInt(zcu)) @bitCast(value.toSignedInt(zcu)) else value.toUnsignedInt(zcu), @@ -6285,20 +6276,15 @@ const NavGen = struct { } // Now, finally, we can start emitting each of the cases. - var extra_index: usize = switch_br.end; - for (0..num_cases) |case_i| { - const case = self.air.extraData(Air.SwitchBr.Case, extra_index); - const items: []const Air.Inst.Ref = @ptrCast(self.air.extra[case.end..][0..case.data.items_len]); - const case_body: []const Air.Inst.Index = @ptrCast(self.air.extra[case.end + items.len ..][0..case.data.body_len]); - extra_index = case.end + case.data.items_len + case_body.len; - - const label = case_labels.at(case_i); + var it = switch_br.iterateCases(); + while (it.next()) |case| { + const label = case_labels.at(case.idx); try self.beginSpvBlock(label); switch (self.control_flow) { .structured => { - const next_block = try self.genStructuredBody(.selection, case_body); + const next_block = try self.genStructuredBody(.selection, case.body); incoming_structured_blocks.appendAssumeCapacity(.{ .src_label = self.current_block_label, .next_block = next_block, @@ -6306,12 +6292,12 @@ const NavGen = struct { try self.func.body.emitBranch(self.spv.gpa, merge_label.?); }, .unstructured => { - try self.genBody(case_body); + try self.genBody(case.body); }, } } - const else_body: []const Air.Inst.Index = @ptrCast(self.air.extra[extra_index..][0..switch_br.data.else_body_len]); + const else_body = it.elseBody(); try self.beginSpvBlock(default); if (else_body.len != 0) { switch (self.control_flow) { |
