diff options
Diffstat (limited to 'src/codegen')
| -rw-r--r-- | src/codegen/c.zig | 245 | ||||
| -rw-r--r-- | src/codegen/llvm.zig | 2513 | ||||
| -rw-r--r-- | src/codegen/llvm/Builder.zig | 7209 | ||||
| -rw-r--r-- | src/codegen/llvm/bindings.zig | 1465 | ||||
| -rw-r--r-- | src/codegen/llvm/bitcode_writer.zig | 426 | ||||
| -rw-r--r-- | src/codegen/llvm/ir.zig | 1643 | ||||
| -rw-r--r-- | src/codegen/spirv.zig | 636 | ||||
| -rw-r--r-- | src/codegen/spirv/Module.zig | 83 |
8 files changed, 9162 insertions, 5058 deletions
diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 4adabea7bb..0977acf7fe 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -900,9 +900,9 @@ pub const DeclGen = struct { try writer.writeByte('{'); var empty = true; - for (0..ty.structFieldCount(mod)) |field_i| { - if (ty.structFieldIsComptime(field_i, mod)) continue; - const field_ty = ty.structFieldType(field_i, mod); + for (0..ty.structFieldCount(mod)) |field_index| { + if (ty.structFieldIsComptime(field_index, mod)) continue; + const field_ty = ty.structFieldType(field_index, mod); if (!field_ty.hasRuntimeBits(mod)) continue; if (!empty) try writer.writeByte(','); @@ -934,9 +934,10 @@ pub const DeclGen = struct { try writer.writeAll(" .payload = {"); } const union_obj = mod.typeToUnion(ty).?; - for (union_obj.field_types.get(ip)) |field_ty| { - if (!Type.fromInterned(field_ty).hasRuntimeBits(mod)) continue; - try dg.renderValue(writer, Type.fromInterned(field_ty), val, initializer_type); + for (0..union_obj.field_types.len) |field_index| { + const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_index]); + if (!field_ty.hasRuntimeBits(mod)) continue; + try dg.renderValue(writer, field_ty, val, initializer_type); break; } if (ty.unionTagTypeSafety(mod)) |_| try writer.writeByte('}'); @@ -1354,25 +1355,23 @@ pub const DeclGen = struct { try writer.writeByte('{'); var empty = true; - for ( - tuple.types.get(ip), - tuple.values.get(ip), - 0.., - ) |field_ty, comptime_ty, field_i| { - if (comptime_ty != .none) continue; - if (!Type.fromInterned(field_ty).hasRuntimeBitsIgnoreComptime(mod)) continue; + for (0..tuple.types.len) |field_index| { + const comptime_val = tuple.values.get(ip)[field_index]; + if (comptime_val != .none) continue; + const field_ty = Type.fromInterned(tuple.types.get(ip)[field_index]); + if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) continue; if (!empty) try writer.writeByte(','); - const field_val = switch (ip.indexToKey(val.ip_index).aggregate.storage) { + const field_val = Value.fromInterned(switch (ip.indexToKey(val.ip_index).aggregate.storage) { .bytes => |bytes| try ip.get(mod.gpa, .{ .int = .{ - .ty = field_ty, - .storage = .{ .u64 = bytes[field_i] }, + .ty = field_ty.toIntern(), + .storage = .{ .u64 = bytes[field_index] }, } }), - .elems => |elems| elems[field_i], + .elems => |elems| elems[field_index], .repeated_elem => |elem| elem, - }; - try dg.renderValue(writer, Type.fromInterned(field_ty), Value.fromInterned(field_val), initializer_type); + }); + try dg.renderValue(writer, field_ty, field_val, initializer_type); empty = false; } @@ -1388,18 +1387,18 @@ pub const DeclGen = struct { try writer.writeByte('{'); var empty = true; - for (0..struct_type.field_types.len) |field_i| { - const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[field_i]); - if (struct_type.fieldIsComptime(ip, field_i)) continue; + for (0..struct_type.field_types.len) |field_index| { + const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[field_index]); + if (struct_type.fieldIsComptime(ip, field_index)) continue; if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) continue; if (!empty) try writer.writeByte(','); const field_val = switch (ip.indexToKey(val.ip_index).aggregate.storage) { .bytes => |bytes| try ip.get(mod.gpa, .{ .int = .{ .ty = field_ty.toIntern(), - .storage = .{ .u64 = bytes[field_i] }, + .storage = .{ .u64 = bytes[field_index] }, } }), - .elems => |elems| elems[field_i], + .elems => |elems| elems[field_index], .repeated_elem => |elem| elem, }; try dg.renderValue(writer, field_ty, Value.fromInterned(field_val), initializer_type); @@ -1413,13 +1412,13 @@ pub const DeclGen = struct { const bits = Type.smallestUnsignedBits(int_info.bits - 1); const bit_offset_ty = try mod.intType(.unsigned, bits); - const field_types = struct_type.field_types.get(ip); var bit_offset: u64 = 0; var eff_num_fields: usize = 0; - for (field_types) |field_ty| { - if (!Type.fromInterned(field_ty).hasRuntimeBitsIgnoreComptime(mod)) continue; + for (0..struct_type.field_types.len) |field_index| { + const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[field_index]); + if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) continue; eff_num_fields += 1; } @@ -1438,15 +1437,16 @@ pub const DeclGen = struct { var eff_index: usize = 0; var needs_closing_paren = false; - for (field_types, 0..) |field_ty, field_i| { - if (!Type.fromInterned(field_ty).hasRuntimeBitsIgnoreComptime(mod)) continue; + for (0..struct_type.field_types.len) |field_index| { + const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[field_index]); + if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) continue; const field_val = switch (ip.indexToKey(val.ip_index).aggregate.storage) { .bytes => |bytes| try ip.get(mod.gpa, .{ .int = .{ - .ty = field_ty, - .storage = .{ .u64 = bytes[field_i] }, + .ty = field_ty.toIntern(), + .storage = .{ .u64 = bytes[field_index] }, } }), - .elems => |elems| elems[field_i], + .elems => |elems| elems[field_index], .repeated_elem => |elem| elem, }; const cast_context = IntCastContext{ .value = .{ .value = Value.fromInterned(field_val) } }; @@ -1454,19 +1454,19 @@ pub const DeclGen = struct { try writer.writeAll("zig_shl_"); try dg.renderTypeForBuiltinFnName(writer, ty); try writer.writeByte('('); - try dg.renderIntCast(writer, ty, cast_context, Type.fromInterned(field_ty), .FunctionArgument); + try dg.renderIntCast(writer, ty, cast_context, field_ty, .FunctionArgument); try writer.writeAll(", "); const bit_offset_val = try mod.intValue(bit_offset_ty, bit_offset); try dg.renderValue(writer, bit_offset_ty, bit_offset_val, .FunctionArgument); try writer.writeByte(')'); } else { - try dg.renderIntCast(writer, ty, cast_context, Type.fromInterned(field_ty), .FunctionArgument); + try dg.renderIntCast(writer, ty, cast_context, field_ty, .FunctionArgument); } if (needs_closing_paren) try writer.writeByte(')'); if (eff_index != eff_num_fields - 1) try writer.writeAll(", "); - bit_offset += Type.fromInterned(field_ty).bitSize(mod); + bit_offset += field_ty.bitSize(mod); needs_closing_paren = true; eff_index += 1; } @@ -1474,8 +1474,9 @@ pub const DeclGen = struct { try writer.writeByte('('); // a << a_off | b << b_off | c << c_off var empty = true; - for (field_types, 0..) |field_ty, field_i| { - if (!Type.fromInterned(field_ty).hasRuntimeBitsIgnoreComptime(mod)) continue; + for (0..struct_type.field_types.len) |field_index| { + const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[field_index]); + if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) continue; if (!empty) try writer.writeAll(" | "); try writer.writeByte('('); @@ -1484,23 +1485,23 @@ pub const DeclGen = struct { const field_val = switch (ip.indexToKey(val.ip_index).aggregate.storage) { .bytes => |bytes| try ip.get(mod.gpa, .{ .int = .{ - .ty = field_ty, - .storage = .{ .u64 = bytes[field_i] }, + .ty = field_ty.toIntern(), + .storage = .{ .u64 = bytes[field_index] }, } }), - .elems => |elems| elems[field_i], + .elems => |elems| elems[field_index], .repeated_elem => |elem| elem, }; if (bit_offset != 0) { - try dg.renderValue(writer, Type.fromInterned(field_ty), Value.fromInterned(field_val), .Other); + try dg.renderValue(writer, field_ty, Value.fromInterned(field_val), .Other); try writer.writeAll(" << "); const bit_offset_val = try mod.intValue(bit_offset_ty, bit_offset); try dg.renderValue(writer, bit_offset_ty, bit_offset_val, .FunctionArgument); } else { - try dg.renderValue(writer, Type.fromInterned(field_ty), Value.fromInterned(field_val), .Other); + try dg.renderValue(writer, field_ty, Value.fromInterned(field_val), .Other); } - bit_offset += Type.fromInterned(field_ty).bitSize(mod); + bit_offset += field_ty.bitSize(mod); empty = false; } try writer.writeByte(')'); @@ -1545,9 +1546,9 @@ pub const DeclGen = struct { try writer.writeByte(')'); } - const field_i = mod.unionTagFieldIndex(union_obj, Value.fromInterned(un.tag)).?; - const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_i]); - const field_name = union_obj.field_names.get(ip)[field_i]; + const field_index = mod.unionTagFieldIndex(union_obj, Value.fromInterned(un.tag)).?; + const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_index]); + const field_name = union_obj.field_names.get(ip)[field_index]; if (union_obj.getLayout(ip) == .Packed) { if (field_ty.hasRuntimeBits(mod)) { if (field_ty.isPtrAtRuntime(mod)) { @@ -1581,9 +1582,10 @@ pub const DeclGen = struct { try writer.print(" .{ } = ", .{fmtIdent(ip.stringToSlice(field_name))}); try dg.renderValue(writer, field_ty, Value.fromInterned(un.val), initializer_type); try writer.writeByte(' '); - } else for (union_obj.field_types.get(ip)) |this_field_ty| { - if (!Type.fromInterned(this_field_ty).hasRuntimeBits(mod)) continue; - try dg.renderValue(writer, Type.fromInterned(this_field_ty), Value.undef, initializer_type); + } else for (0..union_obj.field_types.len) |this_field_index| { + const this_field_ty = Type.fromInterned(union_obj.field_types.get(ip)[this_field_index]); + if (!this_field_ty.hasRuntimeBits(mod)) continue; + try dg.renderValue(writer, this_field_ty, Value.undef, initializer_type); break; } if (ty.unionTagTypeSafety(mod)) |_| try writer.writeByte('}'); @@ -2595,6 +2597,7 @@ pub fn genGlobalAsm(mod: *Module, writer: anytype) !void { pub fn genErrDecls(o: *Object) !void { const mod = o.dg.module; + const ip = &mod.intern_pool; const writer = o.writer(); var max_name_len: usize = 0; @@ -2603,7 +2606,7 @@ pub fn genErrDecls(o: *Object) !void { try writer.writeAll("enum {\n"); o.indent_writer.pushIndent(); for (mod.global_error_set.keys()[1..], 1..) |name_nts, value| { - const name = mod.intern_pool.stringToSlice(name_nts); + const name = ip.stringToSlice(name_nts); max_name_len = @max(name.len, max_name_len); const err_val = try mod.intern(.{ .err = .{ .ty = .anyerror_type, @@ -2621,8 +2624,8 @@ pub fn genErrDecls(o: *Object) !void { defer o.dg.gpa.free(name_buf); @memcpy(name_buf[0..name_prefix.len], name_prefix); - for (mod.global_error_set.keys()) |name_nts| { - const name = mod.intern_pool.stringToSlice(name_nts); + for (mod.global_error_set.keys()) |name_ip| { + const name = ip.stringToSlice(name_ip); @memcpy(name_buf[name_prefix.len..][0..name.len], name); const identifier = name_buf[0 .. name_prefix.len + name.len]; @@ -2652,7 +2655,7 @@ pub fn genErrDecls(o: *Object) !void { try o.dg.renderTypeAndName(writer, name_array_ty, .{ .identifier = array_identifier }, Const, .none, .complete); try writer.writeAll(" = {"); for (mod.global_error_set.keys(), 0..) |name_nts, value| { - const name = mod.intern_pool.stringToSlice(name_nts); + const name = ip.stringToSlice(name_nts); if (value != 0) try writer.writeByte(','); const len_val = try mod.intValue(Type.usize, name.len); @@ -2730,6 +2733,7 @@ fn genExports(o: *Object) !void { pub fn genLazyFn(o: *Object, lazy_fn: LazyFnMap.Entry) !void { const mod = o.dg.module; + const ip = &mod.intern_pool; const w = o.writer(); const key = lazy_fn.key_ptr.*; const val = lazy_fn.value_ptr; @@ -2747,23 +2751,23 @@ pub fn genLazyFn(o: *Object, lazy_fn: LazyFnMap.Entry) !void { try w.writeByte('('); try o.dg.renderTypeAndName(w, enum_ty, .{ .identifier = "tag" }, Const, .none, .complete); try w.writeAll(") {\n switch (tag) {\n"); - for (enum_ty.enumFields(mod), 0..) |name_ip, index_usize| { - const index = @as(u32, @intCast(index_usize)); - const name = mod.intern_pool.stringToSlice(name_ip); - const tag_val = try mod.enumValueFieldIndex(enum_ty, index); + const tag_names = enum_ty.enumFields(mod); + for (0..tag_names.len) |tag_index| { + const tag_name = ip.stringToSlice(tag_names.get(ip)[tag_index]); + const tag_val = try mod.enumValueFieldIndex(enum_ty, @intCast(tag_index)); const int_val = try tag_val.intFromEnum(enum_ty, mod); const name_ty = try mod.arrayType(.{ - .len = name.len, + .len = tag_name.len, .child = .u8_type, .sentinel = .zero_u8, }); const name_val = try mod.intern(.{ .aggregate = .{ .ty = name_ty.toIntern(), - .storage = .{ .bytes = name }, + .storage = .{ .bytes = tag_name }, } }); - const len_val = try mod.intValue(Type.usize, name.len); + const len_val = try mod.intValue(Type.usize, tag_name.len); try w.print(" case {}: {{\n static ", .{ try o.dg.fmtIntLiteral(enum_ty, int_val, .Other), @@ -4136,9 +4140,7 @@ fn airCmpOp( if (need_cast) try writer.writeAll("(void*)"); try f.writeCValue(writer, lhs, .Other); try v.elem(f, writer); - try writer.writeByte(' '); try writer.writeAll(compareOperatorC(operator)); - try writer.writeByte(' '); if (need_cast) try writer.writeAll("(void*)"); try f.writeCValue(writer, rhs, .Other); try v.elem(f, writer); @@ -4177,41 +4179,28 @@ fn airEquality( const writer = f.object.writer(); const inst_ty = f.typeOfIndex(inst); const local = try f.allocLocal(inst, inst_ty); + const a = try Assignment.start(f, writer, inst_ty); try f.writeCValue(writer, local, .Other); - try writer.writeAll(" = "); + try a.assign(f, writer); if (operand_ty.zigTypeTag(mod) == .Optional and !operand_ty.optionalReprIsPayload(mod)) { - // (A && B) || (C && (A == B)) - // A = lhs.is_null ; B = rhs.is_null ; C = rhs.payload == lhs.payload - - switch (operator) { - .eq => {}, - .neq => try writer.writeByte('!'), - else => unreachable, - } - try writer.writeAll("(("); - try f.writeCValue(writer, lhs, .Other); - try writer.writeAll(".is_null && "); - try f.writeCValue(writer, rhs, .Other); - try writer.writeAll(".is_null) || ("); - try f.writeCValue(writer, lhs, .Other); - try writer.writeAll(".payload == "); - try f.writeCValue(writer, rhs, .Other); - try writer.writeAll(".payload && "); + try f.writeCValueMember(writer, lhs, .{ .identifier = "is_null" }); + try writer.writeAll(" || "); + try f.writeCValueMember(writer, rhs, .{ .identifier = "is_null" }); + try writer.writeAll(" ? "); + try f.writeCValueMember(writer, lhs, .{ .identifier = "is_null" }); + try writer.writeAll(compareOperatorC(operator)); + try f.writeCValueMember(writer, rhs, .{ .identifier = "is_null" }); + try writer.writeAll(" : "); + try f.writeCValueMember(writer, lhs, .{ .identifier = "payload" }); + try writer.writeAll(compareOperatorC(operator)); + try f.writeCValueMember(writer, rhs, .{ .identifier = "payload" }); + } else { try f.writeCValue(writer, lhs, .Other); - try writer.writeAll(".is_null == "); + try writer.writeAll(compareOperatorC(operator)); try f.writeCValue(writer, rhs, .Other); - try writer.writeAll(".is_null));\n"); - - return local; } - - try f.writeCValue(writer, lhs, .Other); - try writer.writeByte(' '); - try writer.writeAll(compareOperatorC(operator)); - try writer.writeByte(' '); - try f.writeCValue(writer, rhs, .Other); - try writer.writeAll(";\n"); + try a.end(f, writer); return local; } @@ -6105,41 +6094,48 @@ fn airFloatCast(f: *Function, inst: Air.Inst.Index) !CValue { const ty_op = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const inst_ty = f.typeOfIndex(inst); + const inst_scalar_ty = inst_ty.scalarType(mod); const operand = try f.resolveInst(ty_op.operand); try reap(f, inst, &.{ty_op.operand}); const operand_ty = f.typeOf(ty_op.operand); + const scalar_ty = operand_ty.scalarType(mod); const target = f.object.dg.module.getTarget(); - const operation = if (inst_ty.isRuntimeFloat() and operand_ty.isRuntimeFloat()) - if (inst_ty.floatBits(target) < operand_ty.floatBits(target)) "trunc" else "extend" - else if (inst_ty.isInt(mod) and operand_ty.isRuntimeFloat()) - if (inst_ty.isSignedInt(mod)) "fix" else "fixuns" - else if (inst_ty.isRuntimeFloat() and operand_ty.isInt(mod)) - if (operand_ty.isSignedInt(mod)) "float" else "floatun" + const operation = if (inst_scalar_ty.isRuntimeFloat() and scalar_ty.isRuntimeFloat()) + if (inst_scalar_ty.floatBits(target) < scalar_ty.floatBits(target)) "trunc" else "extend" + else if (inst_scalar_ty.isInt(mod) and scalar_ty.isRuntimeFloat()) + if (inst_scalar_ty.isSignedInt(mod)) "fix" else "fixuns" + else if (inst_scalar_ty.isRuntimeFloat() and scalar_ty.isInt(mod)) + if (scalar_ty.isSignedInt(mod)) "float" else "floatun" else unreachable; const writer = f.object.writer(); const local = try f.allocLocal(inst, inst_ty); + const v = try Vectorize.start(f, inst, writer, operand_ty); + const a = try Assignment.start(f, writer, scalar_ty); try f.writeCValue(writer, local, .Other); - - try writer.writeAll(" = "); - if (inst_ty.isInt(mod) and operand_ty.isRuntimeFloat()) { + try v.elem(f, writer); + try a.assign(f, writer); + if (inst_scalar_ty.isInt(mod) and scalar_ty.isRuntimeFloat()) { try writer.writeAll("zig_wrap_"); - try f.object.dg.renderTypeForBuiltinFnName(writer, inst_ty); + try f.object.dg.renderTypeForBuiltinFnName(writer, inst_scalar_ty); try writer.writeByte('('); } try writer.writeAll("zig_"); try writer.writeAll(operation); - try writer.writeAll(compilerRtAbbrev(operand_ty, mod)); - try writer.writeAll(compilerRtAbbrev(inst_ty, mod)); + try writer.writeAll(compilerRtAbbrev(scalar_ty, mod)); + try writer.writeAll(compilerRtAbbrev(inst_scalar_ty, mod)); try writer.writeByte('('); try f.writeCValue(writer, operand, .FunctionArgument); + try v.elem(f, writer); try writer.writeByte(')'); - if (inst_ty.isInt(mod) and operand_ty.isRuntimeFloat()) { - try f.object.dg.renderBuiltinInfo(writer, inst_ty, .bits); + if (inst_scalar_ty.isInt(mod) and scalar_ty.isRuntimeFloat()) { + try f.object.dg.renderBuiltinInfo(writer, inst_scalar_ty, .bits); try writer.writeByte(')'); } - try writer.writeAll(";\n"); + try a.end(f, writer); + try v.end(f, inst, writer); + return local; } @@ -6311,7 +6307,7 @@ fn airCmpBuiltinCall( try v.elem(f, writer); try f.object.dg.renderBuiltinInfo(writer, scalar_ty, info); try writer.writeByte(')'); - if (!ref_ret) try writer.print(" {s} {}", .{ + if (!ref_ret) try writer.print("{s}{}", .{ compareOperatorC(operator), try f.fmtIntLiteral(Type.i32, try mod.intValue(Type.i32, 0)), }); @@ -7088,17 +7084,16 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { } }, .Struct => switch (inst_ty.containerLayout(mod)) { - .Auto, .Extern => for (resolved_elements, 0..) |element, field_i_usize| { - const field_i: u32 = @intCast(field_i_usize); - if (inst_ty.structFieldIsComptime(field_i, mod)) continue; - const field_ty = inst_ty.structFieldType(field_i, mod); + .Auto, .Extern => for (resolved_elements, 0..) |element, field_index| { + if (inst_ty.structFieldIsComptime(field_index, mod)) continue; + const field_ty = inst_ty.structFieldType(field_index, mod); if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) continue; const a = try Assignment.start(f, writer, field_ty); try f.writeCValueMember(writer, local, if (inst_ty.isSimpleTuple(mod)) - .{ .field = field_i } + .{ .field = field_index } else - .{ .identifier = ip.stringToSlice(inst_ty.legacyStructFieldName(field_i, mod)) }); + .{ .identifier = ip.stringToSlice(inst_ty.legacyStructFieldName(@intCast(field_index), mod)) }); try a.assign(f, writer); try f.writeCValue(writer, element, .Other); try a.end(f, writer); @@ -7113,9 +7108,9 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { var bit_offset: u64 = 0; var empty = true; - for (0..elements.len) |field_i| { - if (inst_ty.structFieldIsComptime(field_i, mod)) continue; - const field_ty = inst_ty.structFieldType(field_i, mod); + for (0..elements.len) |field_index| { + if (inst_ty.structFieldIsComptime(field_index, mod)) continue; + const field_ty = inst_ty.structFieldType(field_index, mod); if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) continue; if (!empty) { @@ -7126,9 +7121,9 @@ fn airAggregateInit(f: *Function, inst: Air.Inst.Index) !CValue { empty = false; } empty = true; - for (resolved_elements, 0..) |element, field_i| { - if (inst_ty.structFieldIsComptime(field_i, mod)) continue; - const field_ty = inst_ty.structFieldType(field_i, mod); + for (resolved_elements, 0..) |element, field_index| { + if (inst_ty.structFieldIsComptime(field_index, mod)) continue; + const field_ty = inst_ty.structFieldType(field_index, mod); if (!field_ty.hasRuntimeBitsIgnoreComptime(mod)) continue; if (!empty) try writer.writeAll(", "); @@ -7658,12 +7653,12 @@ fn compareOperatorAbbrev(operator: std.math.CompareOperator) []const u8 { fn compareOperatorC(operator: std.math.CompareOperator) []const u8 { return switch (operator) { - .lt => "<", - .lte => "<=", - .eq => "==", - .gte => ">=", - .gt => ">", - .neq => "!=", + .lt => " < ", + .lte => " <= ", + .eq => " == ", + .gte => " >= ", + .gt => " > ", + .neq => " != ", }; } diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 504c9ae1d1..bd0c337169 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -770,37 +770,20 @@ pub const Object = struct { builder: Builder, module: *Module, - di_builder: ?if (build_options.have_llvm) *llvm.DIBuilder else noreturn, - /// One of these mappings: - /// - *Module.File => *DIFile - /// - *Module.Decl (Fn) => *DISubprogram - /// - *Module.Decl (Non-Fn) => *DIGlobalVariable - di_map: if (build_options.have_llvm) std.AutoHashMapUnmanaged(*const anyopaque, *llvm.DINode) else struct { - const K = *const anyopaque; - const V = noreturn; - const Self = @This(); + debug_compile_unit: Builder.Metadata, - metadata: ?noreturn = null, - size: Size = 0, - available: Size = 0, + debug_enums_fwd_ref: Builder.Metadata, + debug_globals_fwd_ref: Builder.Metadata, - pub const Size = u0; + debug_enums: std.ArrayListUnmanaged(Builder.Metadata), + debug_globals: std.ArrayListUnmanaged(Builder.Metadata), - pub fn deinit(self: *Self, allocator: Allocator) void { - _ = allocator; - self.* = undefined; - } + debug_file_map: std.AutoHashMapUnmanaged(*const Module.File, Builder.Metadata), + debug_type_map: std.AutoHashMapUnmanaged(Type, Builder.Metadata), + + debug_unresolved_namespace_scopes: std.AutoArrayHashMapUnmanaged(InternPool.NamespaceIndex, Builder.Metadata), - pub fn get(self: Self, key: K) ?V { - _ = self; - _ = key; - return null; - } - }, - di_compile_unit: ?if (build_options.have_llvm) *llvm.DICompileUnit else noreturn, - target_machine: if (build_options.have_llvm) *llvm.TargetMachine else void, - target_data: if (build_options.have_llvm) *llvm.TargetData else void, target: std.Target, /// Ideally we would use `llvm_module.getNamedFunction` to go from *Decl to LLVM function, /// but that has some downsides: @@ -820,7 +803,6 @@ pub const Object = struct { /// TODO when InternPool garbage collection is implemented, this map needs /// to be garbage collected as well. type_map: TypeMap, - di_type_map: DITypeMap, /// The LLVM global table which holds the names corresponding to Zig errors. /// Note that the values are not added until `emit`, when all errors in /// the compilation are known. @@ -850,164 +832,143 @@ pub const Object = struct { pub const TypeMap = std.AutoHashMapUnmanaged(InternPool.Index, Builder.Type); - /// This is an ArrayHashMap as opposed to a HashMap because in `emit` we - /// want to iterate over it while adding entries to it. - pub const DITypeMap = std.AutoArrayHashMapUnmanaged(InternPool.Index, AnnotatedDITypePtr); - pub fn create(arena: Allocator, comp: *Compilation) !*Object { if (build_options.only_c) unreachable; const gpa = comp.gpa; const target = comp.root_mod.resolved_target.result; const llvm_target_triple = try targetTriple(arena, target); - const strip = comp.root_mod.strip; - const optimize_mode = comp.root_mod.optimize_mode; - const pic = comp.root_mod.pic; var builder = try Builder.init(.{ .allocator = gpa, - .use_lib_llvm = comp.config.use_lib_llvm, - .strip = strip or !comp.config.use_lib_llvm, // TODO + .strip = comp.config.debug_format == .strip, .name = comp.root_name, .target = target, .triple = llvm_target_triple, }); errdefer builder.deinit(); - var target_machine: if (build_options.have_llvm) *llvm.TargetMachine else void = undefined; - var target_data: if (build_options.have_llvm) *llvm.TargetData else void = undefined; - if (builder.useLibLlvm()) { - debug_info: { - switch (comp.config.debug_format) { - .strip => break :debug_info, - .code_view => builder.llvm.module.?.addModuleCodeViewFlag(), - .dwarf => |f| builder.llvm.module.?.addModuleDebugInfoFlag(f == .@"64"), + builder.data_layout = try builder.fmt("{}", .{DataLayoutBuilder{ .target = target }}); + + const debug_compile_unit, const debug_enums_fwd_ref, const debug_globals_fwd_ref = + if (!builder.strip) + debug_info: { + // We fully resolve all paths at this point to avoid lack of + // source line info in stack traces or lack of debugging + // information which, if relative paths were used, would be + // very location dependent. + // TODO: the only concern I have with this is WASI as either host or target, should + // we leave the paths as relative then? + // TODO: This is totally wrong. In dwarf, paths are encoded as relative to + // a particular directory, and then the directory path is specified elsewhere. + // In the compiler frontend we have it stored correctly in this + // way already, but here we throw all that sweet information + // into the garbage can by converting into absolute paths. What + // a terrible tragedy. + const compile_unit_dir = blk: { + if (comp.module) |zcu| m: { + const d = try zcu.root_mod.root.joinString(arena, ""); + if (d.len == 0) break :m; + if (std.fs.path.isAbsolute(d)) break :blk d; + break :blk std.fs.realpathAlloc(arena, d) catch break :blk d; } - builder.llvm.di_builder = builder.llvm.module.?.createDIBuilder(true); + break :blk try std.process.getCwdAlloc(arena); + }; + + const debug_file = try builder.debugFile( + try builder.metadataString(comp.root_name), + try builder.metadataString(compile_unit_dir), + ); + const debug_enums_fwd_ref = try builder.debugForwardReference(); + const debug_globals_fwd_ref = try builder.debugForwardReference(); + + const debug_compile_unit = try builder.debugCompileUnit( + debug_file, // Don't use the version string here; LLVM misparses it when it // includes the git revision. - const producer = try builder.fmt("zig {d}.{d}.{d}", .{ + try builder.metadataStringFmt("zig {d}.{d}.{d}", .{ build_options.semver.major, build_options.semver.minor, build_options.semver.patch, - }); - - // We fully resolve all paths at this point to avoid lack of - // source line info in stack traces or lack of debugging - // information which, if relative paths were used, would be - // very location dependent. - // TODO: the only concern I have with this is WASI as either host or target, should - // we leave the paths as relative then? - // TODO: This is totally wrong. In dwarf, paths are encoded as relative to - // a particular directory, and then the directory path is specified elsewhere. - // In the compiler frontend we have it stored correctly in this - // way already, but here we throw all that sweet information - // into the garbage can by converting into absolute paths. What - // a terrible tragedy. - const compile_unit_dir_z = blk: { - if (comp.module) |zcu| m: { - const d = try zcu.root_mod.root.joinStringZ(arena, ""); - if (d.len == 0) break :m; - if (std.fs.path.isAbsolute(d)) break :blk d; - const realpath = std.fs.realpathAlloc(arena, d) catch break :blk d; - break :blk try arena.dupeZ(u8, realpath); - } - const cwd = try std.process.getCwdAlloc(arena); - break :blk try arena.dupeZ(u8, cwd); - }; - - builder.llvm.di_compile_unit = builder.llvm.di_builder.?.createCompileUnit( - DW.LANG.C99, - builder.llvm.di_builder.?.createFile(comp.root_name, compile_unit_dir_z), - producer.slice(&builder).?, - optimize_mode != .Debug, - "", // flags - 0, // runtime version - "", // split name - 0, // dwo id - true, // emit debug info - ); - } - - const opt_level: llvm.CodeGenOptLevel = if (optimize_mode == .Debug) - .None - else - .Aggressive; - - const reloc_mode: llvm.RelocMode = if (pic) - .PIC - else if (comp.config.link_mode == .Dynamic) - llvm.RelocMode.DynamicNoPIC - else - .Static; - - const code_model: llvm.CodeModel = switch (comp.root_mod.code_model) { - .default => .Default, - .tiny => .Tiny, - .small => .Small, - .kernel => .Kernel, - .medium => .Medium, - .large => .Large, - }; - - // TODO handle float ABI better- it should depend on the ABI portion of std.Target - const float_abi: llvm.ABIType = .Default; - - target_machine = llvm.TargetMachine.create( - builder.llvm.target.?, - builder.target_triple.slice(&builder).?, - if (target.cpu.model.llvm_name) |s| s.ptr else null, - comp.root_mod.resolved_target.llvm_cpu_features.?, - opt_level, - reloc_mode, - code_model, - comp.function_sections, - comp.data_sections, - float_abi, - if (target_util.llvmMachineAbi(target)) |s| s.ptr else null, + }), + debug_enums_fwd_ref, + debug_globals_fwd_ref, + .{ .optimized = comp.root_mod.optimize_mode != .Debug }, ); - errdefer target_machine.dispose(); - - target_data = target_machine.createTargetDataLayout(); - errdefer target_data.dispose(); - builder.llvm.module.?.setModuleDataLayout(target_data); - - if (pic) builder.llvm.module.?.setModulePICLevel(); - if (comp.config.pie) builder.llvm.module.?.setModulePIELevel(); - if (code_model != .Default) builder.llvm.module.?.setModuleCodeModel(code_model); + 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.metadataString("Debug Info Version"), + try builder.debugConstant(i32_3), + ); - if (comp.llvm_opt_bisect_limit >= 0) { - builder.llvm.context.setOptBisectLimit(comp.llvm_opt_bisect_limit); + switch (comp.config.debug_format) { + .strip => unreachable, + .dwarf => |f| { + const i32_4 = try builder.intConst(.i32, 4); + const dwarf_version = try builder.debugModuleFlag( + try builder.debugConstant(i32_2), + try builder.metadataString("Dwarf Version"), + try builder.debugConstant(i32_4), + ); + switch (f) { + .@"32" => { + try builder.debugNamed(try builder.metadataString("llvm.module.flags"), &.{ + debug_info_version, + dwarf_version, + }); + }, + .@"64" => { + const dwarf64 = try builder.debugModuleFlag( + try builder.debugConstant(i32_2), + try builder.metadataString("DWARF64"), + try builder.debugConstant(.@"1"), + ); + try builder.debugNamed(try builder.metadataString("llvm.module.flags"), &.{ + debug_info_version, + dwarf_version, + dwarf64, + }); + }, + } + }, + .code_view => { + const code_view = try builder.debugModuleFlag( + try builder.debugConstant(i32_2), + try builder.metadataString("CodeView"), + try builder.debugConstant(.@"1"), + ); + try builder.debugNamed(try builder.metadataString("llvm.module.flags"), &.{ + debug_info_version, + code_view, + }); + }, } - builder.data_layout = try builder.fmt("{}", .{DataLayoutBuilder{ .target = target }}); - if (std.debug.runtime_safety) { - const rep = target_data.stringRep(); - defer llvm.disposeMessage(rep); - std.testing.expectEqualStrings( - std.mem.span(rep), - builder.data_layout.slice(&builder).?, - ) catch unreachable; - } - } + try builder.debugNamed(try builder.metadataString("llvm.dbg.cu"), &.{debug_compile_unit}); + break :debug_info .{ debug_compile_unit, debug_enums_fwd_ref, debug_globals_fwd_ref }; + } else .{.none} ** 3; const obj = try arena.create(Object); obj.* = .{ .gpa = gpa, .builder = builder, .module = comp.module.?, - .di_map = .{}, - .di_builder = if (builder.useLibLlvm()) builder.llvm.di_builder else null, // TODO - .di_compile_unit = if (builder.useLibLlvm()) builder.llvm.di_compile_unit else null, - .target_machine = target_machine, - .target_data = target_data, + .debug_compile_unit = debug_compile_unit, + .debug_enums_fwd_ref = debug_enums_fwd_ref, + .debug_globals_fwd_ref = debug_globals_fwd_ref, + .debug_enums = .{}, + .debug_globals = .{}, + .debug_file_map = .{}, + .debug_type_map = .{}, + .debug_unresolved_namespace_scopes = .{}, .target = target, .decl_map = .{}, .anon_decl_map = .{}, .named_enum_map = .{}, .type_map = .{}, - .di_type_map = .{}, .error_name_table = .none, .extern_collisions = .{}, .null_opt_usize = .no_init, @@ -1018,12 +979,11 @@ pub const Object = struct { pub fn deinit(self: *Object) void { const gpa = self.gpa; - self.di_map.deinit(gpa); - self.di_type_map.deinit(gpa); - if (self.builder.useLibLlvm()) { - self.target_data.dispose(); - self.target_machine.dispose(); - } + self.debug_enums.deinit(gpa); + self.debug_globals.deinit(gpa); + self.debug_file_map.deinit(gpa); + self.debug_type_map.deinit(gpa); + self.debug_unresolved_namespace_scopes.deinit(gpa); self.decl_map.deinit(gpa); self.anon_decl_map.deinit(gpa); self.named_enum_map.deinit(gpa); @@ -1052,8 +1012,8 @@ pub const Object = struct { llvm_errors[0] = try o.builder.undefConst(llvm_slice_ty); for (llvm_errors[1..], error_name_list[1..]) |*llvm_error, name| { - const name_string = try o.builder.string(mod.intern_pool.stringToSlice(name)); - const name_init = try o.builder.stringNullConst(name_string); + const name_string = try o.builder.stringNull(mod.intern_pool.stringToSlice(name)); + const name_init = try o.builder.stringConst(name_string); const name_variable_index = try o.builder.addVariable(.empty, name_init.typeOf(&o.builder), .default); try name_variable_index.setInitializer(name_init, &o.builder); @@ -1064,7 +1024,7 @@ pub const Object = struct { llvm_error.* = try o.builder.structConst(llvm_slice_ty, &.{ name_variable_index.toConst(&o.builder), - try o.builder.intConst(llvm_usize_ty, name_string.slice(&o.builder).?.len), + try o.builder.intConst(llvm_usize_ty, name_string.slice(&o.builder).?.len - 1), }); } @@ -1091,7 +1051,10 @@ pub const Object = struct { const mod = o.module; const errors_len = mod.global_error_set.count(); - var wip = try Builder.WipFunction.init(&o.builder, llvm_fn.ptrConst(&o.builder).kind.function); + var wip = try Builder.WipFunction.init(&o.builder, .{ + .function = llvm_fn.ptrConst(&o.builder).kind.function, + .strip = true, + }); defer wip.deinit(); wip.cursor = .{ .block = try wip.block(0, "Entry") }; @@ -1188,45 +1151,42 @@ pub const Object = struct { }; pub fn emit(self: *Object, options: EmitOptions) !void { - try self.resolveExportExternCollisions(); - try self.genErrorNameTable(); - try self.genCmpLtErrorsLenFunction(); - try self.genModuleLevelAssembly(); - - if (self.di_builder) |dib| { - // When lowering debug info for pointers, we emitted the element types as - // forward decls. Now we must go flesh those out. - // Here we iterate over a hash map while modifying it but it is OK because - // we never add or remove entries during this loop. - var i: usize = 0; - while (i < self.di_type_map.count()) : (i += 1) { - const value_ptr = &self.di_type_map.values()[i]; - const annotated = value_ptr.*; - if (!annotated.isFwdOnly()) continue; - const entry: Object.DITypeMap.Entry = .{ - .key_ptr = &self.di_type_map.keys()[i], - .value_ptr = value_ptr, - }; - _ = try self.lowerDebugTypeImpl(entry, .full, annotated.toDIType()); - } + { + try self.resolveExportExternCollisions(); + try self.genErrorNameTable(); + try self.genCmpLtErrorsLenFunction(); + try self.genModuleLevelAssembly(); - dib.finalize(); - } + if (!self.builder.strip) { + { + var i: usize = 0; + while (i < self.debug_unresolved_namespace_scopes.count()) : (i += 1) { + const namespace_index = self.debug_unresolved_namespace_scopes.keys()[i]; + const fwd_ref = self.debug_unresolved_namespace_scopes.values()[i]; - if (options.pre_ir_path) |path| { - if (std.mem.eql(u8, path, "-")) { - self.builder.dump(); - } else { - _ = try self.builder.printToFile(path); - } - } + const namespace = self.module.namespacePtr(namespace_index); + const debug_type = try self.lowerDebugType(namespace.getType(self.module)); - if (options.pre_bc_path) |path| _ = try self.builder.writeBitcodeToFile(path); + self.builder.debugForwardReferenceSetType(fwd_ref, debug_type); + } + } - if (std.debug.runtime_safety and !try self.builder.verify()) { - @panic("LLVM module verification failed"); + self.builder.debugForwardReferenceSetType( + self.debug_enums_fwd_ref, + try self.builder.debugTuple(self.debug_enums.items), + ); + + self.builder.debugForwardReferenceSetType( + self.debug_globals_fwd_ref, + try self.builder.debugTuple(self.debug_globals.items), + ); + } } + const target_triple_sentinel = + try self.gpa.dupeZ(u8, self.builder.target_triple.slice(&self.builder).?); + defer self.gpa.free(target_triple_sentinel); + const emit_asm_msg = options.asm_path orelse "(none)"; const emit_bin_msg = options.bin_path orelse "(none)"; const post_llvm_ir_msg = options.post_ir_path orelse "(none)"; @@ -1235,19 +1195,135 @@ pub const Object = struct { emit_asm_msg, emit_bin_msg, post_llvm_ir_msg, post_llvm_bc_msg, }); - if (options.asm_path == null and options.bin_path == null and - options.post_ir_path == null and options.post_bc_path == null) return; + const context, const module = emit: { + if (options.pre_ir_path) |path| { + if (std.mem.eql(u8, path, "-")) { + self.builder.dump(); + } else { + _ = try self.builder.printToFile(path); + } + } + + const bitcode = try self.builder.toBitcode(self.gpa); + defer self.gpa.free(bitcode); + self.builder.clearAndFree(); + + if (options.pre_bc_path) |path| { + var file = try std.fs.cwd().createFile(path, .{}); + defer file.close(); + + const ptr: [*]const u8 = @ptrCast(bitcode.ptr); + try file.writeAll(ptr[0..(bitcode.len * 4)]); + } + + if (options.asm_path == null and options.bin_path == null and + options.post_ir_path == null and options.post_bc_path == null) return; + + if (options.post_bc_path) |path| { + var file = try std.fs.cwd().createFileZ(path, .{}); + defer file.close(); + + const ptr: [*]const u8 = @ptrCast(bitcode.ptr); + try file.writeAll(ptr[0..(bitcode.len * 4)]); + } + + if (!build_options.have_llvm or !self.module.comp.config.use_lib_llvm) { + log.err("emitting without libllvm not implemented", .{}); + return error.FailedToEmit; + } + + initializeLLVMTarget(self.module.comp.root_mod.resolved_target.result.cpu.arch); + + const context: *llvm.Context = llvm.Context.create(); + errdefer context.dispose(); + + const bitcode_memory_buffer = llvm.MemoryBuffer.createMemoryBufferWithMemoryRange( + @ptrCast(bitcode.ptr), + bitcode.len * 4, + "BitcodeBuffer", + llvm.Bool.False, + ); + defer bitcode_memory_buffer.dispose(); + + var module: *llvm.Module = undefined; + if (context.parseBitcodeInContext2(bitcode_memory_buffer, &module).toBool()) { + std.debug.print("Failed to parse bitcode\n", .{}); + return error.FailedToEmit; + } + break :emit .{ context, module }; + }; + defer context.dispose(); + + var target: *llvm.Target = undefined; + var error_message: [*:0]const u8 = undefined; + if (llvm.Target.getFromTriple(target_triple_sentinel, &target, &error_message).toBool()) { + defer llvm.disposeMessage(error_message); + + log.err("LLVM failed to parse '{s}': {s}", .{ + target_triple_sentinel, + error_message, + }); + @panic("Invalid LLVM triple"); + } - if (!self.builder.useLibLlvm()) unreachable; // caught in Compilation.Config.resolve + const optimize_mode = self.module.comp.root_mod.optimize_mode; + const pic = self.module.comp.root_mod.pic; + + const opt_level: llvm.CodeGenOptLevel = if (optimize_mode == .Debug) + .None + else + .Aggressive; + + const reloc_mode: llvm.RelocMode = if (pic) + .PIC + else if (self.module.comp.config.link_mode == .Dynamic) + llvm.RelocMode.DynamicNoPIC + else + .Static; + + const code_model: llvm.CodeModel = switch (self.module.comp.root_mod.code_model) { + .default => .Default, + .tiny => .Tiny, + .small => .Small, + .kernel => .Kernel, + .medium => .Medium, + .large => .Large, + }; + + // TODO handle float ABI better- it should depend on the ABI portion of std.Target + const float_abi: llvm.ABIType = .Default; + + var target_machine = llvm.TargetMachine.create( + target, + target_triple_sentinel, + if (self.module.comp.root_mod.resolved_target.result.cpu.model.llvm_name) |s| s.ptr else null, + self.module.comp.root_mod.resolved_target.llvm_cpu_features.?, + opt_level, + reloc_mode, + code_model, + self.module.comp.function_sections, + self.module.comp.data_sections, + float_abi, + if (target_util.llvmMachineAbi(self.module.comp.root_mod.resolved_target.result)) |s| s.ptr else null, + ); + errdefer target_machine.dispose(); + + if (pic) module.setModulePICLevel(); + if (self.module.comp.config.pie) module.setModulePIELevel(); + if (code_model != .Default) module.setModuleCodeModel(code_model); + + if (self.module.comp.llvm_opt_bisect_limit >= 0) { + context.setOptBisectLimit(self.module.comp.llvm_opt_bisect_limit); + } // Unfortunately, LLVM shits the bed when we ask for both binary and assembly. // So we call the entire pipeline multiple times if this is requested. - var error_message: [*:0]const u8 = undefined; + // var error_message: [*:0]const u8 = undefined; var emit_bin_path = options.bin_path; var post_ir_path = options.post_ir_path; if (options.asm_path != null and options.bin_path != null) { - if (self.target_machine.emitToFile( - self.builder.llvm.module.?, + if (target_machine.emitToFile( + module, &error_message, options.is_debug, options.is_small, @@ -1270,8 +1346,8 @@ pub const Object = struct { post_ir_path = null; } - if (self.target_machine.emitToFile( - self.builder.llvm.module.?, + if (target_machine.emitToFile( + module, &error_message, options.is_debug, options.is_small, @@ -1281,7 +1357,7 @@ pub const Object = struct { options.asm_path, emit_bin_path, post_ir_path, - options.post_bc_path, + null, )) { defer llvm.disposeMessage(error_message); @@ -1300,6 +1376,7 @@ pub const Object = struct { air: Air, liveness: Liveness, ) !void { + const comp = zcu.comp; const func = zcu.funcInfo(func_index); const decl_index = func.owner_decl; const decl = zcu.declPtr(decl_index); @@ -1368,7 +1445,10 @@ pub const Object = struct { function_index.setSection(try o.builder.string(section), &o.builder); var deinit_wip = true; - var wip = try Builder.WipFunction.init(&o.builder, function_index); + var wip = try Builder.WipFunction.init(&o.builder, .{ + .function = function_index, + .strip = owner_mod.strip, + }); defer if (deinit_wip) wip.deinit(); wip.cursor = .{ .block = try wip.block(0, "Entry") }; @@ -1387,8 +1467,6 @@ pub const Object = struct { .unsigned => try attributes.addRetAttr(.zeroext, &o.builder), }; - const comp = zcu.comp; - const err_return_tracing = Type.fromInterned(fn_info.return_type).isError(zcu) and comp.config.any_error_tracing; @@ -1421,7 +1499,7 @@ pub const Object = struct { if (isByRef(param_ty, zcu)) { const alignment = param_ty.abiAlignment(zcu).toLlvm(); const param_llvm_ty = param.typeOfWip(&wip); - const arg_ptr = try buildAllocaInner(&wip, false, param_llvm_ty, alignment, target); + const arg_ptr = try buildAllocaInner(&wip, param_llvm_ty, alignment, target); _ = try wip.store(.normal, param, arg_ptr, alignment); args.appendAssumeCapacity(arg_ptr); } else { @@ -1469,7 +1547,7 @@ pub const Object = struct { const param_llvm_ty = try o.lowerType(param_ty); const alignment = param_ty.abiAlignment(zcu).toLlvm(); - const arg_ptr = try buildAllocaInner(&wip, false, param_llvm_ty, alignment, target); + const arg_ptr = try buildAllocaInner(&wip, param_llvm_ty, alignment, target); _ = try wip.store(.normal, param, arg_ptr, alignment); args.appendAssumeCapacity(if (isByRef(param_ty, zcu)) @@ -1514,7 +1592,7 @@ pub const Object = struct { const param_ty = Type.fromInterned(fn_info.param_types.get(ip)[it.zig_index - 1]); const param_llvm_ty = try o.lowerType(param_ty); const param_alignment = param_ty.abiAlignment(zcu).toLlvm(); - const arg_ptr = try buildAllocaInner(&wip, false, param_llvm_ty, param_alignment, target); + const arg_ptr = try buildAllocaInner(&wip, param_llvm_ty, param_alignment, target); const llvm_ty = try o.builder.structType(.normal, field_types); for (0..field_types.len) |field_i| { const param = wip.arg(llvm_arg_i); @@ -1544,7 +1622,7 @@ pub const Object = struct { llvm_arg_i += 1; const alignment = param_ty.abiAlignment(zcu).toLlvm(); - const arg_ptr = try buildAllocaInner(&wip, false, param_llvm_ty, alignment, target); + const arg_ptr = try buildAllocaInner(&wip, param_llvm_ty, alignment, target); _ = try wip.store(.normal, param, arg_ptr, alignment); args.appendAssumeCapacity(if (isByRef(param_ty, zcu)) @@ -1559,7 +1637,7 @@ pub const Object = struct { llvm_arg_i += 1; const alignment = param_ty.abiAlignment(zcu).toLlvm(); - const arg_ptr = try buildAllocaInner(&wip, false, param_llvm_ty, alignment, target); + const arg_ptr = try buildAllocaInner(&wip, param_llvm_ty, alignment, target); _ = try wip.store(.normal, param, arg_ptr, alignment); args.appendAssumeCapacity(if (isByRef(param_ty, zcu)) @@ -1573,40 +1651,37 @@ pub const Object = struct { function_index.setAttributes(try attributes.finish(&o.builder), &o.builder); - var di_file: ?if (build_options.have_llvm) *llvm.DIFile else noreturn = null; - var di_scope: ?if (build_options.have_llvm) *llvm.DIScope else noreturn = null; - - if (o.di_builder) |dib| { - di_file = try o.getDIFile(gpa, namespace.file_scope); + const file, const subprogram = if (!wip.strip) debug_info: { + const file = try o.getDebugFile(namespace.file_scope); const line_number = decl.src_line + 1; const is_internal_linkage = decl.val.getExternFunc(zcu) == null and !zcu.decl_exports.contains(decl_index); - const noret_bit: c_uint = if (fn_info.return_type == .noreturn_type) - llvm.DIFlags.NoReturn - else - 0; - const decl_di_ty = try o.lowerDebugType(decl.ty, .full); - const subprogram = dib.createFunction( - di_file.?.toScope(), - ip.stringToSlice(decl.name), - function_index.name(&o.builder).slice(&o.builder).?, - di_file.?, + const debug_decl_type = try o.lowerDebugType(decl.ty); + + const subprogram = try o.builder.debugSubprogram( + file, + try o.builder.metadataString(ip.stringToSlice(decl.name)), + try o.builder.metadataStringFromString(function_index.name(&o.builder)), line_number, - decl_di_ty, - is_internal_linkage, - true, // is definition - line_number + func.lbrace_line, // scope line - llvm.DIFlags.StaticMember | noret_bit, - owner_mod.optimize_mode != .Debug, - null, // decl_subprogram + line_number + func.lbrace_line, + debug_decl_type, + .{ + .di_flags = .{ + .StaticMember = true, + .NoReturn = fn_info.return_type == .noreturn_type, + }, + .sp_flags = .{ + .Optimized = owner_mod.optimize_mode != .Debug, + .Definition = true, + .LocalToUnit = is_internal_linkage, + }, + }, + o.debug_compile_unit, ); - try o.di_map.put(gpa, decl, subprogram.toNode()); - - function_index.toLlvm(&o.builder).fnSetSubprogram(subprogram); - - di_scope = subprogram.toScope(); - } + function_index.setSubprogram(subprogram, &o.builder); + break :debug_info .{ file, subprogram }; + } else .{.none} ** 2; var fg: FuncGen = .{ .gpa = gpa, @@ -1620,8 +1695,8 @@ pub const Object = struct { .func_inst_table = .{}, .blocks = .{}, .sync_scope = if (owner_mod.single_threaded) .singlethread else .system, - .di_scope = di_scope, - .di_file = di_file, + .file = file, + .scope = subprogram, .base_line = dg.decl.src_line, .prev_dbg_line = 0, .prev_dbg_column = 0, @@ -1707,26 +1782,7 @@ pub const Object = struct { global_index.setUnnamedAddr(.default, &self.builder); if (comp.config.dll_export_fns) global_index.setDllStorageClass(.default, &self.builder); - if (self.di_map.get(decl)) |di_node| { - const decl_name_slice = decl_name.slice(&self.builder).?; - if (try decl.isFunction(mod)) { - const di_func: *llvm.DISubprogram = @ptrCast(di_node); - const linkage_name = llvm.MDString.get( - self.builder.llvm.context, - decl_name_slice.ptr, - decl_name_slice.len, - ); - di_func.replaceLinkageName(linkage_name); - } else { - const di_global: *llvm.DIGlobalVariable = @ptrCast(di_node); - const linkage_name = llvm.MDString.get( - self.builder.llvm.context, - decl_name_slice.ptr, - decl_name_slice.len, - ); - di_global.replaceLinkageName(linkage_name); - } - } + if (decl.val.getVariable(mod)) |decl_var| { global_index.ptrConst(&self.builder).kind.variable.setThreadLocal( if (decl_var.is_threadlocal) .generaldynamic else .default, @@ -1740,27 +1796,6 @@ pub const Object = struct { ); try global_index.rename(main_exp_name, &self.builder); - if (self.di_map.get(decl)) |di_node| { - const main_exp_name_slice = main_exp_name.slice(&self.builder).?; - if (try decl.isFunction(mod)) { - const di_func: *llvm.DISubprogram = @ptrCast(di_node); - const linkage_name = llvm.MDString.get( - self.builder.llvm.context, - main_exp_name_slice.ptr, - main_exp_name_slice.len, - ); - di_func.replaceLinkageName(linkage_name); - } else { - const di_global: *llvm.DIGlobalVariable = @ptrCast(di_node); - const linkage_name = llvm.MDString.get( - self.builder.llvm.context, - main_exp_name_slice.ptr, - main_exp_name_slice.len, - ); - di_global.replaceLinkageName(linkage_name); - } - } - if (decl.val.getVariable(mod)) |decl_var| if (decl_var.is_threadlocal) global_index.ptrConst(&self.builder).kind .variable.setThreadLocal(.generaldynamic, &self.builder); @@ -1768,7 +1803,7 @@ pub const Object = struct { return updateExportedGlobal(self, mod, global_index, exports); } else { const fqn = try self.builder.string( - mod.intern_pool.stringToSlice(try decl.getFullyQualifiedName(mod)), + mod.intern_pool.stringToSlice(try decl.fullyQualifiedName(mod)), ); try global_index.rename(fqn, &self.builder); global_index.setLinkage(.internal, &self.builder); @@ -1890,110 +1925,79 @@ pub const Object = struct { global.delete(&self.builder); } - fn getDIFile(o: *Object, gpa: Allocator, file: *const Module.File) !*llvm.DIFile { - const gop = try o.di_map.getOrPut(gpa, file); - errdefer assert(o.di_map.remove(file)); - if (gop.found_existing) { - return @ptrCast(gop.value_ptr.*); - } - const dir_path_z = d: { - var buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined; - const sub_path = std.fs.path.dirname(file.sub_file_path) orelse ""; - const dir_path = try file.mod.root.joinStringZ(gpa, sub_path); - if (std.fs.path.isAbsolute(dir_path)) break :d dir_path; - const abs = std.fs.realpath(dir_path, &buffer) catch break :d dir_path; - gpa.free(dir_path); - break :d try gpa.dupeZ(u8, abs); - }; - defer gpa.free(dir_path_z); - const sub_file_path_z = try gpa.dupeZ(u8, std.fs.path.basename(file.sub_file_path)); - defer gpa.free(sub_file_path_z); - const di_file = o.di_builder.?.createFile(sub_file_path_z, dir_path_z); - gop.value_ptr.* = di_file.toNode(); - return di_file; + fn getDebugFile(o: *Object, file: *const Module.File) Allocator.Error!Builder.Metadata { + const gpa = o.gpa; + const gop = try o.debug_file_map.getOrPut(gpa, file); + errdefer assert(o.debug_file_map.remove(file)); + if (gop.found_existing) return gop.value_ptr.*; + gop.value_ptr.* = try o.builder.debugFile( + try o.builder.metadataString(std.fs.path.basename(file.sub_file_path)), + dir_path: { + const sub_path = std.fs.path.dirname(file.sub_file_path) orelse ""; + const dir_path = try file.mod.root.joinString(gpa, sub_path); + defer gpa.free(dir_path); + if (std.fs.path.isAbsolute(dir_path)) + break :dir_path try o.builder.metadataString(dir_path); + var abs_buffer: [std.fs.MAX_PATH_BYTES]u8 = undefined; + const abs_path = std.fs.realpath(dir_path, &abs_buffer) catch + break :dir_path try o.builder.metadataString(dir_path); + break :dir_path try o.builder.metadataString(abs_path); + }, + ); + return gop.value_ptr.*; } - const DebugResolveStatus = enum { fwd, full }; - - /// In the implementation of this function, it is required to store a forward decl - /// into `gop` before making any recursive calls (even directly). - fn lowerDebugType( + pub fn lowerDebugType( o: *Object, ty: Type, - resolve: DebugResolveStatus, - ) Allocator.Error!*llvm.DIType { - const gpa = o.gpa; - // Be careful not to reference this `gop` variable after any recursive calls - // to `lowerDebugType`. - const gop = try o.di_type_map.getOrPut(gpa, ty.toIntern()); - if (gop.found_existing) { - const annotated = gop.value_ptr.*; - const di_type = annotated.toDIType(); - if (!annotated.isFwdOnly() or resolve == .fwd) { - return di_type; - } - const entry: Object.DITypeMap.Entry = .{ - .key_ptr = gop.key_ptr, - .value_ptr = gop.value_ptr, - }; - return o.lowerDebugTypeImpl(entry, resolve, di_type); - } - errdefer assert(o.di_type_map.orderedRemove(ty.toIntern())); - const entry: Object.DITypeMap.Entry = .{ - .key_ptr = gop.key_ptr, - .value_ptr = gop.value_ptr, - }; - return o.lowerDebugTypeImpl(entry, resolve, null); - } + ) Allocator.Error!Builder.Metadata { + assert(!o.builder.strip); - /// This is a helper function used by `lowerDebugType`. - fn lowerDebugTypeImpl( - o: *Object, - gop: Object.DITypeMap.Entry, - resolve: DebugResolveStatus, - opt_fwd_decl: ?*llvm.DIType, - ) Allocator.Error!*llvm.DIType { - const ty = Type.fromInterned(gop.key_ptr.*); const gpa = o.gpa; const target = o.target; - const dib = o.di_builder.?; const mod = o.module; const ip = &mod.intern_pool; + + if (o.debug_type_map.get(ty)) |debug_type| return debug_type; + switch (ty.zigTypeTag(mod)) { - .Void, .NoReturn => { - const di_type = dib.createBasicType("void", 0, DW.ATE.signed); - gop.value_ptr.* = AnnotatedDITypePtr.initFull(di_type); - return di_type; + .Void, + .NoReturn, + => { + const debug_void_type = try o.builder.debugSignedType( + try o.builder.metadataString("void"), + 0, + ); + try o.debug_type_map.put(gpa, ty, debug_void_type); + return debug_void_type; }, .Int => { const info = ty.intInfo(mod); assert(info.bits != 0); const name = try o.allocTypeName(ty); defer gpa.free(name); - const dwarf_encoding: c_uint = switch (info.signedness) { - .signed => DW.ATE.signed, - .unsigned => DW.ATE.unsigned, + const builder_name = try o.builder.metadataString(name); + const debug_bits = ty.abiSize(mod) * 8; // lldb cannot handle non-byte sized types + const debug_int_type = switch (info.signedness) { + .signed => try o.builder.debugSignedType(builder_name, debug_bits), + .unsigned => try o.builder.debugUnsignedType(builder_name, debug_bits), }; - const di_bits = ty.abiSize(mod) * 8; // lldb cannot handle non-byte sized types - const di_type = dib.createBasicType(name, di_bits, dwarf_encoding); - gop.value_ptr.* = AnnotatedDITypePtr.initFull(di_type); - return di_type; + try o.debug_type_map.put(gpa, ty, debug_int_type); + return debug_int_type; }, .Enum => { const owner_decl_index = ty.getOwnerDecl(mod); const owner_decl = o.module.declPtr(owner_decl_index); if (!ty.hasRuntimeBitsIgnoreComptime(mod)) { - const enum_di_ty = try o.makeEmptyNamespaceDIType(owner_decl_index); - // The recursive call to `lowerDebugType` via `makeEmptyNamespaceDIType` - // means we can't use `gop` anymore. - try o.di_type_map.put(gpa, ty.toIntern(), AnnotatedDITypePtr.initFull(enum_di_ty)); - return enum_di_ty; + const debug_enum_type = try o.makeEmptyNamespaceDebugType(owner_decl_index); + try o.debug_type_map.put(gpa, ty, debug_enum_type); + return debug_enum_type; } const enum_type = ip.indexToKey(ty.toIntern()).enum_type; - const enumerators = try gpa.alloc(*llvm.DIEnumerator, enum_type.names.len); + const enumerators = try gpa.alloc(Builder.Metadata, enum_type.names.len); defer gpa.free(enumerators); const int_ty = Type.fromInterned(enum_type.tag_ty); @@ -2001,66 +2005,59 @@ pub const Object = struct { assert(int_info.bits != 0); for (enum_type.names.get(ip), 0..) |field_name_ip, i| { - const field_name_z = ip.stringToSlice(field_name_ip); - var bigint_space: Value.BigIntSpace = undefined; const bigint = if (enum_type.values.len != 0) Value.fromInterned(enum_type.values.get(ip)[i]).toBigInt(&bigint_space, mod) else std.math.big.int.Mutable.init(&bigint_space.limbs, i).toConst(); - if (bigint.limbs.len == 1) { - enumerators[i] = dib.createEnumerator(field_name_z, bigint.limbs[0], int_info.signedness == .unsigned); - continue; - } - if (@sizeOf(usize) == @sizeOf(u64)) { - enumerators[i] = dib.createEnumerator2( - field_name_z, - @intCast(bigint.limbs.len), - bigint.limbs.ptr, - int_info.bits, - int_info.signedness == .unsigned, - ); - continue; - } - @panic("TODO implement bigint debug enumerators to llvm int for 32-bit compiler builds"); + enumerators[i] = try o.builder.debugEnumerator( + try o.builder.metadataString(ip.stringToSlice(field_name_ip)), + int_ty.isUnsignedInt(mod), + int_info.bits, + bigint, + ); } - const di_file = try o.getDIFile(gpa, mod.namespacePtr(owner_decl.src_namespace).file_scope); - const di_scope = try o.namespaceToDebugScope(owner_decl.src_namespace); + const file = try o.getDebugFile(mod.namespacePtr(owner_decl.src_namespace).file_scope); + const scope = try o.namespaceToDebugScope(owner_decl.src_namespace); const name = try o.allocTypeName(ty); defer gpa.free(name); - const enum_di_ty = dib.createEnumerationType( - di_scope, - name, - di_file, - owner_decl.src_node + 1, + const debug_enum_type = try o.builder.debugEnumerationType( + try o.builder.metadataString(name), + file, + scope, + owner_decl.src_node + 1, // Line + try o.lowerDebugType(int_ty), ty.abiSize(mod) * 8, ty.abiAlignment(mod).toByteUnits(0) * 8, - enumerators.ptr, - @intCast(enumerators.len), - try o.lowerDebugType(int_ty, .full), - "", + try o.builder.debugTuple(enumerators), ); - // The recursive call to `lowerDebugType` means we can't use `gop` anymore. - try o.di_type_map.put(gpa, ty.toIntern(), AnnotatedDITypePtr.initFull(enum_di_ty)); - return enum_di_ty; + + try o.debug_type_map.put(gpa, ty, debug_enum_type); + try o.debug_enums.append(gpa, debug_enum_type); + return debug_enum_type; }, .Float => { const bits = ty.floatBits(target); const name = try o.allocTypeName(ty); defer gpa.free(name); - const di_type = dib.createBasicType(name, bits, DW.ATE.float); - gop.value_ptr.* = AnnotatedDITypePtr.initFull(di_type); - return di_type; + const debug_float_type = try o.builder.debugFloatType( + try o.builder.metadataString(name), + bits, + ); + try o.debug_type_map.put(gpa, ty, debug_float_type); + return debug_float_type; }, .Bool => { - const di_bits = 8; // lldb cannot handle non-byte sized types - const di_type = dib.createBasicType("bool", di_bits, DW.ATE.boolean); - gop.value_ptr.* = AnnotatedDITypePtr.initFull(di_type); - return di_type; + const debug_bool_type = try o.builder.debugBoolType( + try o.builder.metadataString("bool"), + 8, // lldb cannot handle non-byte sized types + ); + try o.debug_type_map.put(gpa, ty, debug_bool_type); + return debug_bool_type; }, .Pointer => { // Normalize everything that the debug info does not represent. @@ -2090,136 +2087,145 @@ pub const Object = struct { }, }, }); - const ptr_di_ty = try o.lowerDebugType(bland_ptr_ty, resolve); - // The recursive call to `lowerDebugType` means we can't use `gop` anymore. - try o.di_type_map.put(gpa, ty.toIntern(), AnnotatedDITypePtr.init(ptr_di_ty, resolve)); - return ptr_di_ty; + const debug_ptr_type = try o.lowerDebugType(bland_ptr_ty); + try o.debug_type_map.put(gpa, ty, debug_ptr_type); + return debug_ptr_type; } + const debug_fwd_ref = try o.builder.debugForwardReference(); + + // Set as forward reference while the type is lowered in case it references itself + try o.debug_type_map.put(gpa, ty, debug_fwd_ref); + if (ty.isSlice(mod)) { const ptr_ty = ty.slicePtrFieldType(mod); const len_ty = Type.usize; const name = try o.allocTypeName(ty); defer gpa.free(name); - const di_file: ?*llvm.DIFile = null; const line = 0; - const compile_unit_scope = o.di_compile_unit.?.toScope(); - - const fwd_decl = opt_fwd_decl orelse blk: { - const fwd_decl = dib.createReplaceableCompositeType( - DW.TAG.structure_type, - name.ptr, - compile_unit_scope, - di_file, - line, - ); - gop.value_ptr.* = AnnotatedDITypePtr.initFwd(fwd_decl); - if (resolve == .fwd) return fwd_decl; - break :blk fwd_decl; - }; const ptr_size = ptr_ty.abiSize(mod); const ptr_align = ptr_ty.abiAlignment(mod); const len_size = len_ty.abiSize(mod); const len_align = len_ty.abiAlignment(mod); - var offset: u64 = 0; - offset += ptr_size; - offset = len_align.forward(offset); - const len_offset = offset; - - const fields: [2]*llvm.DIType = .{ - dib.createMemberType( - fwd_decl.toScope(), - "ptr", - di_file, - line, - ptr_size * 8, // size in bits - ptr_align.toByteUnits(0) * 8, // align in bits - 0, // offset in bits - 0, // flags - try o.lowerDebugType(ptr_ty, .full), - ), - dib.createMemberType( - fwd_decl.toScope(), - "len", - di_file, - line, - len_size * 8, // size in bits - len_align.toByteUnits(0) * 8, // align in bits - len_offset * 8, // offset in bits - 0, // flags - try o.lowerDebugType(len_ty, .full), - ), - }; + const len_offset = len_align.forward(ptr_size); + + const debug_ptr_type = try o.builder.debugMemberType( + try o.builder.metadataString("ptr"), + .none, // File + debug_fwd_ref, + 0, // Line + try o.lowerDebugType(ptr_ty), + ptr_size * 8, + ptr_align.toByteUnits(0) * 8, + 0, // Offset + ); - const full_di_ty = dib.createStructType( - compile_unit_scope, - name.ptr, - di_file, + const debug_len_type = try o.builder.debugMemberType( + try o.builder.metadataString("len"), + .none, // File + debug_fwd_ref, + 0, // Line + try o.lowerDebugType(len_ty), + len_size * 8, + len_align.toByteUnits(0) * 8, + len_offset * 8, + ); + + const debug_slice_type = try o.builder.debugStructType( + try o.builder.metadataString(name), + .none, // File + o.debug_compile_unit, // Scope line, - ty.abiSize(mod) * 8, // size in bits - ty.abiAlignment(mod).toByteUnits(0) * 8, // align in bits - 0, // flags - null, // derived from - &fields, - fields.len, - 0, // run time lang - null, // vtable holder - "", // unique id + .none, // Underlying type + ty.abiSize(mod) * 8, + ty.abiAlignment(mod).toByteUnits(0) * 8, + try o.builder.debugTuple(&.{ + debug_ptr_type, + debug_len_type, + }), ); - dib.replaceTemporary(fwd_decl, full_di_ty); - // The recursive call to `lowerDebugType` means we can't use `gop` anymore. - try o.di_type_map.put(gpa, ty.toIntern(), AnnotatedDITypePtr.initFull(full_di_ty)); - return full_di_ty; + + o.builder.debugForwardReferenceSetType(debug_fwd_ref, debug_slice_type); + + // Set to real type now that it has been lowered fully + const map_ptr = o.debug_type_map.getPtr(ty) orelse unreachable; + map_ptr.* = debug_slice_type; + + return debug_slice_type; } - const elem_di_ty = try o.lowerDebugType(Type.fromInterned(ptr_info.child), .fwd); + const debug_elem_ty = try o.lowerDebugType(Type.fromInterned(ptr_info.child)); + const name = try o.allocTypeName(ty); defer gpa.free(name); - const ptr_di_ty = dib.createPointerType( - elem_di_ty, + + const debug_ptr_type = try o.builder.debugPointerType( + try o.builder.metadataString(name), + .none, // File + .none, // Scope + 0, // Line + debug_elem_ty, target.ptrBitWidth(), ty.ptrAlignment(mod).toByteUnits(0) * 8, - name, + 0, // Offset ); - // The recursive call to `lowerDebugType` means we can't use `gop` anymore. - try o.di_type_map.put(gpa, ty.toIntern(), AnnotatedDITypePtr.initFull(ptr_di_ty)); - return ptr_di_ty; + + o.builder.debugForwardReferenceSetType(debug_fwd_ref, debug_ptr_type); + + // Set to real type now that it has been lowered fully + const map_ptr = o.debug_type_map.getPtr(ty) orelse unreachable; + map_ptr.* = debug_ptr_type; + + return debug_ptr_type; }, .Opaque => { if (ty.toIntern() == .anyopaque_type) { - const di_ty = dib.createBasicType("anyopaque", 0, DW.ATE.signed); - gop.value_ptr.* = AnnotatedDITypePtr.initFull(di_ty); - return di_ty; + const debug_opaque_type = try o.builder.debugSignedType( + try o.builder.metadataString("anyopaque"), + 0, + ); + try o.debug_type_map.put(gpa, ty, debug_opaque_type); + return debug_opaque_type; } + const name = try o.allocTypeName(ty); defer gpa.free(name); const owner_decl_index = ty.getOwnerDecl(mod); const owner_decl = o.module.declPtr(owner_decl_index); - const opaque_di_ty = dib.createForwardDeclType( - DW.TAG.structure_type, - name, + const debug_opaque_type = try o.builder.debugStructType( + try o.builder.metadataString(name), + try o.getDebugFile(mod.namespacePtr(owner_decl.src_namespace).file_scope), try o.namespaceToDebugScope(owner_decl.src_namespace), - try o.getDIFile(gpa, mod.namespacePtr(owner_decl.src_namespace).file_scope), - owner_decl.src_node + 1, + owner_decl.src_node + 1, // Line + .none, // Underlying type + 0, // Size + 0, // Align + .none, // Fields ); - // The recursive call to `lowerDebugType` va `namespaceToDebugScope` - // means we can't use `gop` anymore. - try o.di_type_map.put(gpa, ty.toIntern(), AnnotatedDITypePtr.initFull(opaque_di_ty)); - return opaque_di_ty; + try o.debug_type_map.put(gpa, ty, debug_opaque_type); + return debug_opaque_type; }, .Array => { - const array_di_ty = dib.createArrayType( + const debug_array_type = try o.builder.debugArrayType( + .none, // Name + .none, // File + .none, // Scope + 0, // Line + try o.lowerDebugType(ty.childType(mod)), ty.abiSize(mod) * 8, ty.abiAlignment(mod).toByteUnits(0) * 8, - try o.lowerDebugType(ty.childType(mod), .full), - @intCast(ty.arrayLen(mod)), + try o.builder.debugTuple(&.{ + 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(mod))), + ), + }), ); - // The recursive call to `lowerDebugType` means we can't use `gop` anymore. - try o.di_type_map.put(gpa, ty.toIntern(), AnnotatedDITypePtr.initFull(array_di_ty)); - return array_di_ty; + try o.debug_type_map.put(gpa, ty, debug_array_type); + return debug_array_type; }, .Vector => { const elem_ty = ty.elemType2(mod); @@ -2227,146 +2233,136 @@ pub const Object = struct { // @bitSizOf(elem) * len > @bitSizOf(vec). // Neither gdb nor lldb seem to be able to display non-byte sized // vectors properly. - const elem_di_type = switch (elem_ty.zigTypeTag(mod)) { + const debug_elem_type = switch (elem_ty.zigTypeTag(mod)) { .Int => blk: { const info = elem_ty.intInfo(mod); assert(info.bits != 0); const name = try o.allocTypeName(ty); defer gpa.free(name); - const dwarf_encoding: c_uint = switch (info.signedness) { - .signed => DW.ATE.signed, - .unsigned => DW.ATE.unsigned, + const builder_name = try o.builder.metadataString(name); + break :blk switch (info.signedness) { + .signed => try o.builder.debugSignedType(builder_name, info.bits), + .unsigned => try o.builder.debugUnsignedType(builder_name, info.bits), }; - break :blk dib.createBasicType(name, info.bits, dwarf_encoding); }, - .Bool => dib.createBasicType("bool", 1, DW.ATE.boolean), - else => try o.lowerDebugType(ty.childType(mod), .full), + .Bool => try o.builder.debugBoolType( + try o.builder.metadataString("bool"), + 1, + ), + else => try o.lowerDebugType(ty.childType(mod)), }; - const vector_di_ty = dib.createVectorType( + const debug_vector_type = try o.builder.debugVectorType( + .none, // Name + .none, // File + .none, // Scope + 0, // Line + debug_elem_type, ty.abiSize(mod) * 8, - @intCast(ty.abiAlignment(mod).toByteUnits(0) * 8), - elem_di_type, - ty.vectorLen(mod), + ty.abiAlignment(mod).toByteUnits(0) * 8, + try o.builder.debugTuple(&.{ + 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(mod))), + ), + }), ); - // The recursive call to `lowerDebugType` means we can't use `gop` anymore. - try o.di_type_map.put(gpa, ty.toIntern(), AnnotatedDITypePtr.initFull(vector_di_ty)); - return vector_di_ty; + + try o.debug_type_map.put(gpa, ty, debug_vector_type); + return debug_vector_type; }, .Optional => { const name = try o.allocTypeName(ty); defer gpa.free(name); const child_ty = ty.optionalChild(mod); if (!child_ty.hasRuntimeBitsIgnoreComptime(mod)) { - const di_bits = 8; // lldb cannot handle non-byte sized types - const di_ty = dib.createBasicType(name, di_bits, DW.ATE.boolean); - gop.value_ptr.* = AnnotatedDITypePtr.initFull(di_ty); - return di_ty; + const debug_bool_type = try o.builder.debugBoolType( + try o.builder.metadataString(name), + 8, + ); + try o.debug_type_map.put(gpa, ty, debug_bool_type); + return debug_bool_type; } + + const debug_fwd_ref = try o.builder.debugForwardReference(); + + // Set as forward reference while the type is lowered in case it references itself + try o.debug_type_map.put(gpa, ty, debug_fwd_ref); + if (ty.optionalReprIsPayload(mod)) { - const ptr_di_ty = try o.lowerDebugType(child_ty, resolve); - // The recursive call to `lowerDebugType` means we can't use `gop` anymore. - try o.di_type_map.put(gpa, ty.toIntern(), AnnotatedDITypePtr.init(ptr_di_ty, resolve)); - return ptr_di_ty; - } + const debug_optional_type = try o.lowerDebugType(child_ty); - const di_file: ?*llvm.DIFile = null; - const line = 0; - const compile_unit_scope = o.di_compile_unit.?.toScope(); - const fwd_decl = opt_fwd_decl orelse blk: { - const fwd_decl = dib.createReplaceableCompositeType( - DW.TAG.structure_type, - name.ptr, - compile_unit_scope, - di_file, - line, - ); - gop.value_ptr.* = AnnotatedDITypePtr.initFwd(fwd_decl); - if (resolve == .fwd) return fwd_decl; - break :blk fwd_decl; - }; + o.builder.debugForwardReferenceSetType(debug_fwd_ref, debug_optional_type); + + // Set to real type now that it has been lowered fully + const map_ptr = o.debug_type_map.getPtr(ty) orelse unreachable; + map_ptr.* = debug_optional_type; + + return debug_optional_type; + } const non_null_ty = Type.u8; const payload_size = child_ty.abiSize(mod); const payload_align = child_ty.abiAlignment(mod); const non_null_size = non_null_ty.abiSize(mod); const non_null_align = non_null_ty.abiAlignment(mod); + const non_null_offset = non_null_align.forward(payload_size); + + const debug_data_type = try o.builder.debugMemberType( + try o.builder.metadataString("data"), + .none, // File + debug_fwd_ref, + 0, // Line + try o.lowerDebugType(child_ty), + payload_size * 8, + payload_align.toByteUnits(0) * 8, + 0, // Offset + ); - var offset: u64 = 0; - offset += payload_size; - offset = non_null_align.forward(offset); - const non_null_offset = offset; - - const fields: [2]*llvm.DIType = .{ - dib.createMemberType( - fwd_decl.toScope(), - "data", - di_file, - line, - payload_size * 8, // size in bits - payload_align.toByteUnits(0) * 8, // align in bits - 0, // offset in bits - 0, // flags - try o.lowerDebugType(child_ty, .full), - ), - dib.createMemberType( - fwd_decl.toScope(), - "some", - di_file, - line, - non_null_size * 8, // size in bits - non_null_align.toByteUnits(0) * 8, // align in bits - non_null_offset * 8, // offset in bits - 0, // flags - try o.lowerDebugType(non_null_ty, .full), - ), - }; + const debug_some_type = try o.builder.debugMemberType( + try o.builder.metadataString("some"), + .none, + debug_fwd_ref, + 0, + try o.lowerDebugType(non_null_ty), + non_null_size * 8, + non_null_align.toByteUnits(0) * 8, + non_null_offset * 8, + ); - const full_di_ty = dib.createStructType( - compile_unit_scope, - name.ptr, - di_file, - line, - ty.abiSize(mod) * 8, // size in bits - ty.abiAlignment(mod).toByteUnits(0) * 8, // align in bits - 0, // flags - null, // derived from - &fields, - fields.len, - 0, // run time lang - null, // vtable holder - "", // unique id + const debug_optional_type = try o.builder.debugStructType( + try o.builder.metadataString(name), + .none, // File + o.debug_compile_unit, // Scope + 0, // Line + .none, // Underlying type + ty.abiSize(mod) * 8, + ty.abiAlignment(mod).toByteUnits(0) * 8, + try o.builder.debugTuple(&.{ + debug_data_type, + debug_some_type, + }), ); - dib.replaceTemporary(fwd_decl, full_di_ty); - // The recursive call to `lowerDebugType` means we can't use `gop` anymore. - try o.di_type_map.put(gpa, ty.toIntern(), AnnotatedDITypePtr.initFull(full_di_ty)); - return full_di_ty; + + o.builder.debugForwardReferenceSetType(debug_fwd_ref, debug_optional_type); + + // Set to real type now that it has been lowered fully + const map_ptr = o.debug_type_map.getPtr(ty) orelse unreachable; + map_ptr.* = debug_optional_type; + + return debug_optional_type; }, .ErrorUnion => { const payload_ty = ty.errorUnionPayload(mod); if (!payload_ty.hasRuntimeBitsIgnoreComptime(mod)) { - const err_set_di_ty = try o.lowerDebugType(Type.anyerror, .full); - // The recursive call to `lowerDebugType` means we can't use `gop` anymore. - try o.di_type_map.put(gpa, ty.toIntern(), AnnotatedDITypePtr.initFull(err_set_di_ty)); - return err_set_di_ty; + // TODO: Maybe remove? + const debug_error_union_type = try o.lowerDebugType(Type.anyerror); + try o.debug_type_map.put(gpa, ty, debug_error_union_type); + return debug_error_union_type; } + const name = try o.allocTypeName(ty); defer gpa.free(name); - const di_file: ?*llvm.DIFile = null; - const line = 0; - const compile_unit_scope = o.di_compile_unit.?.toScope(); - const fwd_decl = opt_fwd_decl orelse blk: { - const fwd_decl = dib.createReplaceableCompositeType( - DW.TAG.structure_type, - name.ptr, - compile_unit_scope, - di_file, - line, - ); - gop.value_ptr.* = AnnotatedDITypePtr.initFwd(fwd_decl); - if (resolve == .fwd) return fwd_decl; - break :blk fwd_decl; - }; const error_size = Type.anyerror.abiSize(mod); const error_align = Type.anyerror.abiAlignment(mod); @@ -2389,59 +2385,55 @@ pub const Object = struct { error_offset = error_align.forward(payload_size); } - var fields: [2]*llvm.DIType = undefined; - fields[error_index] = dib.createMemberType( - fwd_decl.toScope(), - "tag", - di_file, - line, - error_size * 8, // size in bits - error_align.toByteUnits(0) * 8, // align in bits - error_offset * 8, // offset in bits - 0, // flags - try o.lowerDebugType(Type.anyerror, .full), + const debug_fwd_ref = try o.builder.debugForwardReference(); + + var fields: [2]Builder.Metadata = undefined; + fields[error_index] = try o.builder.debugMemberType( + try o.builder.metadataString("tag"), + .none, // File + debug_fwd_ref, + 0, // Line + try o.lowerDebugType(Type.anyerror), + error_size * 8, + error_align.toByteUnits(0) * 8, + error_offset * 8, ); - fields[payload_index] = dib.createMemberType( - fwd_decl.toScope(), - "value", - di_file, - line, - payload_size * 8, // size in bits - payload_align.toByteUnits(0) * 8, // align in bits - payload_offset * 8, // offset in bits - 0, // flags - try o.lowerDebugType(payload_ty, .full), + fields[payload_index] = try o.builder.debugMemberType( + try o.builder.metadataString("value"), + .none, // File + debug_fwd_ref, + 0, // Line + try o.lowerDebugType(payload_ty), + payload_size * 8, + payload_align.toByteUnits(0) * 8, + payload_offset * 8, ); - const full_di_ty = dib.createStructType( - compile_unit_scope, - name.ptr, - di_file, - line, - ty.abiSize(mod) * 8, // size in bits - ty.abiAlignment(mod).toByteUnits(0) * 8, // align in bits - 0, // flags - null, // derived from - &fields, - fields.len, - 0, // run time lang - null, // vtable holder - "", // unique id + const debug_error_union_type = try o.builder.debugStructType( + try o.builder.metadataString(name), + .none, // File + o.debug_compile_unit, // Sope + 0, // Line + .none, // Underlying type + ty.abiSize(mod) * 8, + ty.abiAlignment(mod).toByteUnits(0) * 8, + try o.builder.debugTuple(&fields), ); - dib.replaceTemporary(fwd_decl, full_di_ty); - // The recursive call to `lowerDebugType` means we can't use `gop` anymore. - try o.di_type_map.put(gpa, ty.toIntern(), AnnotatedDITypePtr.initFull(full_di_ty)); - return full_di_ty; + + o.builder.debugForwardReferenceSetType(debug_fwd_ref, debug_error_union_type); + + try o.debug_type_map.put(gpa, ty, debug_error_union_type); + return debug_error_union_type; }, .ErrorSet => { - // TODO make this a proper enum with all the error codes in it. - // will need to consider how to take incremental compilation into account. - const di_ty = dib.createBasicType("anyerror", 16, DW.ATE.unsigned); - gop.value_ptr.* = AnnotatedDITypePtr.initFull(di_ty); - return di_ty; + const debug_error_set = try o.builder.debugUnsignedType( + try o.builder.metadataString("anyerror"), + 16, + ); + try o.debug_type_map.put(gpa, ty, debug_error_set); + return debug_error_set; }, .Struct => { - const compile_unit_scope = o.di_compile_unit.?.toScope(); const name = try o.allocTypeName(ty); defer gpa.free(name); @@ -2449,40 +2441,28 @@ pub const Object = struct { const backing_int_ty = struct_type.backingIntType(ip).*; if (backing_int_ty != .none) { const info = Type.fromInterned(backing_int_ty).intInfo(mod); - const dwarf_encoding: c_uint = switch (info.signedness) { - .signed => DW.ATE.signed, - .unsigned => DW.ATE.unsigned, + const builder_name = try o.builder.metadataString(name); + const debug_int_type = switch (info.signedness) { + .signed => try o.builder.debugSignedType(builder_name, ty.abiSize(mod) * 8), + .unsigned => try o.builder.debugUnsignedType(builder_name, ty.abiSize(mod) * 8), }; - const di_bits = ty.abiSize(mod) * 8; // lldb cannot handle non-byte sized types - const di_ty = dib.createBasicType(name, di_bits, dwarf_encoding); - gop.value_ptr.* = AnnotatedDITypePtr.initFull(di_ty); - return di_ty; + try o.debug_type_map.put(gpa, ty, debug_int_type); + return debug_int_type; } } - const fwd_decl = opt_fwd_decl orelse blk: { - const fwd_decl = dib.createReplaceableCompositeType( - DW.TAG.structure_type, - name.ptr, - compile_unit_scope, - null, // file - 0, // line - ); - gop.value_ptr.* = AnnotatedDITypePtr.initFwd(fwd_decl); - if (resolve == .fwd) return fwd_decl; - break :blk fwd_decl; - }; - switch (ip.indexToKey(ty.toIntern())) { .anon_struct_type => |tuple| { - var di_fields: std.ArrayListUnmanaged(*llvm.DIType) = .{}; - defer di_fields.deinit(gpa); + var fields: std.ArrayListUnmanaged(Builder.Metadata) = .{}; + defer fields.deinit(gpa); - try di_fields.ensureUnusedCapacity(gpa, tuple.types.len); + try fields.ensureUnusedCapacity(gpa, tuple.types.len); comptime assert(struct_layout_version == 2); var offset: u64 = 0; + const debug_fwd_ref = try o.builder.debugForwardReference(); + for (tuple.types.get(ip), tuple.values.get(ip), 0..) |field_ty, field_val, i| { if (field_val != .none or !Type.fromInterned(field_ty).hasRuntimeBits(mod)) continue; @@ -2497,38 +2477,33 @@ pub const Object = struct { try std.fmt.allocPrintZ(gpa, "{d}", .{i}); defer if (tuple.names.len == 0) gpa.free(field_name); - try di_fields.append(gpa, dib.createMemberType( - fwd_decl.toScope(), - field_name, - null, // file - 0, // line - field_size * 8, // size in bits - field_align.toByteUnits(0) * 8, // align in bits - field_offset * 8, // offset in bits - 0, // flags - try o.lowerDebugType(Type.fromInterned(field_ty), .full), + fields.appendAssumeCapacity(try o.builder.debugMemberType( + try o.builder.metadataString(field_name), + .none, // File + debug_fwd_ref, + 0, + try o.lowerDebugType(Type.fromInterned(field_ty)), + field_size * 8, + field_align.toByteUnits(0) * 8, + field_offset * 8, )); } - const full_di_ty = dib.createStructType( - compile_unit_scope, - name.ptr, - null, // file - 0, // line - ty.abiSize(mod) * 8, // size in bits - ty.abiAlignment(mod).toByteUnits(0) * 8, // align in bits - 0, // flags - null, // derived from - di_fields.items.ptr, - @intCast(di_fields.items.len), - 0, // run time lang - null, // vtable holder - "", // unique id + const debug_struct_type = try o.builder.debugStructType( + try o.builder.metadataString(name), + .none, // File + o.debug_compile_unit, // Scope + 0, // Line + .none, // Underlying type + ty.abiSize(mod) * 8, + ty.abiAlignment(mod).toByteUnits(0) * 8, + try o.builder.debugTuple(fields.items), ); - dib.replaceTemporary(fwd_decl, full_di_ty); - // The recursive call to `lowerDebugType` means we can't use `gop` anymore. - try o.di_type_map.put(gpa, ty.toIntern(), AnnotatedDITypePtr.initFull(full_di_ty)); - return full_di_ty; + + o.builder.debugForwardReferenceSetType(debug_fwd_ref, debug_struct_type); + + try o.debug_type_map.put(gpa, ty, debug_struct_type); + return debug_struct_type; }, .struct_type => |struct_type| { if (!struct_type.haveFieldTypes(ip)) { @@ -2540,12 +2515,9 @@ pub const Object = struct { // rather than changing the frontend to unnecessarily resolve the // struct field types. const owner_decl_index = ty.getOwnerDecl(mod); - const struct_di_ty = try o.makeEmptyNamespaceDIType(owner_decl_index); - dib.replaceTemporary(fwd_decl, struct_di_ty); - // The recursive call to `lowerDebugType` via `makeEmptyNamespaceDIType` - // means we can't use `gop` anymore. - try o.di_type_map.put(gpa, ty.toIntern(), AnnotatedDITypePtr.initFull(struct_di_ty)); - return struct_di_ty; + const debug_struct_type = try o.makeEmptyNamespaceDebugType(owner_decl_index); + try o.debug_type_map.put(gpa, ty, debug_struct_type); + return debug_struct_type; } }, else => {}, @@ -2553,20 +2525,22 @@ pub const Object = struct { if (!ty.hasRuntimeBitsIgnoreComptime(mod)) { const owner_decl_index = ty.getOwnerDecl(mod); - const struct_di_ty = try o.makeEmptyNamespaceDIType(owner_decl_index); - dib.replaceTemporary(fwd_decl, struct_di_ty); - // The recursive call to `lowerDebugType` via `makeEmptyNamespaceDIType` - // means we can't use `gop` anymore. - try o.di_type_map.put(gpa, ty.toIntern(), AnnotatedDITypePtr.initFull(struct_di_ty)); - return struct_di_ty; + const debug_struct_type = try o.makeEmptyNamespaceDebugType(owner_decl_index); + try o.debug_type_map.put(gpa, ty, debug_struct_type); + return debug_struct_type; } const struct_type = mod.typeToStruct(ty).?; - var di_fields: std.ArrayListUnmanaged(*llvm.DIType) = .{}; - defer di_fields.deinit(gpa); + var fields: std.ArrayListUnmanaged(Builder.Metadata) = .{}; + defer fields.deinit(gpa); + + try fields.ensureUnusedCapacity(gpa, struct_type.field_types.len); + + const debug_fwd_ref = try o.builder.debugForwardReference(); - try di_fields.ensureUnusedCapacity(gpa, struct_type.field_types.len); + // Set as forward reference while the type is lowered in case it references itself + try o.debug_type_map.put(gpa, ty, debug_fwd_ref); comptime assert(struct_layout_version == 2); var it = struct_type.iterateRuntimeOrder(ip); @@ -2584,103 +2558,88 @@ pub const Object = struct { const field_name = struct_type.fieldName(ip, field_index).unwrap() orelse try ip.getOrPutStringFmt(gpa, "{d}", .{field_index}); - const field_di_ty = try o.lowerDebugType(field_ty, .full); - - try di_fields.append(gpa, dib.createMemberType( - fwd_decl.toScope(), - ip.stringToSlice(field_name), - null, // file - 0, // line - field_size * 8, // size in bits - field_align.toByteUnits(0) * 8, // align in bits - field_offset * 8, // offset in bits - 0, // flags - field_di_ty, + fields.appendAssumeCapacity(try o.builder.debugMemberType( + try o.builder.metadataString(ip.stringToSlice(field_name)), + .none, // File + debug_fwd_ref, + 0, // Line + try o.lowerDebugType(field_ty), + field_size * 8, + field_align.toByteUnits(0) * 8, + field_offset * 8, )); } - const full_di_ty = dib.createStructType( - compile_unit_scope, - name.ptr, - null, // file - 0, // line - ty.abiSize(mod) * 8, // size in bits - ty.abiAlignment(mod).toByteUnits(0) * 8, // align in bits - 0, // flags - null, // derived from - di_fields.items.ptr, - @intCast(di_fields.items.len), - 0, // run time lang - null, // vtable holder - "", // unique id + const debug_struct_type = try o.builder.debugStructType( + try o.builder.metadataString(name), + .none, // File + o.debug_compile_unit, // Scope + 0, // Line + .none, // Underlying type + ty.abiSize(mod) * 8, + ty.abiAlignment(mod).toByteUnits(0) * 8, + try o.builder.debugTuple(fields.items), ); - dib.replaceTemporary(fwd_decl, full_di_ty); - // The recursive call to `lowerDebugType` means we can't use `gop` anymore. - try o.di_type_map.put(gpa, ty.toIntern(), AnnotatedDITypePtr.initFull(full_di_ty)); - return full_di_ty; + + o.builder.debugForwardReferenceSetType(debug_fwd_ref, debug_struct_type); + + // Set to real type now that it has been lowered fully + const map_ptr = o.debug_type_map.getPtr(ty) orelse unreachable; + map_ptr.* = debug_struct_type; + + return debug_struct_type; }, .Union => { - const compile_unit_scope = o.di_compile_unit.?.toScope(); const owner_decl_index = ty.getOwnerDecl(mod); const name = try o.allocTypeName(ty); defer gpa.free(name); - const fwd_decl = opt_fwd_decl orelse blk: { - const fwd_decl = dib.createReplaceableCompositeType( - DW.TAG.structure_type, - name.ptr, - o.di_compile_unit.?.toScope(), - null, // file - 0, // line - ); - gop.value_ptr.* = AnnotatedDITypePtr.initFwd(fwd_decl); - if (resolve == .fwd) return fwd_decl; - break :blk fwd_decl; - }; - const union_type = ip.indexToKey(ty.toIntern()).union_type; if (!union_type.haveFieldTypes(ip) or !ty.hasRuntimeBitsIgnoreComptime(mod)) { - const union_di_ty = try o.makeEmptyNamespaceDIType(owner_decl_index); - dib.replaceTemporary(fwd_decl, union_di_ty); - // The recursive call to `lowerDebugType` via `makeEmptyNamespaceDIType` - // means we can't use `gop` anymore. - try o.di_type_map.put(gpa, ty.toIntern(), AnnotatedDITypePtr.initFull(union_di_ty)); - return union_di_ty; + const debug_union_type = try o.makeEmptyNamespaceDebugType(owner_decl_index); + try o.debug_type_map.put(gpa, ty, debug_union_type); + return debug_union_type; } const union_obj = ip.loadUnionType(union_type); const layout = mod.getUnionLayout(union_obj); + const debug_fwd_ref = try o.builder.debugForwardReference(); + + // Set as forward reference while the type is lowered in case it references itself + try o.debug_type_map.put(gpa, ty, debug_fwd_ref); + if (layout.payload_size == 0) { - const tag_di_ty = try o.lowerDebugType(Type.fromInterned(union_obj.enum_tag_ty), .full); - const di_fields = [_]*llvm.DIType{tag_di_ty}; - const full_di_ty = dib.createStructType( - compile_unit_scope, - name.ptr, - null, // file - 0, // line - ty.abiSize(mod) * 8, // size in bits - ty.abiAlignment(mod).toByteUnits(0) * 8, // align in bits - 0, // flags - null, // derived from - &di_fields, - di_fields.len, - 0, // run time lang - null, // vtable holder - "", // unique id + const debug_union_type = try o.builder.debugStructType( + try o.builder.metadataString(name), + .none, // File + o.debug_compile_unit, // Scope + 0, // Line + .none, // Underlying type + ty.abiSize(mod) * 8, + ty.abiAlignment(mod).toByteUnits(0) * 8, + try o.builder.debugTuple( + &.{try o.lowerDebugType(Type.fromInterned(union_obj.enum_tag_ty))}, + ), ); - dib.replaceTemporary(fwd_decl, full_di_ty); - // The recursive call to `lowerDebugType` via `makeEmptyNamespaceDIType` - // means we can't use `gop` anymore. - try o.di_type_map.put(gpa, ty.toIntern(), AnnotatedDITypePtr.initFull(full_di_ty)); - return full_di_ty; + + // Set to real type now that it has been lowered fully + const map_ptr = o.debug_type_map.getPtr(ty) orelse unreachable; + map_ptr.* = debug_union_type; + + return debug_union_type; } - var di_fields: std.ArrayListUnmanaged(*llvm.DIType) = .{}; - defer di_fields.deinit(gpa); + var fields: std.ArrayListUnmanaged(Builder.Metadata) = .{}; + defer fields.deinit(gpa); - try di_fields.ensureUnusedCapacity(gpa, union_obj.field_names.len); + try fields.ensureUnusedCapacity(gpa, union_obj.field_names.len); + + const debug_union_fwd_ref = if (layout.tag_size == 0) + debug_fwd_ref + else + try o.builder.debugForwardReference(); for (0..union_obj.field_names.len) |field_index| { const field_ty = union_obj.field_types.get(ip)[field_index]; @@ -2689,18 +2648,16 @@ pub const Object = struct { const field_size = Type.fromInterned(field_ty).abiSize(mod); const field_align = mod.unionFieldNormalAlignment(union_obj, @intCast(field_index)); - const field_di_ty = try o.lowerDebugType(Type.fromInterned(field_ty), .full); const field_name = union_obj.field_names.get(ip)[field_index]; - di_fields.appendAssumeCapacity(dib.createMemberType( - fwd_decl.toScope(), - ip.stringToSlice(field_name), - null, // file - 0, // line - field_size * 8, // size in bits - field_align.toByteUnits(0) * 8, // align in bits - 0, // offset in bits - 0, // flags - field_di_ty, + fields.appendAssumeCapacity(try o.builder.debugMemberType( + try o.builder.metadataString(ip.stringToSlice(field_name)), + .none, // File + debug_union_fwd_ref, + 0, // Line + try o.lowerDebugType(Type.fromInterned(field_ty)), + field_size * 8, + field_align.toByteUnits(0) * 8, + 0, // Offset )); } @@ -2711,25 +2668,25 @@ pub const Object = struct { break :name union_name_buf.?; }; - const union_di_ty = dib.createUnionType( - compile_unit_scope, - union_name.ptr, - null, // file - 0, // line - ty.abiSize(mod) * 8, // size in bits - ty.abiAlignment(mod).toByteUnits(0) * 8, // align in bits - 0, // flags - di_fields.items.ptr, - @intCast(di_fields.items.len), - 0, // run time lang - "", // unique id + const debug_union_type = try o.builder.debugUnionType( + try o.builder.metadataString(union_name), + .none, // File + o.debug_compile_unit, // Scope + 0, // Line + .none, // Underlying type + ty.abiSize(mod) * 8, + ty.abiAlignment(mod).toByteUnits(0) * 8, + try o.builder.debugTuple(fields.items), ); + o.builder.debugForwardReferenceSetType(debug_union_fwd_ref, debug_union_type); + if (layout.tag_size == 0) { - dib.replaceTemporary(fwd_decl, union_di_ty); - // The recursive call to `lowerDebugType` means we can't use `gop` anymore. - try o.di_type_map.put(gpa, ty.toIntern(), AnnotatedDITypePtr.initFull(union_di_ty)); - return union_di_ty; + // Set to real type now that it has been lowered fully + const map_ptr = o.debug_type_map.getPtr(ty) orelse unreachable; + map_ptr.* = debug_union_type; + + return debug_union_type; } var tag_offset: u64 = undefined; @@ -2742,81 +2699,80 @@ pub const Object = struct { tag_offset = layout.tag_align.forward(layout.payload_size); } - const tag_di = dib.createMemberType( - fwd_decl.toScope(), - "tag", - null, // file - 0, // line + const debug_tag_type = try o.builder.debugMemberType( + try o.builder.metadataString("tag"), + .none, // File + debug_fwd_ref, + 0, // Line + try o.lowerDebugType(Type.fromInterned(union_obj.enum_tag_ty)), layout.tag_size * 8, layout.tag_align.toByteUnits(0) * 8, - tag_offset * 8, // offset in bits - 0, // flags - try o.lowerDebugType(Type.fromInterned(union_obj.enum_tag_ty), .full), + tag_offset * 8, ); - const payload_di = dib.createMemberType( - fwd_decl.toScope(), - "payload", - null, // file - 0, // line - layout.payload_size * 8, // size in bits + const debug_payload_type = try o.builder.debugMemberType( + try o.builder.metadataString("payload"), + .none, // File + debug_fwd_ref, + 0, // Line + debug_union_type, + layout.payload_size * 8, layout.payload_align.toByteUnits(0) * 8, - payload_offset * 8, // offset in bits - 0, // flags - union_di_ty, + payload_offset * 8, ); - const full_di_fields: [2]*llvm.DIType = + const full_fields: [2]Builder.Metadata = if (layout.tag_align.compare(.gte, layout.payload_align)) - .{ tag_di, payload_di } + .{ debug_tag_type, debug_payload_type } else - .{ payload_di, tag_di }; - - const full_di_ty = dib.createStructType( - compile_unit_scope, - name.ptr, - null, // file - 0, // line - ty.abiSize(mod) * 8, // size in bits - ty.abiAlignment(mod).toByteUnits(0) * 8, // align in bits - 0, // flags - null, // derived from - &full_di_fields, - full_di_fields.len, - 0, // run time lang - null, // vtable holder - "", // unique id + .{ debug_payload_type, debug_tag_type }; + + const debug_tagged_union_type = try o.builder.debugStructType( + try o.builder.metadataString(name), + .none, // File + o.debug_compile_unit, // Scope + 0, // Line + .none, // Underlying type + ty.abiSize(mod) * 8, + ty.abiAlignment(mod).toByteUnits(0) * 8, + try o.builder.debugTuple(&full_fields), ); - dib.replaceTemporary(fwd_decl, full_di_ty); - // The recursive call to `lowerDebugType` means we can't use `gop` anymore. - try o.di_type_map.put(gpa, ty.toIntern(), AnnotatedDITypePtr.initFull(full_di_ty)); - return full_di_ty; + + o.builder.debugForwardReferenceSetType(debug_fwd_ref, debug_tagged_union_type); + + // Set to real type now that it has been lowered fully + const map_ptr = o.debug_type_map.getPtr(ty) orelse unreachable; + map_ptr.* = debug_tagged_union_type; + + return debug_tagged_union_type; }, .Fn => { const fn_info = mod.typeToFunc(ty).?; - var param_di_types = std.ArrayList(*llvm.DIType).init(gpa); - defer param_di_types.deinit(); + var debug_param_types = std.ArrayList(Builder.Metadata).init(gpa); + defer debug_param_types.deinit(); + + try debug_param_types.ensureUnusedCapacity(3 + fn_info.param_types.len); // Return type goes first. if (Type.fromInterned(fn_info.return_type).hasRuntimeBitsIgnoreComptime(mod)) { const sret = firstParamSRet(fn_info, mod); - const di_ret_ty = if (sret) Type.void else Type.fromInterned(fn_info.return_type); - try param_di_types.append(try o.lowerDebugType(di_ret_ty, .full)); + const ret_ty = if (sret) Type.void else Type.fromInterned(fn_info.return_type); + debug_param_types.appendAssumeCapacity(try o.lowerDebugType(ret_ty)); if (sret) { const ptr_ty = try mod.singleMutPtrType(Type.fromInterned(fn_info.return_type)); - try param_di_types.append(try o.lowerDebugType(ptr_ty, .full)); + debug_param_types.appendAssumeCapacity(try o.lowerDebugType(ptr_ty)); } } else { - try param_di_types.append(try o.lowerDebugType(Type.void, .full)); + debug_param_types.appendAssumeCapacity(try o.lowerDebugType(Type.void)); } if (Type.fromInterned(fn_info.return_type).isError(mod) and o.module.comp.config.any_error_tracing) { const ptr_ty = try mod.singleMutPtrType(try o.getStackTraceType()); - try param_di_types.append(try o.lowerDebugType(ptr_ty, .full)); + debug_param_types.appendAssumeCapacity(try o.lowerDebugType(ptr_ty)); } for (0..fn_info.param_types.len) |i| { @@ -2825,20 +2781,18 @@ pub const Object = struct { if (isByRef(param_ty, mod)) { const ptr_ty = try mod.singleMutPtrType(param_ty); - try param_di_types.append(try o.lowerDebugType(ptr_ty, .full)); + debug_param_types.appendAssumeCapacity(try o.lowerDebugType(ptr_ty)); } else { - try param_di_types.append(try o.lowerDebugType(param_ty, .full)); + debug_param_types.appendAssumeCapacity(try o.lowerDebugType(param_ty)); } } - const fn_di_ty = dib.createSubroutineType( - param_di_types.items.ptr, - @intCast(param_di_types.items.len), - 0, + const debug_function_type = try o.builder.debugSubroutineType( + try o.builder.debugTuple(debug_param_types.items), ); - // The recursive call to `lowerDebugType` means we can't use `gop` anymore. - try o.di_type_map.put(gpa, ty.toIntern(), AnnotatedDITypePtr.initFull(fn_di_ty)); - return fn_di_ty; + + try o.debug_type_map.put(gpa, ty, debug_function_type); + return debug_function_type; }, .ComptimeInt => unreachable, .ComptimeFloat => unreachable, @@ -2852,39 +2806,30 @@ pub const Object = struct { } } - fn namespaceToDebugScope(o: *Object, namespace_index: InternPool.NamespaceIndex) !*llvm.DIScope { + fn namespaceToDebugScope(o: *Object, namespace_index: InternPool.NamespaceIndex) !Builder.Metadata { const mod = o.module; const namespace = mod.namespacePtr(namespace_index); - if (namespace.parent == .none) { - const di_file = try o.getDIFile(o.gpa, namespace.file_scope); - return di_file.toScope(); - } - const di_type = try o.lowerDebugType(namespace.ty, .fwd); - return di_type.toScope(); + if (namespace.parent == .none) return try o.getDebugFile(namespace.file_scope); + + const gop = try o.debug_unresolved_namespace_scopes.getOrPut(o.gpa, namespace_index); + + if (!gop.found_existing) gop.value_ptr.* = try o.builder.debugForwardReference(); + + return gop.value_ptr.*; } - /// This is to be used instead of void for debug info types, to avoid tripping - /// Assertion `!isa<DIType>(Scope) && "shouldn't make a namespace scope for a type"' - /// when targeting CodeView (Windows). - fn makeEmptyNamespaceDIType(o: *Object, decl_index: InternPool.DeclIndex) !*llvm.DIType { + fn makeEmptyNamespaceDebugType(o: *Object, decl_index: InternPool.DeclIndex) !Builder.Metadata { const mod = o.module; const decl = mod.declPtr(decl_index); - const fields: [0]*llvm.DIType = .{}; - const di_scope = try o.namespaceToDebugScope(decl.src_namespace); - return o.di_builder.?.createStructType( - di_scope, - mod.intern_pool.stringToSlice(decl.name), // TODO use fully qualified name - try o.getDIFile(o.gpa, mod.namespacePtr(decl.src_namespace).file_scope), + return o.builder.debugStructType( + try o.builder.metadataString(mod.intern_pool.stringToSlice(decl.name)), // TODO use fully qualified name + try o.getDebugFile(mod.namespacePtr(decl.src_namespace).file_scope), + try o.namespaceToDebugScope(decl.src_namespace), decl.src_line + 1, - 0, // size in bits - 0, // align in bits - 0, // flags - null, // derived from - undefined, // TODO should be able to pass &fields, - fields.len, - 0, // run time lang - null, // vtable holder - "", // unique id + .none, + 0, + 0, + .none, ); } @@ -2896,15 +2841,13 @@ pub const Object = struct { const builtin_str = try mod.intern_pool.getOrPutString(mod.gpa, "builtin"); const std_namespace = mod.namespacePtr(mod.declPtr(std_file.root_decl.unwrap().?).src_namespace); - const builtin_decl = std_namespace.decls - .getKeyAdapted(builtin_str, Module.DeclAdapter{ .mod = mod }).?; + const builtin_decl = std_namespace.decls.getKeyAdapted(builtin_str, Module.DeclAdapter{ .zcu = mod }).?; const stack_trace_str = try mod.intern_pool.getOrPutString(mod.gpa, "StackTrace"); // buffer is only used for int_type, `builtin` is a struct. const builtin_ty = mod.declPtr(builtin_decl).val.toType(); const builtin_namespace = builtin_ty.getNamespace(mod).?; - const stack_trace_decl_index = builtin_namespace.decls - .getKeyAdapted(stack_trace_str, Module.DeclAdapter{ .mod = mod }).?; + const stack_trace_decl_index = builtin_namespace.decls.getKeyAdapted(stack_trace_str, Module.DeclAdapter{ .zcu = mod }).?; const stack_trace_decl = mod.declPtr(stack_trace_decl_index); // Sema should have ensured that StackTrace was analyzed. @@ -2947,7 +2890,7 @@ pub const Object = struct { try o.builder.string(ip.stringToSlice(if (is_extern) decl.name else - try decl.getFullyQualifiedName(zcu))), + try decl.fullyQualifiedName(zcu))), toLlvmAddressSpace(decl.@"addrspace", target), ); gop.value_ptr.* = function_index.ptrConst(&o.builder).global; @@ -3161,7 +3104,7 @@ pub const Object = struct { const variable_index = try o.builder.addVariable( try o.builder.string(mod.intern_pool.stringToSlice( - if (is_extern) decl.name else try decl.getFullyQualifiedName(mod), + if (is_extern) decl.name else try decl.fullyQualifiedName(mod), )), try o.lowerType(decl.ty), toLlvmGlobalAddressSpace(decl.@"addrspace", mod.getTarget()), @@ -3193,26 +3136,6 @@ pub const Object = struct { } fn lowerType(o: *Object, t: Type) Allocator.Error!Builder.Type { - const ty = try o.lowerTypeInner(t); - const mod = o.module; - if (std.debug.runtime_safety and o.builder.useLibLlvm() and false) check: { - const llvm_ty = ty.toLlvm(&o.builder); - if (t.zigTypeTag(mod) == .Opaque) break :check; - if (!t.hasRuntimeBits(mod)) break :check; - if (!try ty.isSized(&o.builder)) break :check; - - const zig_size = t.abiSize(mod); - const llvm_size = o.target_data.abiSizeOfType(llvm_ty); - if (llvm_size != zig_size) { - log.err("when lowering {}, Zig ABI size = {d} but LLVM ABI size = {d}", .{ - t.fmt(o.module), zig_size, llvm_size, - }); - } - } - return ty; - } - - fn lowerTypeInner(o: *Object, t: Type) Allocator.Error!Builder.Type { const mod = o.module; const target = mod.getTarget(); const ip = &mod.intern_pool; @@ -3397,20 +3320,17 @@ pub const Object = struct { }, .simple_type => unreachable, .struct_type => |struct_type| { - const gop = try o.type_map.getOrPut(o.gpa, t.toIntern()); - if (gop.found_existing) return gop.value_ptr.*; + if (o.type_map.get(t.toIntern())) |value| return value; if (struct_type.layout == .Packed) { const int_ty = try o.lowerType(Type.fromInterned(struct_type.backingIntType(ip).*)); - gop.value_ptr.* = int_ty; + try o.type_map.put(o.gpa, t.toIntern(), int_ty); return int_ty; } const name = try o.builder.string(ip.stringToSlice( - try mod.declPtr(struct_type.decl.unwrap().?).getFullyQualifiedName(mod), + try mod.declPtr(struct_type.decl.unwrap().?).fullyQualifiedName(mod), )); - const ty = try o.builder.opaqueType(name); - gop.value_ptr.* = ty; // must be done before any recursive calls var llvm_field_types = std.ArrayListUnmanaged(Builder.Type){}; defer llvm_field_types.deinit(o.gpa); @@ -3475,7 +3395,10 @@ pub const Object = struct { ); } - try o.builder.namedTypeSetBody( + const ty = try o.builder.opaqueType(name); + try o.type_map.put(o.gpa, t.toIntern(), ty); + + o.builder.namedTypeSetBody( ty, try o.builder.structType(struct_kind, llvm_field_types.items), ); @@ -3544,29 +3467,26 @@ pub const Object = struct { return o.builder.structType(.normal, llvm_field_types.items); }, .union_type => |union_type| { - const gop = try o.type_map.getOrPut(o.gpa, t.toIntern()); - if (gop.found_existing) return gop.value_ptr.*; + if (o.type_map.get(t.toIntern())) |value| return value; const union_obj = ip.loadUnionType(union_type); const layout = mod.getUnionLayout(union_obj); if (union_obj.flagsPtr(ip).layout == .Packed) { const int_ty = try o.builder.intType(@intCast(t.bitSize(mod))); - gop.value_ptr.* = int_ty; + try o.type_map.put(o.gpa, t.toIntern(), int_ty); return int_ty; } if (layout.payload_size == 0) { const enum_tag_ty = try o.lowerType(Type.fromInterned(union_obj.enum_tag_ty)); - gop.value_ptr.* = enum_tag_ty; + try o.type_map.put(o.gpa, t.toIntern(), enum_tag_ty); return enum_tag_ty; } const name = try o.builder.string(ip.stringToSlice( - try mod.declPtr(union_obj.decl).getFullyQualifiedName(mod), + try mod.declPtr(union_obj.decl).fullyQualifiedName(mod), )); - const ty = try o.builder.opaqueType(name); - gop.value_ptr.* = ty; // must be done before any recursive calls const aligned_field_ty = Type.fromInterned(union_obj.field_types.get(ip)[layout.most_aligned_field]); const aligned_field_llvm_ty = try o.lowerType(aligned_field_ty); @@ -3586,7 +3506,10 @@ pub const Object = struct { }; if (layout.tag_size == 0) { - try o.builder.namedTypeSetBody( + const ty = try o.builder.opaqueType(name); + try o.type_map.put(o.gpa, t.toIntern(), ty); + + o.builder.namedTypeSetBody( ty, try o.builder.structType(.normal, &.{payload_ty}), ); @@ -3611,7 +3534,10 @@ pub const Object = struct { llvm_fields_len += 1; } - try o.builder.namedTypeSetBody( + const ty = try o.builder.opaqueType(name); + try o.type_map.put(o.gpa, t.toIntern(), ty); + + o.builder.namedTypeSetBody( ty, try o.builder.structType(.normal, llvm_fields[0..llvm_fields_len]), ); @@ -4359,7 +4285,7 @@ pub const Object = struct { const err_align = err_int_ty.abiAlignment(mod); const index: u32 = if (payload_align.compare(.gt, err_align)) 2 else 1; return o.builder.gepConst(.inbounds, try o.lowerType(eu_ty), parent_ptr, null, &.{ - try o.builder.intConst(.i32, 0), try o.builder.intConst(.i32, index), + .@"0", try o.builder.intConst(.i32, index), }); }, .opt_payload => |opt_ptr| { @@ -4375,9 +4301,7 @@ pub const Object = struct { return parent_ptr; } - return o.builder.gepConst(.inbounds, try o.lowerType(opt_ty), parent_ptr, null, &.{ - try o.builder.intConst(.i32, 0), try o.builder.intConst(.i32, 0), - }); + return o.builder.gepConst(.inbounds, try o.lowerType(opt_ty), parent_ptr, null, &.{ .@"0", .@"0" }); }, .comptime_field => unreachable, .elem => |elem_ptr| { @@ -4408,7 +4332,7 @@ pub const Object = struct { const parent_llvm_ty = try o.lowerType(parent_ty); return o.builder.gepConst(.inbounds, parent_llvm_ty, parent_ptr, null, &.{ - try o.builder.intConst(.i32, 0), + .@"0", try o.builder.intConst(.i32, @intFromBool( layout.tag_size > 0 and layout.tag_align.compare(.gte, layout.payload_align), )), @@ -4434,7 +4358,7 @@ pub const Object = struct { parent_ptr, null, if (o.llvmFieldIndex(parent_ty, field_index)) |llvm_field_index| &.{ - try o.builder.intConst(.i32, 0), + .@"0", try o.builder.intConst(.i32, llvm_field_index), } else &.{ try o.builder.intConst(.i32, @intFromBool( @@ -4447,7 +4371,7 @@ pub const Object = struct { assert(parent_ty.isSlice(mod)); const parent_llvm_ty = try o.lowerType(parent_ty); return o.builder.gepConst(.inbounds, parent_llvm_ty, parent_ptr, null, &.{ - try o.builder.intConst(.i32, 0), try o.builder.intConst(.i32, field_index), + .@"0", try o.builder.intConst(.i32, field_index), }); }, else => unreachable, @@ -4679,7 +4603,7 @@ pub const Object = struct { const usize_ty = try o.lowerType(Type.usize); const ret_ty = try o.lowerType(Type.slice_const_u8_sentinel_0); - const fqn = try zcu.declPtr(enum_type.decl).getFullyQualifiedName(zcu); + const fqn = try zcu.declPtr(enum_type.decl).fullyQualifiedName(zcu); const target = zcu.root_mod.resolved_target.result; const function_index = try o.builder.addFunction( try o.builder.fnType(ret_ty, &.{try o.lowerType(Type.fromInterned(enum_type.tag_ty))}, .normal), @@ -4696,7 +4620,10 @@ pub const Object = struct { function_index.setAttributes(try attributes.finish(&o.builder), &o.builder); gop.value_ptr.* = function_index.ptrConst(&o.builder).global; - var wip = try Builder.WipFunction.init(&o.builder, function_index); + var wip = try Builder.WipFunction.init(&o.builder, .{ + .function = function_index, + .strip = true, + }); defer wip.deinit(); wip.cursor = .{ .block = try wip.block(0, "Entry") }; @@ -4707,8 +4634,8 @@ pub const Object = struct { defer wip_switch.finish(&wip); for (0..enum_type.names.len) |field_index| { - const name = try o.builder.string(ip.stringToSlice(enum_type.names.get(ip)[field_index])); - const name_init = try o.builder.stringNullConst(name); + const name = try o.builder.stringNull(ip.stringToSlice(enum_type.names.get(ip)[field_index])); + const name_init = try o.builder.stringConst(name); const name_variable_index = try o.builder.addVariable(.empty, name_init.typeOf(&o.builder), .default); try name_variable_index.setInitializer(name_init, &o.builder); @@ -4719,7 +4646,7 @@ pub const Object = struct { const name_val = try o.builder.structValue(ret_ty, &.{ name_variable_index.toConst(&o.builder), - try o.builder.intConst(usize_ty, name.slice(&o.builder).?.len), + try o.builder.intConst(usize_ty, name.slice(&o.builder).?.len - 1), }); const return_block = try wip.block(1, "Name"); @@ -4766,23 +4693,23 @@ pub const DeclGen = struct { fn genDecl(dg: *DeclGen) !void { const o = dg.object; - const mod = o.module; + const zcu = o.module; const decl = dg.decl; const decl_index = dg.decl_index; assert(decl.has_tv); - if (decl.val.getExternFunc(mod)) |extern_func| { + if (decl.val.getExternFunc(zcu)) |extern_func| { _ = try o.resolveLlvmFunction(extern_func.decl); } else { const variable_index = try o.resolveGlobalDecl(decl_index); variable_index.setAlignment( - decl.getAlignment(mod).toLlvm(), + decl.getAlignment(zcu).toLlvm(), &o.builder, ); - if (mod.intern_pool.stringToSliceUnwrap(decl.@"linksection")) |section| + if (zcu.intern_pool.stringToSliceUnwrap(decl.@"linksection")) |section| variable_index.setSection(try o.builder.string(section), &o.builder); assert(decl.has_tv); - const init_val = if (decl.val.getVariable(mod)) |decl_var| decl_var.init else init_val: { + const init_val = if (decl.val.getVariable(zcu)) |decl_var| decl_var.init else init_val: { variable_index.setMutability(.constant, &o.builder); break :init_val decl.val.toIntern(); }; @@ -4791,26 +4718,36 @@ pub const DeclGen = struct { else => try o.lowerValue(init_val), }, &o.builder); - if (o.di_builder) |dib| { - const di_file = - try o.getDIFile(o.gpa, mod.namespacePtr(decl.src_namespace).file_scope); - - const line_number = decl.src_line + 1; - const is_internal_linkage = !o.module.decl_exports.contains(decl_index); - const di_global = dib.createGlobalVariableExpression( - di_file.toScope(), - mod.intern_pool.stringToSlice(decl.name), - variable_index.name(&o.builder).slice(&o.builder).?, - di_file, - line_number, - try o.lowerDebugType(decl.ty, .full), - is_internal_linkage, - ); + const line_number = decl.src_line + 1; + const is_internal_linkage = !o.module.decl_exports.contains(decl_index); - try o.di_map.put(o.gpa, dg.decl, di_global.getVariable().toNode()); - if (!is_internal_linkage or decl.isExtern(mod)) - variable_index.toLlvm(&o.builder).attachMetaData(di_global); - } + const namespace = zcu.namespacePtr(decl.src_namespace); + const owner_mod = namespace.file_scope.mod; + + if (owner_mod.strip) return; + + const debug_file = try o.getDebugFile(namespace.file_scope); + + const debug_global_var = try o.builder.debugGlobalVar( + try o.builder.metadataString(zcu.intern_pool.stringToSlice(decl.name)), // Name + try o.builder.metadataStringFromString(variable_index.name(&o.builder)), // Linkage name + debug_file, // File + debug_file, // Scope + line_number, + try o.lowerDebugType(decl.ty), + variable_index, + .{ .local = is_internal_linkage }, + ); + + const debug_expression = try o.builder.debugExpression(&.{}); + + const debug_global_var_expression = try o.builder.debugGlobalVarExpression( + debug_global_var, + debug_expression, + ); + if (!is_internal_linkage or decl.isExtern(zcu)) + variable_index.setGlobalVariableExpression(debug_global_var_expression, &o.builder); + try o.debug_globals.append(o.gpa, debug_global_var_expression); } } }; @@ -4821,19 +4758,22 @@ pub const FuncGen = struct { air: Air, liveness: Liveness, wip: Builder.WipFunction, - di_scope: ?if (build_options.have_llvm) *llvm.DIScope else noreturn, - di_file: ?if (build_options.have_llvm) *llvm.DIFile else noreturn, + + file: Builder.Metadata, + scope: Builder.Metadata, + + inlined: std.ArrayListUnmanaged(struct { + base_line: u32, + location: Builder.DebugLocation, + scope: Builder.Metadata, + }) = .{}, + + scope_stack: std.ArrayListUnmanaged(Builder.Metadata) = .{}, + base_line: u32, prev_dbg_line: c_uint, prev_dbg_column: c_uint, - /// Stack of locations where a call was inlined. - dbg_inlined: std.ArrayListUnmanaged(if (build_options.have_llvm) DbgState else void) = .{}, - - /// Stack of `DILexicalBlock`s. dbg_block instructions cannot happend accross - /// dbg_inline instructions so no special handling there is required. - dbg_block_stack: std.ArrayListUnmanaged(if (build_options.have_llvm) *llvm.DIScope else void) = .{}, - /// This stores the LLVM values used in a function, such that they can be referred to /// in other instructions. This table is cleared before every function is generated. func_inst_table: std.AutoHashMapUnmanaged(Air.Inst.Ref, Builder.Value), @@ -4863,7 +4803,6 @@ pub const FuncGen = struct { sync_scope: Builder.SyncScope, - const DbgState = if (build_options.have_llvm) struct { loc: *llvm.DILocation, scope: *llvm.DIScope, base_line: u32 } else struct {}; const BreakList = union { list: std.MultiArrayList(struct { bb: Builder.Function.Block.Index, @@ -4874,8 +4813,8 @@ pub const FuncGen = struct { fn deinit(self: *FuncGen) void { self.wip.deinit(); - self.dbg_inlined.deinit(self.gpa); - self.dbg_block_stack.deinit(self.gpa); + self.scope_stack.deinit(self.gpa); + self.inlined.deinit(self.gpa); self.func_inst_table.deinit(self.gpa); self.blocks.deinit(self.gpa); } @@ -5484,9 +5423,6 @@ pub const FuncGen = struct { // a different LLVM type than the usual one. We solve this here at the callsite // by using our canonical type, then loading it if necessary. const alignment = return_type.abiAlignment(mod).toLlvm(); - if (o.builder.useLibLlvm()) - assert(o.target_data.abiSizeOfType(abi_ret_ty.toLlvm(&o.builder)) >= - o.target_data.abiSizeOfType(llvm_ret_ty.toLlvm(&o.builder))); const rp = try self.buildAlloca(abi_ret_ty, alignment); _ = try self.wip.store(.normal, call, rp, alignment); return if (isByRef(return_type, mod)) @@ -5853,7 +5789,7 @@ pub const FuncGen = struct { }; const phi = try self.wip.phi(.i1, ""); - try phi.finish( + phi.finish( &incoming_values, &.{ both_null_block, mixed_block, both_pl_block_end }, &self.wip, @@ -5920,7 +5856,7 @@ pub const FuncGen = struct { parent_bb.ptr(&self.wip).incoming = @intCast(breaks.list.len); const phi = try self.wip.phi(llvm_ty, ""); - try phi.finish(breaks.list.items(.val), breaks.list.items(.bb), &self.wip); + phi.finish(breaks.list.items(.val), breaks.list.items(.bb), &self.wip); return phi.toValue(); } else { parent_bb.ptr(&self.wip).incoming = @intCast(breaks.len); @@ -6644,137 +6580,163 @@ pub const FuncGen = struct { } fn airDbgStmt(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { - const di_scope = self.di_scope orelse return .none; const dbg_stmt = self.air.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt; self.prev_dbg_line = @intCast(self.base_line + dbg_stmt.line + 1); self.prev_dbg_column = @intCast(dbg_stmt.column + 1); - const inlined_at = if (self.dbg_inlined.items.len > 0) - self.dbg_inlined.items[self.dbg_inlined.items.len - 1].loc + + const inlined_at_location = if (self.inlined.getLastOrNull()) |inlined| + try inlined.location.toMetadata(self.wip.builder) else - null; - self.wip.llvm.builder.setCurrentDebugLocation( - self.prev_dbg_line, - self.prev_dbg_column, - di_scope, - inlined_at, - ); + .none; + + self.wip.debug_location = .{ + .location = .{ + .line = self.prev_dbg_line, + .column = self.prev_dbg_column, + .scope = self.scope, + .inlined_at = inlined_at_location, + }, + }; + return .none; } fn airDbgInlineBegin(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { const o = self.dg.object; - const dib = o.di_builder orelse return .none; - const ty_fn = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_fn; - const zcu = o.module; + + const ty_fn = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_fn; const func = zcu.funcInfo(ty_fn.func); const decl_index = func.owner_decl; const decl = zcu.declPtr(decl_index); const namespace = zcu.namespacePtr(decl.src_namespace); const owner_mod = namespace.file_scope.mod; - const di_file = try o.getDIFile(self.gpa, zcu.namespacePtr(decl.src_namespace).file_scope); - self.di_file = di_file; - const line_number = decl.src_line + 1; - const cur_debug_location = self.wip.llvm.builder.getCurrentDebugLocation2(); - try self.dbg_inlined.append(self.gpa, .{ - .loc = @ptrCast(cur_debug_location), - .scope = self.di_scope.?, + self.file = try o.getDebugFile(namespace.file_scope); + + const line_number = decl.src_line + 1; + try self.inlined.append(self.gpa, .{ + .location = self.wip.debug_location, + .scope = self.scope, .base_line = self.base_line, }); - const fqn = try decl.getFullyQualifiedName(zcu); + const fqn = try decl.fullyQualifiedName(zcu); const is_internal_linkage = !zcu.decl_exports.contains(decl_index); const fn_ty = try zcu.funcType(.{ .param_types = &.{}, .return_type = .void_type, }); - const fn_di_ty = try o.lowerDebugType(fn_ty, .full); - const subprogram = dib.createFunction( - di_file.toScope(), - zcu.intern_pool.stringToSlice(decl.name), - zcu.intern_pool.stringToSlice(fqn), - di_file, + + const subprogram = try o.builder.debugSubprogram( + self.file, + try o.builder.metadataString(zcu.intern_pool.stringToSlice(decl.name)), + try o.builder.metadataString(zcu.intern_pool.stringToSlice(fqn)), line_number, - fn_di_ty, - is_internal_linkage, - true, // is definition - line_number + func.lbrace_line, // scope line - llvm.DIFlags.StaticMember, - owner_mod.optimize_mode != .Debug, - null, // decl_subprogram + line_number + func.lbrace_line, + try o.lowerDebugType(fn_ty), + .{ + .di_flags = .{ .StaticMember = true }, + .sp_flags = .{ + .Optimized = owner_mod.optimize_mode != .Debug, + .Definition = true, + .LocalToUnit = is_internal_linkage, + }, + }, + o.debug_compile_unit, ); - const lexical_block = dib.createLexicalBlock(subprogram.toScope(), di_file, line_number, 1); - self.di_scope = lexical_block.toScope(); + const lexical_block = try o.builder.debugLexicalBlock( + subprogram, + self.file, + line_number, + 1, + ); + self.scope = lexical_block; self.base_line = decl.src_line; + const inlined_at_location = try self.wip.debug_location.toMetadata(&o.builder); + self.wip.debug_location = .{ + .location = .{ + .line = line_number, + .column = 0, + .scope = self.scope, + .inlined_at = inlined_at_location, + }, + }; return .none; } - fn airDbgInlineEnd(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { + fn airDbgInlineEnd(self: *FuncGen, inst: Air.Inst.Index) Allocator.Error!Builder.Value { const o = self.dg.object; - if (o.di_builder == null) return .none; + const ty_fn = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_fn; const mod = o.module; const decl = mod.funcOwnerDeclPtr(ty_fn.func); - const di_file = try o.getDIFile(self.gpa, mod.namespacePtr(decl.src_namespace).file_scope); - self.di_file = di_file; - const old = self.dbg_inlined.pop(); - self.di_scope = old.scope; + self.file = try o.getDebugFile(mod.namespacePtr(decl.src_namespace).file_scope); + + const old = self.inlined.pop(); + self.scope = old.scope; self.base_line = old.base_line; + self.wip.debug_location = old.location; return .none; } - fn airDbgBlockBegin(self: *FuncGen) !Builder.Value { + fn airDbgBlockBegin(self: *FuncGen) Allocator.Error!Builder.Value { const o = self.dg.object; - const dib = o.di_builder orelse return .none; - const old_scope = self.di_scope.?; - try self.dbg_block_stack.append(self.gpa, old_scope); - const lexical_block = dib.createLexicalBlock(old_scope, self.di_file.?, self.prev_dbg_line, self.prev_dbg_column); - self.di_scope = lexical_block.toScope(); + + try self.scope_stack.append(self.gpa, self.scope); + + const old = self.scope; + self.scope = try o.builder.debugLexicalBlock( + old, + self.file, + self.prev_dbg_line, + self.prev_dbg_column, + ); return .none; } fn airDbgBlockEnd(self: *FuncGen) !Builder.Value { - const o = self.dg.object; - if (o.di_builder == null) return .none; - self.di_scope = self.dbg_block_stack.pop(); + self.scope = self.scope_stack.pop(); return .none; } fn airDbgVarPtr(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { const o = self.dg.object; const mod = o.module; - const dib = o.di_builder orelse return .none; const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; const operand = try self.resolveInst(pl_op.operand); const name = self.air.nullTerminatedString(pl_op.payload); const ptr_ty = self.typeOf(pl_op.operand); - const di_local_var = dib.createAutoVariable( - self.di_scope.?, - name.ptr, - self.di_file.?, + const debug_local_var = try o.builder.debugLocalVar( + try o.builder.metadataString(name), + self.file, + self.scope, self.prev_dbg_line, - try o.lowerDebugType(ptr_ty.childType(mod), .full), - true, // always preserve - 0, // flags + try o.lowerDebugType(ptr_ty.childType(mod)), ); - const inlined_at = if (self.dbg_inlined.items.len > 0) - self.dbg_inlined.items[self.dbg_inlined.items.len - 1].loc - else - null; - const debug_loc = llvm.getDebugLoc(self.prev_dbg_line, self.prev_dbg_column, self.di_scope.?, inlined_at); - const insert_block = self.wip.cursor.block.toLlvm(&self.wip); - _ = dib.insertDeclareAtEnd(operand.toLlvm(&self.wip), di_local_var, debug_loc, insert_block); + + _ = try self.wip.callIntrinsic( + .normal, + .none, + .@"dbg.declare", + &.{}, + &.{ + (try self.wip.debugValue(operand)).toValue(), + debug_local_var.toValue(), + (try o.builder.debugExpression(&.{})).toValue(), + }, + "", + ); + return .none; } fn airDbgVarVal(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { const o = self.dg.object; - const dib = o.di_builder orelse return .none; const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; const operand = try self.resolveInst(pl_op.operand); const operand_ty = self.typeOf(pl_op.operand); @@ -6782,32 +6744,58 @@ pub const FuncGen = struct { if (needDbgVarWorkaround(o)) return .none; - const di_local_var = dib.createAutoVariable( - self.di_scope.?, - name.ptr, - self.di_file.?, + const debug_local_var = try o.builder.debugLocalVar( + try o.builder.metadataString(name), + self.file, + self.scope, self.prev_dbg_line, - try o.lowerDebugType(operand_ty, .full), - true, // always preserve - 0, // flags + try o.lowerDebugType(operand_ty), ); - const inlined_at = if (self.dbg_inlined.items.len > 0) - self.dbg_inlined.items[self.dbg_inlined.items.len - 1].loc - else - null; - const debug_loc = llvm.getDebugLoc(self.prev_dbg_line, self.prev_dbg_column, self.di_scope.?, inlined_at); - const insert_block = self.wip.cursor.block.toLlvm(&self.wip); + const zcu = o.module; const owner_mod = self.dg.ownerModule(); if (isByRef(operand_ty, zcu)) { - _ = dib.insertDeclareAtEnd(operand.toLlvm(&self.wip), di_local_var, debug_loc, insert_block); + _ = try self.wip.callIntrinsic( + .normal, + .none, + .@"dbg.declare", + &.{}, + &.{ + (try self.wip.debugValue(operand)).toValue(), + debug_local_var.toValue(), + (try o.builder.debugExpression(&.{})).toValue(), + }, + "", + ); } else if (owner_mod.optimize_mode == .Debug) { const alignment = operand_ty.abiAlignment(zcu).toLlvm(); const alloca = try self.buildAlloca(operand.typeOfWip(&self.wip), alignment); _ = try self.wip.store(.normal, operand, alloca, alignment); - _ = dib.insertDeclareAtEnd(alloca.toLlvm(&self.wip), di_local_var, debug_loc, insert_block); + _ = try self.wip.callIntrinsic( + .normal, + .none, + .@"dbg.declare", + &.{}, + &.{ + (try self.wip.debugValue(alloca)).toValue(), + debug_local_var.toValue(), + (try o.builder.debugExpression(&.{})).toValue(), + }, + "", + ); } else { - _ = dib.insertDbgValueIntrinsicAtEnd(operand.toLlvm(&self.wip), di_local_var, debug_loc, insert_block); + _ = try self.wip.callIntrinsic( + .normal, + .none, + .@"dbg.value", + &.{}, + &.{ + (try self.wip.debugValue(operand)).toValue(), + debug_local_var.toValue(), + (try o.builder.debugExpression(&.{})).toValue(), + }, + "", + ); } return .none; } @@ -7876,7 +7864,7 @@ pub const FuncGen = struct { .none, if (scalar_ty.isSignedInt(mod)) .@"smul.fix.sat" else .@"umul.fix.sat", &.{try o.lowerType(inst_ty)}, - &.{ lhs, rhs, try o.builder.intValue(.i32, 0) }, + &.{ lhs, rhs, .@"0" }, "", ); } @@ -8199,7 +8187,6 @@ pub const FuncGen = struct { const libc_fn = try self.getLibcFunction(fn_name, &.{ scalar_llvm_ty, scalar_llvm_ty }, .i32); - const zero = try o.builder.intConst(.i32, 0); const int_cond: Builder.IntegerCondition = switch (pred) { .eq => .eq, .neq => .ne, @@ -8216,7 +8203,7 @@ pub const FuncGen = struct { const init = try o.builder.poisonValue(vector_result_ty); const result = try self.buildElementwiseCall(libc_fn, ¶ms, init, vec_len); - const zero_vector = try o.builder.splatValue(vector_result_ty, zero); + const zero_vector = try o.builder.splatValue(vector_result_ty, .@"0"); return self.wip.icmp(int_cond, result, zero_vector, ""); } @@ -8229,7 +8216,7 @@ pub const FuncGen = struct { ¶ms, "", ); - return self.wip.icmp(int_cond, result, zero.toValue(), ""); + return self.wip.icmp(int_cond, result, .@"0", ""); } const FloatOp = enum { @@ -8664,8 +8651,6 @@ pub const FuncGen = struct { const operand_ty = self.typeOf(ty_op.operand); const dest_ty = self.typeOfIndex(inst); const target = mod.getTarget(); - const dest_bits = dest_ty.floatBits(target); - const src_bits = operand_ty.floatBits(target); if (intrinsicsAllowed(dest_ty, target) and intrinsicsAllowed(operand_ty, target)) { return self.wip.cast(.fpext, operand, try o.lowerType(dest_ty), ""); @@ -8673,11 +8658,19 @@ pub const FuncGen = struct { const operand_llvm_ty = try o.lowerType(operand_ty); const dest_llvm_ty = try o.lowerType(dest_ty); + const dest_bits = dest_ty.scalarType(mod).floatBits(target); + const src_bits = operand_ty.scalarType(mod).floatBits(target); const fn_name = try o.builder.fmt("__extend{s}f{s}f2", .{ compilerRtFloatAbbrev(src_bits), compilerRtFloatAbbrev(dest_bits), }); const libc_fn = try self.getLibcFunction(fn_name, &.{operand_llvm_ty}, dest_llvm_ty); + if (dest_ty.isVector(mod)) return self.buildElementwiseCall( + libc_fn, + &.{operand}, + try o.builder.poisonValue(dest_llvm_ty), + dest_ty.vectorLen(mod), + ); return self.wip.call( .normal, .ccc, @@ -8829,41 +8822,82 @@ pub const FuncGen = struct { const arg_val = self.args[self.arg_index]; self.arg_index += 1; + if (self.wip.strip) return arg_val; + const inst_ty = self.typeOfIndex(inst); - if (o.di_builder) |dib| { - if (needDbgVarWorkaround(o)) return arg_val; - - const src_index = self.air.instructions.items(.data)[@intFromEnum(inst)].arg.src_index; - const func_index = self.dg.decl.getOwnedFunctionIndex(); - const func = mod.funcInfo(func_index); - const lbrace_line = mod.declPtr(func.owner_decl).src_line + func.lbrace_line + 1; - const lbrace_col = func.lbrace_column + 1; - const di_local_var = dib.createParameterVariable( - self.di_scope.?, - mod.getParamName(func_index, src_index).ptr, // TODO test 0 bit args - self.di_file.?, - lbrace_line, - try o.lowerDebugType(inst_ty, .full), - true, // always preserve - 0, // flags - @intCast(self.arg_index), // includes +1 because 0 is return type - ); + if (needDbgVarWorkaround(o)) return arg_val; + + const src_index = self.air.instructions.items(.data)[@intFromEnum(inst)].arg.src_index; + const func_index = self.dg.decl.getOwnedFunctionIndex(); + const func = mod.funcInfo(func_index); + const lbrace_line = mod.declPtr(func.owner_decl).src_line + func.lbrace_line + 1; + const lbrace_col = func.lbrace_column + 1; + + const debug_parameter = try o.builder.debugParameter( + try o.builder.metadataString(mod.getParamName(func_index, src_index)), + self.file, + self.scope, + lbrace_line, + try o.lowerDebugType(inst_ty), + @intCast(self.arg_index), + ); - const debug_loc = llvm.getDebugLoc(lbrace_line, lbrace_col, self.di_scope.?, null); - const insert_block = self.wip.cursor.block.toLlvm(&self.wip); - const owner_mod = self.dg.ownerModule(); - if (isByRef(inst_ty, mod)) { - _ = dib.insertDeclareAtEnd(arg_val.toLlvm(&self.wip), di_local_var, debug_loc, insert_block); - } else if (owner_mod.optimize_mode == .Debug) { - const alignment = inst_ty.abiAlignment(mod).toLlvm(); - const alloca = try self.buildAlloca(arg_val.typeOfWip(&self.wip), alignment); - _ = try self.wip.store(.normal, arg_val, alloca, alignment); - _ = dib.insertDeclareAtEnd(alloca.toLlvm(&self.wip), di_local_var, debug_loc, insert_block); - } else { - _ = dib.insertDbgValueIntrinsicAtEnd(arg_val.toLlvm(&self.wip), di_local_var, debug_loc, insert_block); - } + const old_location = self.wip.debug_location; + self.wip.debug_location = .{ + .location = .{ + .line = lbrace_line, + .column = lbrace_col, + .scope = self.scope, + .inlined_at = .none, + }, + }; + + const owner_mod = self.dg.ownerModule(); + if (isByRef(inst_ty, mod)) { + _ = try self.wip.callIntrinsic( + .normal, + .none, + .@"dbg.declare", + &.{}, + &.{ + (try self.wip.debugValue(arg_val)).toValue(), + debug_parameter.toValue(), + (try o.builder.debugExpression(&.{})).toValue(), + }, + "", + ); + } else if (owner_mod.optimize_mode == .Debug) { + const alignment = inst_ty.abiAlignment(mod).toLlvm(); + const alloca = try self.buildAlloca(arg_val.typeOfWip(&self.wip), alignment); + _ = try self.wip.store(.normal, arg_val, alloca, alignment); + _ = try self.wip.callIntrinsic( + .normal, + .none, + .@"dbg.declare", + &.{}, + &.{ + (try self.wip.debugValue(alloca)).toValue(), + debug_parameter.toValue(), + (try o.builder.debugExpression(&.{})).toValue(), + }, + "", + ); + } else { + _ = try self.wip.callIntrinsic( + .normal, + .none, + .@"dbg.value", + &.{}, + &.{ + (try self.wip.debugValue(arg_val)).toValue(), + debug_parameter.toValue(), + (try o.builder.debugExpression(&.{})).toValue(), + }, + "", + ); } + self.wip.debug_location = old_location; return arg_val; } @@ -8901,7 +8935,7 @@ pub const FuncGen = struct { alignment: Builder.Alignment, ) Allocator.Error!Builder.Value { const target = self.dg.object.module.getTarget(); - return buildAllocaInner(&self.wip, self.di_scope != null, llvm_ty, alignment, target); + return buildAllocaInner(&self.wip, llvm_ty, alignment, target); } // Workaround for https://github.com/ziglang/zig/issues/16392 @@ -9016,18 +9050,14 @@ pub const FuncGen = struct { // https://github.com/ziglang/zig/issues/11946 return o.builder.intValue(llvm_usize, 0); } - const result = try self.wip.callIntrinsic(.normal, .none, .returnaddress, &.{}, &.{ - try o.builder.intValue(.i32, 0), - }, ""); + const result = try self.wip.callIntrinsic(.normal, .none, .returnaddress, &.{}, &.{.@"0"}, ""); return self.wip.cast(.ptrtoint, result, llvm_usize, ""); } fn airFrameAddress(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { _ = inst; const o = self.dg.object; - const result = try self.wip.callIntrinsic(.normal, .none, .frameaddress, &.{.ptr}, &.{ - try o.builder.intValue(.i32, 0), - }, ""); + const result = try self.wip.callIntrinsic(.normal, .none, .frameaddress, &.{.ptr}, &.{.@"0"}, ""); return self.wip.cast(.ptrtoint, result, try o.lowerType(Type.usize), ""); } @@ -9355,7 +9385,7 @@ pub const FuncGen = struct { _ = try self.wip.br(loop_block); self.wip.cursor = .{ .block = end_block }; - try it_ptr.finish(&.{ next_ptr, dest_ptr }, &.{ body_block, entry_block }, &self.wip); + it_ptr.finish(&.{ next_ptr, dest_ptr }, &.{ body_block, entry_block }, &self.wip); return .none; } @@ -9565,6 +9595,7 @@ pub const FuncGen = struct { fn airErrorSetHasValue(self: *FuncGen, inst: Air.Inst.Index) !Builder.Value { const o = self.dg.object; const mod = o.module; + const ip = &mod.intern_pool; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const operand = try self.resolveInst(ty_op.operand); const error_set_ty = ty_op.ty.toType(); @@ -9576,8 +9607,8 @@ pub const FuncGen = struct { var wip_switch = try self.wip.@"switch"(operand, invalid_block, @intCast(names.len)); defer wip_switch.finish(&self.wip); - for (names) |name| { - const err_int = mod.global_error_set.getIndex(name).?; + for (0..names.len) |name_index| { + const err_int = mod.global_error_set.getIndex(names.get(ip)[name_index]).?; const this_tag_int_value = try o.builder.intConst(try o.errorIntType(), err_int); try wip_switch.addCase(this_tag_int_value, valid_block, &self.wip); } @@ -9589,7 +9620,7 @@ pub const FuncGen = struct { self.wip.cursor = .{ .block = end_block }; const phi = try self.wip.phi(.i1, ""); - try phi.finish(&.{ .true, .false }, &.{ valid_block, invalid_block }, &self.wip); + phi.finish(&.{ .true, .false }, &.{ valid_block, invalid_block }, &self.wip); return phi.toValue(); } @@ -9621,7 +9652,7 @@ pub const FuncGen = struct { if (gop.found_existing) return gop.value_ptr.*; errdefer assert(o.named_enum_map.remove(enum_type.decl)); - const fqn = try zcu.declPtr(enum_type.decl).getFullyQualifiedName(zcu); + const fqn = try zcu.declPtr(enum_type.decl).fullyQualifiedName(zcu); const target = zcu.root_mod.resolved_target.result; const function_index = try o.builder.addFunction( try o.builder.fnType(.i1, &.{try o.lowerType(Type.fromInterned(enum_type.tag_ty))}, .normal), @@ -9638,7 +9669,10 @@ pub const FuncGen = struct { function_index.setAttributes(try attributes.finish(&o.builder), &o.builder); gop.value_ptr.* = function_index; - var wip = try Builder.WipFunction.init(&o.builder, function_index); + var wip = try Builder.WipFunction.init(&o.builder, .{ + .function = function_index, + .strip = true, + }); defer wip.deinit(); wip.cursor = .{ .block = try wip.block(0, "Entry") }; @@ -10110,7 +10144,6 @@ pub const FuncGen = struct { const field_align = mod.unionFieldNormalAlignment(union_obj, extra.field_index); const llvm_usize = try o.lowerType(Type.usize); const usize_zero = try o.builder.intValue(llvm_usize, 0); - const i32_zero = try o.builder.intValue(.i32, 0); const llvm_union_ty = t: { const payload_ty = p: { @@ -10149,7 +10182,7 @@ pub const FuncGen = struct { .flags = .{ .alignment = field_align }, }); if (layout.tag_size == 0) { - const indices = [3]Builder.Value{ usize_zero, i32_zero, i32_zero }; + const indices = [3]Builder.Value{ usize_zero, .@"0", .@"0" }; const len: usize = if (field_size == layout.payload_size) 2 else 3; const field_ptr = try self.wip.gep(.inbounds, llvm_union_ty, result_ptr, indices[0..len], ""); @@ -10159,11 +10192,9 @@ pub const FuncGen = struct { { const payload_index = @intFromBool(layout.tag_align.compare(.gte, layout.payload_align)); - const indices: [3]Builder.Value = - .{ usize_zero, try o.builder.intValue(.i32, payload_index), i32_zero }; + const indices: [3]Builder.Value = .{ usize_zero, try o.builder.intValue(.i32, payload_index), .@"0" }; const len: usize = if (field_size == layout.payload_size) 2 else 3; - const field_ptr = - try self.wip.gep(.inbounds, llvm_union_ty, result_ptr, indices[0..len], ""); + const field_ptr = try self.wip.gep(.inbounds, llvm_union_ty, result_ptr, indices[0..len], ""); try self.store(field_ptr, field_ptr_ty, llvm_payload, .none); } { @@ -10269,7 +10300,7 @@ pub const FuncGen = struct { const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; const dimension = pl_op.payload; - if (dimension >= 3) return o.builder.intValue(.i32, 1); + if (dimension >= 3) return .@"1"; // Fetch the dispatch pointer, which points to this structure: // https://github.com/RadeonOpenCompute/ROCR-Runtime/blob/adae6c61e10d371f7cbc3d0e94ae2c070cab18a4/src/inc/hsa.h#L2913 @@ -11139,6 +11170,9 @@ fn lowerSystemVFnRetTy(o: *Object, fn_info: InternPool.Key.FuncType) Allocator.E .memory => unreachable, // handled above .win_i128 => unreachable, // windows only .none => break, + .integer_per_element => { + @panic("TODO"); + }, } } const first_non_integer = std.mem.indexOfNone(x86_64_abi.Class, &classes, &.{.integer}); @@ -11417,6 +11451,9 @@ const ParamTypeIterator = struct { .memory => unreachable, // handled above .win_i128 => unreachable, // windows only .none => break, + .integer_per_element => { + @panic("TODO"); + }, } } const first_non_integer = std.mem.indexOfNone(x86_64_abi.Class, &classes, &.{.integer}); @@ -11427,6 +11464,12 @@ const ParamTypeIterator = struct { it.llvm_index += 1; return .abi_sized_int; } + if (it.llvm_index + types_index > 6) { + it.zig_index += 1; + it.llvm_index += 1; + it.byval_attr = true; + return .byref; + } switch (ip.indexToKey(ty.toIntern())) { .struct_type => |struct_type| { assert(struct_type.haveLayout(ip)); @@ -11672,39 +11715,6 @@ const struct_layout_version = 2; // https://github.com/llvm/llvm-project/issues/56585/ is fixed const optional_layout_version = 3; -/// We use the least significant bit of the pointer address to tell us -/// whether the type is fully resolved. Types that are only fwd declared -/// have the LSB flipped to a 1. -const AnnotatedDITypePtr = enum(usize) { - _, - - fn initFwd(di_type: *llvm.DIType) AnnotatedDITypePtr { - const addr = @intFromPtr(di_type); - assert(@as(u1, @truncate(addr)) == 0); - return @enumFromInt(addr | 1); - } - - fn initFull(di_type: *llvm.DIType) AnnotatedDITypePtr { - const addr = @intFromPtr(di_type); - return @enumFromInt(addr); - } - - fn init(di_type: *llvm.DIType, resolve: Object.DebugResolveStatus) AnnotatedDITypePtr { - const addr = @intFromPtr(di_type); - const bit = @intFromBool(resolve == .fwd); - return @enumFromInt(addr | bit); - } - - fn toDIType(self: AnnotatedDITypePtr) *llvm.DIType { - const fixed_addr = @intFromEnum(self) & ~@as(usize, 1); - return @ptrFromInt(fixed_addr); - } - - fn isFwdOnly(self: AnnotatedDITypePtr) bool { - return @as(u1, @truncate(@intFromEnum(self))) != 0; - } -}; - const lt_errors_fn_name = "__zig_lt_errors_len"; /// Without this workaround, LLVM crashes with "unknown codeview register H1" @@ -11728,7 +11738,6 @@ fn compilerRtIntBits(bits: u16) u16 { fn buildAllocaInner( wip: *Builder.WipFunction, - di_scope_non_null: bool, llvm_ty: Builder.Type, alignment: Builder.Alignment, target: std.Target, @@ -11737,19 +11746,15 @@ fn buildAllocaInner( const alloca = blk: { const prev_cursor = wip.cursor; - const prev_debug_location = if (wip.builder.useLibLlvm()) - wip.llvm.builder.getCurrentDebugLocation2() - else - undefined; + const prev_debug_location = wip.debug_location; defer { wip.cursor = prev_cursor; if (wip.cursor.block == .entry) wip.cursor.instruction += 1; - if (wip.builder.useLibLlvm() and di_scope_non_null) - wip.llvm.builder.setCurrentDebugLocation2(prev_debug_location); + wip.debug_location = prev_debug_location; } wip.cursor = .{ .block = .entry }; - if (wip.builder.useLibLlvm()) wip.llvm.builder.clearCurrentDebugLocation(); + wip.debug_location = .no_location; break :blk try wip.alloca(.normal, llvm_ty, .none, alignment, address_space, ""); }; @@ -11795,3 +11800,195 @@ fn constraintAllowsRegister(constraint: []const u8) bool { } } else return false; } + +pub fn initializeLLVMTarget(arch: std.Target.Cpu.Arch) void { + switch (arch) { + .aarch64, .aarch64_be, .aarch64_32 => { + llvm.LLVMInitializeAArch64Target(); + llvm.LLVMInitializeAArch64TargetInfo(); + llvm.LLVMInitializeAArch64TargetMC(); + llvm.LLVMInitializeAArch64AsmPrinter(); + llvm.LLVMInitializeAArch64AsmParser(); + }, + .amdgcn => { + llvm.LLVMInitializeAMDGPUTarget(); + llvm.LLVMInitializeAMDGPUTargetInfo(); + llvm.LLVMInitializeAMDGPUTargetMC(); + llvm.LLVMInitializeAMDGPUAsmPrinter(); + llvm.LLVMInitializeAMDGPUAsmParser(); + }, + .thumb, .thumbeb, .arm, .armeb => { + llvm.LLVMInitializeARMTarget(); + llvm.LLVMInitializeARMTargetInfo(); + llvm.LLVMInitializeARMTargetMC(); + llvm.LLVMInitializeARMAsmPrinter(); + llvm.LLVMInitializeARMAsmParser(); + }, + .avr => { + llvm.LLVMInitializeAVRTarget(); + llvm.LLVMInitializeAVRTargetInfo(); + llvm.LLVMInitializeAVRTargetMC(); + llvm.LLVMInitializeAVRAsmPrinter(); + llvm.LLVMInitializeAVRAsmParser(); + }, + .bpfel, .bpfeb => { + llvm.LLVMInitializeBPFTarget(); + llvm.LLVMInitializeBPFTargetInfo(); + llvm.LLVMInitializeBPFTargetMC(); + llvm.LLVMInitializeBPFAsmPrinter(); + llvm.LLVMInitializeBPFAsmParser(); + }, + .hexagon => { + llvm.LLVMInitializeHexagonTarget(); + llvm.LLVMInitializeHexagonTargetInfo(); + llvm.LLVMInitializeHexagonTargetMC(); + llvm.LLVMInitializeHexagonAsmPrinter(); + llvm.LLVMInitializeHexagonAsmParser(); + }, + .lanai => { + llvm.LLVMInitializeLanaiTarget(); + llvm.LLVMInitializeLanaiTargetInfo(); + llvm.LLVMInitializeLanaiTargetMC(); + llvm.LLVMInitializeLanaiAsmPrinter(); + llvm.LLVMInitializeLanaiAsmParser(); + }, + .mips, .mipsel, .mips64, .mips64el => { + llvm.LLVMInitializeMipsTarget(); + llvm.LLVMInitializeMipsTargetInfo(); + llvm.LLVMInitializeMipsTargetMC(); + llvm.LLVMInitializeMipsAsmPrinter(); + llvm.LLVMInitializeMipsAsmParser(); + }, + .msp430 => { + llvm.LLVMInitializeMSP430Target(); + llvm.LLVMInitializeMSP430TargetInfo(); + llvm.LLVMInitializeMSP430TargetMC(); + llvm.LLVMInitializeMSP430AsmPrinter(); + llvm.LLVMInitializeMSP430AsmParser(); + }, + .nvptx, .nvptx64 => { + llvm.LLVMInitializeNVPTXTarget(); + llvm.LLVMInitializeNVPTXTargetInfo(); + llvm.LLVMInitializeNVPTXTargetMC(); + llvm.LLVMInitializeNVPTXAsmPrinter(); + // There is no LLVMInitializeNVPTXAsmParser function available. + }, + .powerpc, .powerpcle, .powerpc64, .powerpc64le => { + llvm.LLVMInitializePowerPCTarget(); + llvm.LLVMInitializePowerPCTargetInfo(); + llvm.LLVMInitializePowerPCTargetMC(); + llvm.LLVMInitializePowerPCAsmPrinter(); + llvm.LLVMInitializePowerPCAsmParser(); + }, + .riscv32, .riscv64 => { + llvm.LLVMInitializeRISCVTarget(); + llvm.LLVMInitializeRISCVTargetInfo(); + llvm.LLVMInitializeRISCVTargetMC(); + llvm.LLVMInitializeRISCVAsmPrinter(); + llvm.LLVMInitializeRISCVAsmParser(); + }, + .sparc, .sparc64, .sparcel => { + llvm.LLVMInitializeSparcTarget(); + llvm.LLVMInitializeSparcTargetInfo(); + llvm.LLVMInitializeSparcTargetMC(); + llvm.LLVMInitializeSparcAsmPrinter(); + llvm.LLVMInitializeSparcAsmParser(); + }, + .s390x => { + llvm.LLVMInitializeSystemZTarget(); + llvm.LLVMInitializeSystemZTargetInfo(); + llvm.LLVMInitializeSystemZTargetMC(); + llvm.LLVMInitializeSystemZAsmPrinter(); + llvm.LLVMInitializeSystemZAsmParser(); + }, + .wasm32, .wasm64 => { + llvm.LLVMInitializeWebAssemblyTarget(); + llvm.LLVMInitializeWebAssemblyTargetInfo(); + llvm.LLVMInitializeWebAssemblyTargetMC(); + llvm.LLVMInitializeWebAssemblyAsmPrinter(); + llvm.LLVMInitializeWebAssemblyAsmParser(); + }, + .x86, .x86_64 => { + llvm.LLVMInitializeX86Target(); + llvm.LLVMInitializeX86TargetInfo(); + llvm.LLVMInitializeX86TargetMC(); + llvm.LLVMInitializeX86AsmPrinter(); + llvm.LLVMInitializeX86AsmParser(); + }, + .xtensa => { + if (build_options.llvm_has_xtensa) { + llvm.LLVMInitializeXtensaTarget(); + llvm.LLVMInitializeXtensaTargetInfo(); + llvm.LLVMInitializeXtensaTargetMC(); + // There is no LLVMInitializeXtensaAsmPrinter function. + llvm.LLVMInitializeXtensaAsmParser(); + } + }, + .xcore => { + llvm.LLVMInitializeXCoreTarget(); + llvm.LLVMInitializeXCoreTargetInfo(); + llvm.LLVMInitializeXCoreTargetMC(); + llvm.LLVMInitializeXCoreAsmPrinter(); + // There is no LLVMInitializeXCoreAsmParser function. + }, + .m68k => { + if (build_options.llvm_has_m68k) { + llvm.LLVMInitializeM68kTarget(); + llvm.LLVMInitializeM68kTargetInfo(); + llvm.LLVMInitializeM68kTargetMC(); + llvm.LLVMInitializeM68kAsmPrinter(); + llvm.LLVMInitializeM68kAsmParser(); + } + }, + .csky => { + if (build_options.llvm_has_csky) { + llvm.LLVMInitializeCSKYTarget(); + llvm.LLVMInitializeCSKYTargetInfo(); + llvm.LLVMInitializeCSKYTargetMC(); + // There is no LLVMInitializeCSKYAsmPrinter function. + llvm.LLVMInitializeCSKYAsmParser(); + } + }, + .ve => { + llvm.LLVMInitializeVETarget(); + llvm.LLVMInitializeVETargetInfo(); + llvm.LLVMInitializeVETargetMC(); + llvm.LLVMInitializeVEAsmPrinter(); + llvm.LLVMInitializeVEAsmParser(); + }, + .arc => { + if (build_options.llvm_has_arc) { + llvm.LLVMInitializeARCTarget(); + llvm.LLVMInitializeARCTargetInfo(); + llvm.LLVMInitializeARCTargetMC(); + llvm.LLVMInitializeARCAsmPrinter(); + // There is no LLVMInitializeARCAsmParser function. + } + }, + + // LLVM backends that have no initialization functions. + .tce, + .tcele, + .r600, + .le32, + .le64, + .amdil, + .amdil64, + .hsail, + .hsail64, + .shave, + .spir, + .spir64, + .kalimba, + .renderscript32, + .renderscript64, + .dxil, + .loongarch32, + .loongarch64, + => {}, + + .spu_2 => unreachable, // LLVM does not support this backend + .spirv32 => unreachable, // LLVM does not support this backend + .spirv64 => unreachable, // LLVM does not support this backend + } +} diff --git a/src/codegen/llvm/Builder.zig b/src/codegen/llvm/Builder.zig index 140c8fa941..6e850dd169 100644 --- a/src/codegen/llvm/Builder.zig +++ b/src/codegen/llvm/Builder.zig @@ -1,21 +1,6 @@ gpa: Allocator, -use_lib_llvm: bool, strip: bool, -llvm: if (build_options.have_llvm) struct { - context: *llvm.Context, - module: ?*llvm.Module, - target: ?*llvm.Target, - di_builder: ?*llvm.DIBuilder, - di_compile_unit: ?*llvm.DICompileUnit, - attribute_kind_ids: ?*[Attribute.Kind.len]c_uint, - attributes: std.ArrayListUnmanaged(*llvm.Attribute), - types: std.ArrayListUnmanaged(*llvm.Type), - globals: std.ArrayListUnmanaged(*llvm.Value), - constants: std.ArrayListUnmanaged(*llvm.Value), - replacements: std.AutoHashMapUnmanaged(*llvm.Value, Global.Index), -} else void, - source_filename: String, data_layout: String, target_triple: String, @@ -37,6 +22,8 @@ attributes_map: std.AutoArrayHashMapUnmanaged(void, void), attributes_indices: std.ArrayListUnmanaged(u32), attributes_extra: std.ArrayListUnmanaged(u32), +function_attributes_set: std.AutoArrayHashMapUnmanaged(FunctionAttributes, void), + globals: std.AutoArrayHashMapUnmanaged(String, Global), next_unnamed_global: String, next_replaced_global: String, @@ -50,17 +37,29 @@ constant_items: std.MultiArrayList(Constant.Item), constant_extra: std.ArrayListUnmanaged(u32), constant_limbs: std.ArrayListUnmanaged(std.math.big.Limb), +metadata_map: std.AutoArrayHashMapUnmanaged(void, void), +metadata_items: std.MultiArrayList(Metadata.Item), +metadata_extra: std.ArrayListUnmanaged(u32), +metadata_limbs: std.ArrayListUnmanaged(std.math.big.Limb), +metadata_forward_references: std.ArrayListUnmanaged(Metadata), +metadata_named: std.AutoArrayHashMapUnmanaged(MetadataString, struct { + len: u32, + index: Metadata.Item.ExtraIndex, +}), + +metadata_string_map: std.AutoArrayHashMapUnmanaged(void, void), +metadata_string_indices: std.ArrayListUnmanaged(u32), +metadata_string_bytes: std.ArrayListUnmanaged(u8), + pub const expected_args_len = 16; pub const expected_attrs_len = 16; pub const expected_fields_len = 32; pub const expected_gep_indices_len = 8; pub const expected_cases_len = 8; pub const expected_incoming_len = 8; -pub const expected_intrinsic_name_len = 64; pub const Options = struct { allocator: Allocator, - use_lib_llvm: bool = false, strip: bool = true, name: []const u8 = &.{}, target: std.Target = builtin.target, @@ -77,11 +76,11 @@ pub const String = enum(u32) { return self.toIndex() == null; } - pub fn slice(self: String, b: *const Builder) ?[:0]const u8 { + pub fn slice(self: String, builder: *const Builder) ?[]const u8 { const index = self.toIndex() orelse return null; - const start = b.string_indices.items[index]; - const end = b.string_indices.items[index + 1]; - return b.string_bytes.items[start .. end - 1 :0]; + const start = builder.string_indices.items[index]; + const end = builder.string_indices.items[index + 1]; + return builder.string_bytes.items[start..end]; } const FormatData = struct { @@ -94,17 +93,21 @@ pub const String = enum(u32) { _: std.fmt.FormatOptions, writer: anytype, ) @TypeOf(writer).Error!void { - if (comptime std.mem.indexOfNone(u8, fmt_str, "@\"")) |_| + if (comptime std.mem.indexOfNone(u8, fmt_str, "\"r")) |_| @compileError("invalid format string: '" ++ fmt_str ++ "'"); assert(data.string != .none); - const sentinel_slice = data.string.slice(data.builder) orelse + const string_slice = data.string.slice(data.builder) orelse return writer.print("{d}", .{@intFromEnum(data.string)}); - try printEscapedString(sentinel_slice[0 .. sentinel_slice.len + comptime @intFromBool( - std.mem.indexOfScalar(u8, fmt_str, '@') != null, - )], if (comptime std.mem.indexOfScalar(u8, fmt_str, '"')) |_| - .always_quote - else - .quote_unless_valid_identifier, writer); + if (comptime std.mem.indexOfScalar(u8, fmt_str, 'r')) |_| + return writer.writeAll(string_slice); + try printEscapedString( + string_slice, + if (comptime std.mem.indexOfScalar(u8, fmt_str, '"')) |_| + .always_quote + else + .quote_unless_valid_identifier, + writer, + ); } pub fn fmt(self: String, builder: *const Builder) std.fmt.Formatter(format) { return .{ .data = .{ .string = self, .builder = builder } }; @@ -130,6 +133,72 @@ pub const String = enum(u32) { }; }; +pub const BinaryOpcode = enum(u4) { + add = 0, + sub = 1, + mul = 2, + udiv = 3, + sdiv = 4, + urem = 5, + srem = 6, + shl = 7, + lshr = 8, + ashr = 9, + @"and" = 10, + @"or" = 11, + xor = 12, +}; + +pub const CastOpcode = enum(u4) { + trunc = 0, + zext = 1, + sext = 2, + fptoui = 3, + fptosi = 4, + uitofp = 5, + sitofp = 6, + fptrunc = 7, + fpext = 8, + ptrtoint = 9, + inttoptr = 10, + bitcast = 11, + addrspacecast = 12, +}; + +pub const CmpPredicate = enum(u6) { + fcmp_false = 0, + fcmp_oeq = 1, + fcmp_ogt = 2, + fcmp_oge = 3, + fcmp_olt = 4, + fcmp_ole = 5, + fcmp_one = 6, + fcmp_ord = 7, + fcmp_uno = 8, + fcmp_ueq = 9, + fcmp_ugt = 10, + fcmp_uge = 11, + fcmp_ult = 12, + fcmp_ule = 13, + fcmp_une = 14, + fcmp_true = 15, + icmp_eq = 32, + icmp_ne = 33, + icmp_ugt = 34, + icmp_uge = 35, + icmp_ult = 36, + icmp_ule = 37, + icmp_sgt = 38, + icmp_sge = 39, + icmp_slt = 40, + icmp_sle = 41, +}; + +pub const StrtabString = struct { + offset: usize, + size: usize, +}; + pub const Type = enum(u32) { void, half, @@ -178,20 +247,20 @@ pub const Type = enum(u32) { named_structure, }; - pub const Simple = enum { - void, - half, - bfloat, - float, - double, - fp128, - x86_fp80, - ppc_fp128, - x86_amx, - x86_mmx, - label, - token, - metadata, + pub const Simple = enum(u5) { + void = 2, + half = 10, + bfloat = 23, + float = 3, + double = 4, + fp128 = 14, + x86_fp80 = 13, + ppc_fp128 = 15, + x86_amx = 24, + x86_mmx = 17, + label = 5, + token = 22, + metadata = 16, }; pub const Function = struct { @@ -579,7 +648,6 @@ pub const Type = enum(u32) { var visited: IsSizedVisited = .{}; defer visited.deinit(builder.gpa); const result = try self.isSizedVisited(&visited, builder); - if (builder.useLibLlvm()) assert(result == self.toLlvm(builder).isSized().toBool()); return result; } @@ -766,11 +834,6 @@ pub const Type = enum(u32) { return .{ .data = .{ .type = self, .builder = builder } }; } - pub fn toLlvm(self: Type, builder: *const Builder) *llvm.Type { - assert(builder.useLibLlvm()); - return builder.llvm.types.items[@intFromEnum(self)]; - } - const IsSizedVisited = std.AutoHashMapUnmanaged(Type, void); fn isSizedVisited( self: Type, @@ -1051,14 +1114,21 @@ pub const Attribute = union(Kind) { .no_sanitize_hwaddress, .sanitize_address_dyninit, => |kind| { - const field = @typeInfo(Attribute).Union.fields[@intFromEnum(kind)]; + const field = comptime blk: { + @setEvalBranchQuota(10_000); + for (@typeInfo(Attribute).Union.fields) |field| { + if (std.mem.eql(u8, field.name, @tagName(kind))) break :blk field; + } + unreachable; + }; comptime assert(std.mem.eql(u8, @tagName(kind), field.name)); return @unionInit(Attribute, field.name, switch (field.type) { void => {}, u32 => storage.value, Alignment, String, Type, UwTable => @enumFromInt(storage.value), AllocKind, AllocSize, FpClass, Memory, VScaleRange => @bitCast(storage.value), - else => @compileError("bad payload type: " ++ @typeName(field.type)), + else => @compileError("bad payload type: " ++ field.name ++ ": " ++ + @typeName(field.type)), }); }, .string, .none => unreachable, @@ -1246,109 +1316,104 @@ pub const Attribute = union(Kind) { fn toStorage(self: Index, builder: *const Builder) Storage { return builder.attributes.keys()[@intFromEnum(self)]; } - - fn toLlvm(self: Index, builder: *const Builder) *llvm.Attribute { - assert(builder.useLibLlvm()); - return builder.llvm.attributes.items[@intFromEnum(self)]; - } }; pub const Kind = enum(u32) { // Parameter Attributes - zeroext, - signext, - inreg, - byval, - byref, - preallocated, - inalloca, - sret, - elementtype, - @"align", - @"noalias", - nocapture, - nofree, - nest, - returned, - nonnull, - dereferenceable, - dereferenceable_or_null, - swiftself, - swiftasync, - swifterror, - immarg, - noundef, - nofpclass, - alignstack, - allocalign, - allocptr, - readnone, - readonly, - writeonly, + zeroext = 34, + signext = 24, + inreg = 5, + byval = 3, + byref = 69, + preallocated = 65, + inalloca = 38, + sret = 29, // TODO: ? + elementtype = 77, + @"align" = 1, + @"noalias" = 9, + nocapture = 11, + nofree = 62, + nest = 8, + returned = 22, + nonnull = 39, + dereferenceable = 41, + dereferenceable_or_null = 42, + swiftself = 46, + swiftasync = 75, + swifterror = 47, + immarg = 60, + noundef = 68, + nofpclass = 87, + alignstack = 25, + allocalign = 80, + allocptr = 81, + readnone = 20, + readonly = 21, + writeonly = 52, // Function Attributes //alignstack, - allockind, - allocsize, - alwaysinline, - builtin, - cold, - convergent, - disable_sanitizer_information, - fn_ret_thunk_extern, - hot, - inlinehint, - jumptable, - memory, - minsize, - naked, - nobuiltin, - nocallback, - noduplicate, + allockind = 82, + allocsize = 51, + alwaysinline = 2, + builtin = 35, + cold = 36, + convergent = 43, + disable_sanitizer_information = 78, + fn_ret_thunk_extern = 84, + hot = 72, + inlinehint = 4, + jumptable = 40, + memory = 86, + minsize = 6, + naked = 7, + nobuiltin = 10, + nocallback = 71, + noduplicate = 12, //nofree, - noimplicitfloat, - @"noinline", - nomerge, - nonlazybind, - noprofile, - skipprofile, - noredzone, - noreturn, - norecurse, - willreturn, - nosync, - nounwind, - nosanitize_bounds, - nosanitize_coverage, - null_pointer_is_valid, - optforfuzzing, - optnone, - optsize, + noimplicitfloat = 13, + @"noinline" = 14, + nomerge = 66, + nonlazybind = 15, + noprofile = 73, + skipprofile = 85, + noredzone = 16, + noreturn = 17, + norecurse = 48, + willreturn = 61, + nosync = 63, + nounwind = 18, + nosanitize_bounds = 79, + nosanitize_coverage = 76, + null_pointer_is_valid = 67, + optforfuzzing = 57, + optnone = 37, + optsize = 19, //preallocated, - returns_twice, - safestack, - sanitize_address, - sanitize_memory, - sanitize_thread, - sanitize_hwaddress, - sanitize_memtag, - speculative_load_hardening, - speculatable, - ssp, - sspstrong, - sspreq, - strictfp, - uwtable, - nocf_check, - shadowcallstack, - mustprogress, - vscale_range, + returns_twice = 23, + safestack = 44, + sanitize_address = 30, + sanitize_memory = 32, + sanitize_thread = 31, + sanitize_hwaddress = 55, + sanitize_memtag = 64, + speculative_load_hardening = 59, + speculatable = 53, + ssp = 26, + sspstrong = 28, + sspreq = 27, + strictfp = 54, + uwtable = 33, + nocf_check = 56, + shadowcallstack = 58, + mustprogress = 70, + vscale_range = 74, // Global Attributes - no_sanitize_address, - no_sanitize_hwaddress, + no_sanitize_address = 100, + no_sanitize_hwaddress = 101, //sanitize_memtag, - sanitize_address_dyninit, + sanitize_address_dyninit = 102, string = std.math.maxInt(u31), none = std.math.maxInt(u32), @@ -1368,11 +1433,6 @@ pub const Attribute = union(Kind) { const str: String = @enumFromInt(@intFromEnum(self)); return if (str.isAnon()) null else str; } - - fn toLlvm(self: Kind, builder: *const Builder) *c_uint { - assert(builder.useLibLlvm()); - return &builder.llvm.attribute_kind_ids.?[@intFromEnum(self)]; - } }; pub const FpClass = packed struct(u32) { @@ -1494,12 +1554,12 @@ pub const Attribute = union(Kind) { fn toStorage(self: Attribute) Storage { return switch (self) { - inline else => |value| .{ .kind = @as(Kind, self), .value = switch (@TypeOf(value)) { + inline else => |value, tag| .{ .kind = @as(Kind, self), .value = switch (@TypeOf(value)) { void => 0, u32 => value, Alignment, String, Type, UwTable => @intFromEnum(value), AllocKind, AllocSize, FpClass, Memory, VScaleRange => @bitCast(value), - else => @compileError("bad payload type: " ++ @typeName(@TypeOf(value))), + else => @compileError("bad payload type: " ++ @tagName(tag) ++ @typeName(@TypeOf(value))), } }, .string => |string_attr| .{ .kind = Kind.fromString(string_attr.kind), @@ -1709,18 +1769,18 @@ pub const FunctionAttributes = enum(u32) { } }; -pub const Linkage = enum { - private, - internal, - weak, - weak_odr, - linkonce, - linkonce_odr, - available_externally, - appending, - common, - extern_weak, - external, +pub const Linkage = enum(u4) { + private = 9, + internal = 3, + weak = 1, + weak_odr = 10, + linkonce = 4, + linkonce_odr = 11, + available_externally = 12, + appending = 2, + common = 8, + extern_weak = 7, + external = 0, pub fn format( self: Linkage, @@ -1731,20 +1791,16 @@ pub const Linkage = enum { if (self != .external) try writer.print(" {s}", .{@tagName(self)}); } - fn toLlvm(self: Linkage) llvm.Linkage { - return switch (self) { - .private => .Private, - .internal => .Internal, - .weak => .WeakAny, - .weak_odr => .WeakODR, - .linkonce => .LinkOnceAny, - .linkonce_odr => .LinkOnceODR, - .available_externally => .AvailableExternally, - .appending => .Appending, - .common => .Common, - .extern_weak => .ExternalWeak, - .external => .External, - }; + fn formatOptional( + data: ?Linkage, + comptime _: []const u8, + _: std.fmt.FormatOptions, + writer: anytype, + ) @TypeOf(writer).Error!void { + if (data) |linkage| try writer.print(" {s}", .{@tagName(linkage)}); + } + pub fn fmtOptional(self: ?Linkage) std.fmt.Formatter(formatOptional) { + return .{ .data = self }; } }; @@ -1763,10 +1819,10 @@ pub const Preemption = enum { } }; -pub const Visibility = enum { - default, - hidden, - protected, +pub const Visibility = enum(u2) { + default = 0, + hidden = 1, + protected = 2, pub fn format( self: Visibility, @@ -1776,20 +1832,12 @@ pub const Visibility = enum { ) @TypeOf(writer).Error!void { if (self != .default) try writer.print(" {s}", .{@tagName(self)}); } - - fn toLlvm(self: Visibility) llvm.Visibility { - return switch (self) { - .default => .Default, - .hidden => .Hidden, - .protected => .Protected, - }; - } }; -pub const DllStorageClass = enum { - default, - dllimport, - dllexport, +pub const DllStorageClass = enum(u2) { + default = 0, + dllimport = 1, + dllexport = 2, pub fn format( self: DllStorageClass, @@ -1799,22 +1847,14 @@ pub const DllStorageClass = enum { ) @TypeOf(writer).Error!void { if (self != .default) try writer.print(" {s}", .{@tagName(self)}); } - - fn toLlvm(self: DllStorageClass) llvm.DLLStorageClass { - return switch (self) { - .default => .Default, - .dllimport => .DLLImport, - .dllexport => .DLLExport, - }; - } }; -pub const ThreadLocal = enum { - default, - generaldynamic, - localdynamic, - initialexec, - localexec, +pub const ThreadLocal = enum(u3) { + default = 0, + generaldynamic = 1, + localdynamic = 2, + initialexec = 3, + localexec = 4, pub fn format( self: ThreadLocal, @@ -1826,24 +1866,14 @@ pub const ThreadLocal = enum { try writer.print("{s}thread_local", .{prefix}); if (self != .generaldynamic) try writer.print("({s})", .{@tagName(self)}); } - - fn toLlvm(self: ThreadLocal) llvm.ThreadLocalMode { - return switch (self) { - .default => .NotThreadLocal, - .generaldynamic => .GeneralDynamicTLSModel, - .localdynamic => .LocalDynamicTLSModel, - .initialexec => .InitialExecTLSModel, - .localexec => .LocalExecTLSModel, - }; - } }; pub const Mutability = enum { global, constant }; -pub const UnnamedAddr = enum { - default, - unnamed_addr, - local_unnamed_addr, +pub const UnnamedAddr = enum(u2) { + default = 0, + unnamed_addr = 1, + local_unnamed_addr = 2, pub fn format( self: UnnamedAddr, @@ -1971,6 +2001,10 @@ pub const Alignment = enum(u6) { return if (self == .default) null else @as(u64, 1) << @intFromEnum(self); } + pub fn toLlvm(self: Alignment) u6 { + return if (self == .default) 0 else (@intFromEnum(self) + 1); + } + pub fn format( self: Alignment, comptime prefix: []const u8, @@ -2100,11 +2134,6 @@ pub const CallConv = enum(u10) { _ => try writer.print(" cc{d}", .{@intFromEnum(self)}), } } - - fn toLlvm(self: CallConv) llvm.CallConv { - // These enum values appear in LLVM IR, and so are guaranteed to be stable. - return @enumFromInt(@intFromEnum(self)); - } }; pub const Global = struct { @@ -2117,6 +2146,7 @@ pub const Global = struct { externally_initialized: ExternallyInitialized = .default, type: Type, partition: String = .none, + dbg: Metadata = .none, kind: union(enum) { alias: Alias.Index, variable: Variable.Index, @@ -2153,6 +2183,18 @@ pub const Global = struct { return builder.globals.keys()[@intFromEnum(self.unwrap(builder))]; } + pub fn strtab(self: Index, builder: *const Builder) StrtabString { + const name_index = self.name(builder).toIndex() orelse return .{ + .offset = 0, + .size = 0, + }; + + return .{ + .offset = builder.string_indices.items[name_index], + .size = builder.string_indices.items[name_index + 1] - builder.string_indices.items[name_index], + }; + } + pub fn typeOf(self: Index, builder: *const Builder) Type { return self.ptrConst(builder).type; } @@ -2162,32 +2204,25 @@ pub const Global = struct { } pub fn setLinkage(self: Index, linkage: Linkage, builder: *Builder) void { - if (builder.useLibLlvm()) self.toLlvm(builder).setLinkage(linkage.toLlvm()); self.ptr(builder).linkage = linkage; self.updateDsoLocal(builder); } pub fn setVisibility(self: Index, visibility: Visibility, builder: *Builder) void { - if (builder.useLibLlvm()) self.toLlvm(builder).setVisibility(visibility.toLlvm()); self.ptr(builder).visibility = visibility; self.updateDsoLocal(builder); } pub fn setDllStorageClass(self: Index, class: DllStorageClass, builder: *Builder) void { - if (builder.useLibLlvm()) self.toLlvm(builder).setDLLStorageClass(class.toLlvm()); self.ptr(builder).dll_storage_class = class; } pub fn setUnnamedAddr(self: Index, unnamed_addr: UnnamedAddr, builder: *Builder) void { - if (builder.useLibLlvm()) self.toLlvm(builder).setUnnamedAddr( - llvm.Bool.fromBool(unnamed_addr != .default), - ); self.ptr(builder).unnamed_addr = unnamed_addr; } - pub fn toLlvm(self: Index, builder: *const Builder) *llvm.Value { - assert(builder.useLibLlvm()); - return builder.llvm.globals.items[@intFromEnum(self.unwrap(builder))]; + pub fn setDebugMetadata(self: Index, dbg: Metadata, builder: *Builder) void { + self.ptr(builder).dbg = dbg; } const FormatData = struct { @@ -2220,13 +2255,10 @@ pub const Global = struct { pub fn replace(self: Index, other: Index, builder: *Builder) Allocator.Error!void { try builder.ensureUnusedGlobalCapacity(.empty); - if (builder.useLibLlvm()) - try builder.llvm.replacements.ensureUnusedCapacity(builder.gpa, 1); self.replaceAssumeCapacity(other, builder); } pub fn delete(self: Index, builder: *Builder) void { - if (builder.useLibLlvm()) self.toLlvm(builder).eraseGlobalValue(); self.ptr(builder).kind = .{ .replaced = .none }; } @@ -2254,12 +2286,8 @@ pub const Global = struct { const old_name = self.name(builder); if (new_name == old_name) return; const index = @intFromEnum(self.unwrap(builder)); - if (builder.useLibLlvm()) - builder.llvm.globals.appendAssumeCapacity(builder.llvm.globals.items[index]); _ = builder.addGlobalAssumeCapacity(new_name, builder.globals.values()[index]); - if (builder.useLibLlvm()) _ = builder.llvm.globals.pop(); builder.globals.swapRemoveAt(index); - self.updateName(builder); if (!old_name.isAnon()) return; builder.next_unnamed_global = @enumFromInt(@intFromEnum(builder.next_unnamed_global) - 1); if (builder.next_unnamed_global == old_name) return; @@ -2272,23 +2300,10 @@ pub const Global = struct { self.renameAssumeCapacity(other_name, builder); } - fn updateName(self: Index, builder: *const Builder) void { - if (!builder.useLibLlvm()) return; - const index = @intFromEnum(self.unwrap(builder)); - const name_slice = self.name(builder).slice(builder) orelse ""; - builder.llvm.globals.items[index].setValueName(name_slice.ptr, name_slice.len); - } - fn replaceAssumeCapacity(self: Index, other: Index, builder: *Builder) void { if (self.eql(other, builder)) return; builder.next_replaced_global = @enumFromInt(@intFromEnum(builder.next_replaced_global) - 1); self.renameAssumeCapacity(builder.next_replaced_global, builder); - if (builder.useLibLlvm()) { - const self_llvm = self.toLlvm(builder); - self_llvm.replaceAllUsesWith(other.toLlvm(builder)); - self_llvm.removeGlobalValue(); - builder.llvm.replacements.putAssumeCapacityNoClobber(self_llvm, other); - } self.ptr(builder).kind = .{ .replaced = other.unwrap(builder) }; } @@ -2345,13 +2360,8 @@ pub const Alias = struct { } pub fn setAliasee(self: Index, aliasee: Constant, builder: *Builder) void { - if (builder.useLibLlvm()) self.toLlvm(builder).setAliasee(aliasee.toLlvm(builder)); self.ptr(builder).aliasee = aliasee; } - - fn toLlvm(self: Index, builder: *const Builder) *llvm.Value { - return self.ptrConst(builder).global.toLlvm(builder); - } }; }; @@ -2404,14 +2414,10 @@ pub const Variable = struct { } pub fn setThreadLocal(self: Index, thread_local: ThreadLocal, builder: *Builder) void { - if (builder.useLibLlvm()) self.toLlvm(builder).setThreadLocalMode(thread_local.toLlvm()); self.ptr(builder).thread_local = thread_local; } pub fn setMutability(self: Index, mutability: Mutability, builder: *Builder) void { - if (builder.useLibLlvm()) self.toLlvm(builder).setGlobalConstant( - llvm.Bool.fromBool(mutability == .constant), - ); self.ptr(builder).mutability = mutability; } @@ -2424,67 +2430,25 @@ pub const Variable = struct { const variable = self.ptrConst(builder); const global = variable.global.ptr(builder); const initializer_type = initializer.typeOf(builder); - if (builder.useLibLlvm() and global.type != initializer_type) { - try builder.llvm.replacements.ensureUnusedCapacity(builder.gpa, 1); - // LLVM does not allow us to change the type of globals. So we must - // create a new global with the correct type, copy all its attributes, - // and then update all references to point to the new global, - // delete the original, and rename the new one to the old one's name. - // This is necessary because LLVM does not support const bitcasting - // a struct with padding bytes, which is needed to lower a const union value - // to LLVM, when a field other than the most-aligned is active. Instead, - // we must lower to an unnamed struct, and pointer cast at usage sites - // of the global. Such an unnamed struct is the cause of the global type - // mismatch, because we don't have the LLVM type until the *value* is created, - // whereas the global needs to be created based on the type alone, because - // lowering the value may reference the global as a pointer. - // Related: https://github.com/ziglang/zig/issues/13265 - const old_global = &builder.llvm.globals.items[@intFromEnum(variable.global)]; - const new_global = builder.llvm.module.?.addGlobalInAddressSpace( - initializer_type.toLlvm(builder), - "", - @intFromEnum(global.addr_space), - ); - new_global.setLinkage(global.linkage.toLlvm()); - new_global.setUnnamedAddr(llvm.Bool.fromBool(global.unnamed_addr != .default)); - new_global.setAlignment(@intCast(variable.alignment.toByteUnits() orelse 0)); - if (variable.section != .none) - new_global.setSection(variable.section.slice(builder).?); - old_global.*.replaceAllUsesWith(new_global); - builder.llvm.replacements.putAssumeCapacityNoClobber(old_global.*, variable.global); - new_global.takeName(old_global.*); - old_global.*.removeGlobalValue(); - old_global.* = new_global; - self.ptr(builder).mutability = .global; - } global.type = initializer_type; } - if (builder.useLibLlvm()) self.toLlvm(builder).setInitializer(switch (initializer) { - .no_init => null, - else => initializer.toLlvm(builder), - }); self.ptr(builder).init = initializer; } pub fn setSection(self: Index, section: String, builder: *Builder) void { - if (builder.useLibLlvm()) self.toLlvm(builder).setSection(section.slice(builder).?); self.ptr(builder).section = section; } pub fn setAlignment(self: Index, alignment: Alignment, builder: *Builder) void { - if (builder.useLibLlvm()) - self.toLlvm(builder).setAlignment(@intCast(alignment.toByteUnits() orelse 0)); self.ptr(builder).alignment = alignment; } pub fn getAlignment(self: Index, builder: *Builder) Alignment { - if (builder.useLibLlvm()) - return Alignment.fromByteUnits(self.toLlvm(builder).getAlignment()); return self.ptr(builder).alignment; } - pub fn toLlvm(self: Index, builder: *const Builder) *llvm.Value { - return self.ptrConst(builder).global.toLlvm(builder); + pub fn setGlobalVariableExpression(self: Index, expression: Metadata, builder: *Builder) void { + self.ptrConst(builder).global.setDebugMetadata(expression, builder); } }; }; @@ -2633,6 +2597,10 @@ pub const Intrinsic = enum { @"threadlocal.address", vscale, + // Debug + @"dbg.declare", + @"dbg.value", + // AMDGPU @"amdgcn.workitem.id.x", @"amdgcn.workitem.id.y", @@ -3727,6 +3695,25 @@ pub const Intrinsic = enum { .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, }, + .@"dbg.declare" = .{ + .ret_len = 0, + .params = &.{ + .{ .kind = .{ .type = .metadata } }, + .{ .kind = .{ .type = .metadata } }, + .{ .kind = .{ .type = .metadata } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .@"dbg.value" = .{ + .ret_len = 0, + .params = &.{ + .{ .kind = .{ .type = .metadata } }, + .{ .kind = .{ .type = .metadata } }, + .{ .kind = .{ .type = .metadata } }, + }, + .attrs = &.{ .nocallback, .nofree, .nosync, .nounwind, .speculatable, .willreturn, .{ .memory = Attribute.Memory.all(.none) } }, + }, + .@"amdgcn.workitem.id.x" = .{ .ret_len = 1, .params = &.{ @@ -3809,7 +3796,10 @@ pub const Function = struct { blocks: []const Block = &.{}, instructions: std.MultiArrayList(Instruction) = .{}, names: [*]const String = &[0]String{}, - metadata: ?[*]const Metadata = null, + value_indices: [*]const u32 = &[0]u32{}, + strip: bool, + debug_locations: std.AutoHashMapUnmanaged(Instruction.Index, DebugLocation) = .{}, + debug_values: []const Instruction.Index = &.{}, extra: []const u32 = &.{}, pub const Index = enum(u32) { @@ -3853,7 +3843,6 @@ pub const Function = struct { } pub fn setCallConv(self: Index, call_conv: CallConv, builder: *Builder) void { - if (builder.useLibLlvm()) self.toLlvm(builder).setFunctionCallConv(call_conv.toLlvm()); self.ptr(builder).call_conv = call_conv; } @@ -3862,94 +3851,19 @@ pub const Function = struct { new_function_attributes: FunctionAttributes, builder: *Builder, ) void { - if (builder.useLibLlvm()) { - const llvm_function = self.toLlvm(builder); - const old_function_attributes = self.ptrConst(builder).attributes; - for (0..@max( - old_function_attributes.slice(builder).len, - new_function_attributes.slice(builder).len, - )) |function_attribute_index| { - const llvm_attribute_index = - @as(llvm.AttributeIndex, @intCast(function_attribute_index)) -% 1; - const old_attributes_slice = - old_function_attributes.get(function_attribute_index, builder).slice(builder); - const new_attributes_slice = - new_function_attributes.get(function_attribute_index, builder).slice(builder); - var old_attribute_index: usize = 0; - var new_attribute_index: usize = 0; - while (true) { - const old_attribute_kind = if (old_attribute_index < old_attributes_slice.len) - old_attributes_slice[old_attribute_index].getKind(builder) - else - .none; - const new_attribute_kind = if (new_attribute_index < new_attributes_slice.len) - new_attributes_slice[new_attribute_index].getKind(builder) - else - .none; - switch (std.math.order( - @intFromEnum(old_attribute_kind), - @intFromEnum(new_attribute_kind), - )) { - .lt => { - // Removed - if (old_attribute_kind.toString()) |attribute_name| { - const attribute_name_slice = attribute_name.slice(builder).?; - llvm_function.removeStringAttributeAtIndex( - llvm_attribute_index, - attribute_name_slice.ptr, - @intCast(attribute_name_slice.len), - ); - } else { - const llvm_kind_id = old_attribute_kind.toLlvm(builder).*; - assert(llvm_kind_id != 0); - llvm_function.removeEnumAttributeAtIndex( - llvm_attribute_index, - llvm_kind_id, - ); - } - old_attribute_index += 1; - continue; - }, - .eq => { - // Iteration finished - if (old_attribute_kind == .none) break; - // No change - if (old_attributes_slice[old_attribute_index] == - new_attributes_slice[new_attribute_index]) - { - old_attribute_index += 1; - new_attribute_index += 1; - continue; - } - old_attribute_index += 1; - }, - .gt => {}, - } - // New or changed - llvm_function.addAttributeAtIndex( - llvm_attribute_index, - new_attributes_slice[new_attribute_index].toLlvm(builder), - ); - new_attribute_index += 1; - } - } - } self.ptr(builder).attributes = new_function_attributes; } pub fn setSection(self: Index, section: String, builder: *Builder) void { - if (builder.useLibLlvm()) self.toLlvm(builder).setSection(section.slice(builder).?); self.ptr(builder).section = section; } pub fn setAlignment(self: Index, alignment: Alignment, builder: *Builder) void { - if (builder.useLibLlvm()) - self.toLlvm(builder).setAlignment(@intCast(alignment.toByteUnits() orelse 0)); self.ptr(builder).alignment = alignment; } - pub fn toLlvm(self: Index, builder: *const Builder) *llvm.Value { - return self.ptrConst(builder).global.toLlvm(builder); + pub fn setSubprogram(self: Index, subprogram: Metadata, builder: *Builder) void { + self.ptrConst(builder).global.setDebugMetadata(subprogram, builder); } }; @@ -4098,6 +4012,143 @@ pub const Function = struct { va_arg, xor, zext, + + pub fn toBinaryOpcode(self: Tag) BinaryOpcode { + return switch (self) { + .add, + .@"add nsw", + .@"add nuw", + .@"add nuw nsw", + .fadd, + .@"fadd fast", + => .add, + .sub, + .@"sub nsw", + .@"sub nuw", + .@"sub nuw nsw", + .fsub, + .@"fsub fast", + => .sub, + .sdiv, + .@"sdiv exact", + .fdiv, + .@"fdiv fast", + => .sdiv, + .fmul, + .@"fmul fast", + .mul, + .@"mul nsw", + .@"mul nuw", + .@"mul nuw nsw", + => .mul, + .srem, + .frem, + .@"frem fast", + => .srem, + .udiv, + .@"udiv exact", + => .udiv, + .shl, + .@"shl nsw", + .@"shl nuw", + .@"shl nuw nsw", + => .shl, + .lshr, + .@"lshr exact", + => .lshr, + .ashr, + .@"ashr exact", + => .ashr, + .@"and" => .@"and", + .@"or" => .@"or", + .xor => .xor, + .urem => .urem, + else => unreachable, + }; + } + + pub fn toCastOpcode(self: Tag) CastOpcode { + return switch (self) { + .trunc => .trunc, + .zext => .zext, + .sext => .sext, + .fptoui => .fptoui, + .fptosi => .fptosi, + .uitofp => .uitofp, + .sitofp => .sitofp, + .fptrunc => .fptrunc, + .fpext => .fpext, + .ptrtoint => .ptrtoint, + .inttoptr => .inttoptr, + .bitcast => .bitcast, + .addrspacecast => .addrspacecast, + else => unreachable, + }; + } + + pub fn toCmpPredicate(self: Tag) CmpPredicate { + return switch (self) { + .@"fcmp false", + .@"fcmp fast false", + => .fcmp_false, + .@"fcmp oeq", + .@"fcmp fast oeq", + => .fcmp_oeq, + .@"fcmp oge", + .@"fcmp fast oge", + => .fcmp_oge, + .@"fcmp ogt", + .@"fcmp fast ogt", + => .fcmp_ogt, + .@"fcmp ole", + .@"fcmp fast ole", + => .fcmp_ole, + .@"fcmp olt", + .@"fcmp fast olt", + => .fcmp_olt, + .@"fcmp one", + .@"fcmp fast one", + => .fcmp_one, + .@"fcmp ord", + .@"fcmp fast ord", + => .fcmp_ord, + .@"fcmp true", + .@"fcmp fast true", + => .fcmp_true, + .@"fcmp ueq", + .@"fcmp fast ueq", + => .fcmp_ueq, + .@"fcmp uge", + .@"fcmp fast uge", + => .fcmp_uge, + .@"fcmp ugt", + .@"fcmp fast ugt", + => .fcmp_ugt, + .@"fcmp ule", + .@"fcmp fast ule", + => .fcmp_ule, + .@"fcmp ult", + .@"fcmp fast ult", + => .fcmp_ult, + .@"fcmp une", + .@"fcmp fast une", + => .fcmp_une, + .@"fcmp uno", + .@"fcmp fast uno", + => .fcmp_uno, + .@"icmp eq" => .icmp_eq, + .@"icmp ne" => .icmp_ne, + .@"icmp sge" => .icmp_sge, + .@"icmp sgt" => .icmp_sgt, + .@"icmp sle" => .icmp_sle, + .@"icmp slt" => .icmp_slt, + .@"icmp uge" => .icmp_uge, + .@"icmp ugt" => .icmp_ugt, + .@"icmp ule" => .icmp_ule, + .@"icmp ult" => .icmp_ult, + else => unreachable, + }; + } }; pub const Index = enum(u32) { @@ -4108,6 +4159,10 @@ pub const Function = struct { return function.names[@intFromEnum(self)]; } + pub fn valueIndex(self: Instruction.Index, function: *const Function) u32 { + return function.value_indices[@intFromEnum(self)]; + } + pub fn toValue(self: Instruction.Index) Value { return @enumFromInt(@intFromEnum(self)); } @@ -4136,6 +4191,7 @@ pub const Function = struct { .@"store atomic", .@"switch", .@"unreachable", + .block, => false, .call, .@"call fast", @@ -4240,7 +4296,7 @@ pub const Function = struct { => wip.builder.structTypeAssumeCapacity(.normal, &.{ wip.extraData(CmpXchg, instruction.data).cmp.typeOfWip(wip), .i1, - }) catch unreachable, + }), .extractelement => wip.extraData(ExtractElement, instruction.data) .val.typeOfWip(wip).childType(wip.builder), .extractvalue => { @@ -4427,7 +4483,7 @@ pub const Function = struct { function.extraData(CmpXchg, instruction.data) .cmp.typeOf(function_index, builder), .i1, - }) catch unreachable, + }), .extractelement => function.extraData(ExtractElement, instruction.data) .val.typeOf(function_index, builder).childType(builder), .extractvalue => { @@ -4557,20 +4613,6 @@ pub const Function = struct { ) std.fmt.Formatter(format) { return .{ .data = .{ .instruction = self, .function = function, .builder = builder } }; } - - fn toLlvm(self: Instruction.Index, wip: *const WipFunction) *llvm.Value { - assert(wip.builder.useLibLlvm()); - const llvm_value = wip.llvm.instructions.items[@intFromEnum(self)]; - const global = wip.builder.llvm.replacements.get(llvm_value) orelse return llvm_value; - return global.toLlvm(wip.builder); - } - - fn llvmName(self: Instruction.Index, wip: *const WipFunction) [:0]const u8 { - return if (wip.builder.strip) - "" - else - wip.names.items[@intFromEnum(self)].slice(wip.builder).?; - } }; pub const ExtraIndex = u32; @@ -4664,43 +4706,22 @@ pub const Function = struct { val: Value, pub const Operation = enum(u5) { - xchg, - add, - sub, - @"and", - nand, - @"or", - xor, - max, - min, - umax, - umin, - fadd, - fsub, - fmax, - fmin, + xchg = 0, + add = 1, + sub = 2, + @"and" = 3, + nand = 4, + @"or" = 5, + xor = 6, + max = 7, + min = 8, + umax = 9, + umin = 10, + fadd = 11, + fsub = 12, + fmax = 13, + fmin = 14, none = std.math.maxInt(u5), - - fn toLlvm(self: Operation) llvm.AtomicRMWBinOp { - return switch (self) { - .xchg => .Xchg, - .add => .Add, - .sub => .Sub, - .@"and" => .And, - .nand => .Nand, - .@"or" => .Or, - .xor => .Xor, - .max => .Max, - .min => .Min, - .umax => .UMax, - .umin => .UMin, - .fadd => .FAdd, - .fsub => .FSub, - .fmax => .FMax, - .fmin => .FMin, - .none => unreachable, - }; - } }; }; @@ -4764,7 +4785,9 @@ pub const Function = struct { pub fn deinit(self: *Function, gpa: Allocator) void { gpa.free(self.extra); - if (self.metadata) |metadata| gpa.free(metadata[0..self.instructions.len]); + gpa.free(self.debug_values); + self.debug_locations.deinit(gpa); + gpa.free(self.value_indices[0..self.instructions.len]); gpa.free(self.names[0..self.instructions.len]); self.instructions.deinit(gpa); gpa.free(self.blocks); @@ -4822,7 +4845,7 @@ pub const Function = struct { Instruction.Alloca.Info, Instruction.Call.Info, => @bitCast(value), - else => @compileError("bad field type: " ++ @typeName(field.type)), + else => @compileError("bad field type: " ++ field.name ++ ": " ++ @typeName(field.type)), }; return .{ .data = result, @@ -4835,19 +4858,42 @@ pub const Function = struct { } }; +pub const DebugLocation = union(enum) { + no_location: void, + location: Location, + + pub const Location = struct { + line: u32, + column: u32, + scope: Builder.Metadata, + inlined_at: Builder.Metadata, + }; + + pub fn toMetadata(self: DebugLocation, builder: *Builder) Allocator.Error!Metadata { + return switch (self) { + .no_location => .none, + .location => |location| try builder.debugLocation( + location.line, + location.column, + location.scope, + location.inlined_at, + ), + }; + } +}; + pub const WipFunction = struct { builder: *Builder, function: Function.Index, - llvm: if (build_options.have_llvm) struct { - builder: *llvm.Builder, - blocks: std.ArrayListUnmanaged(*llvm.BasicBlock), - instructions: std.ArrayListUnmanaged(*llvm.Value), - } else void, + prev_debug_location: DebugLocation, + debug_location: DebugLocation, cursor: Cursor, blocks: std.ArrayListUnmanaged(Block), instructions: std.MultiArrayList(Instruction), names: std.ArrayListUnmanaged(String), - metadata: std.ArrayListUnmanaged(Metadata), + strip: bool, + debug_locations: std.AutoArrayHashMapUnmanaged(Instruction.Index, DebugLocation), + debug_values: std.AutoArrayHashMapUnmanaged(Instruction.Index, void), extra: std.ArrayListUnmanaged(u32), pub const Cursor = struct { block: Block.Index, instruction: u32 = 0 }; @@ -4873,51 +4919,42 @@ pub const WipFunction = struct { pub fn toInst(self: Index, function: *const Function) Instruction.Index { return function.blocks[@intFromEnum(self)].instruction; } - - pub fn toLlvm(self: Index, wip: *const WipFunction) *llvm.BasicBlock { - assert(wip.builder.useLibLlvm()); - return wip.llvm.blocks.items[@intFromEnum(self)]; - } }; }; pub const Instruction = Function.Instruction; - pub fn init(builder: *Builder, function: Function.Index) Allocator.Error!WipFunction { - if (builder.useLibLlvm()) { - const llvm_function = function.toLlvm(builder); - while (llvm_function.getFirstBasicBlock()) |bb| bb.deleteBasicBlock(); - } - - var self = WipFunction{ + pub fn init(builder: *Builder, options: struct { + function: Function.Index, + strip: bool, + }) Allocator.Error!WipFunction { + var self: WipFunction = .{ .builder = builder, - .function = function, - .llvm = if (builder.useLibLlvm()) .{ - .builder = builder.llvm.context.createBuilder(), - .blocks = .{}, - .instructions = .{}, - } else undefined, + .function = options.function, + .prev_debug_location = .no_location, + .debug_location = .no_location, .cursor = undefined, .blocks = .{}, .instructions = .{}, .names = .{}, - .metadata = .{}, + .strip = options.strip, + .debug_locations = .{}, + .debug_values = .{}, .extra = .{}, }; errdefer self.deinit(); - const params_len = function.typeOf(self.builder).functionParameters(self.builder).len; + const params_len = options.function.typeOf(self.builder).functionParameters(self.builder).len; try self.ensureUnusedExtraCapacity(params_len, NoExtra, 0); try self.instructions.ensureUnusedCapacity(self.builder.gpa, params_len); - if (!self.builder.strip) try self.names.ensureUnusedCapacity(self.builder.gpa, params_len); - if (self.builder.useLibLlvm()) - try self.llvm.instructions.ensureUnusedCapacity(self.builder.gpa, params_len); + if (!self.strip) { + try self.names.ensureUnusedCapacity(self.builder.gpa, params_len); + } for (0..params_len) |param_index| { self.instructions.appendAssumeCapacity(.{ .tag = .arg, .data = @intCast(param_index) }); - if (!self.builder.strip) self.names.appendAssumeCapacity(.empty); // TODO: param names - if (self.builder.useLibLlvm()) self.llvm.instructions.appendAssumeCapacity( - function.toLlvm(self.builder).getParam(@intCast(param_index)), - ); + if (!self.strip) { + self.names.appendAssumeCapacity(.empty); // TODO: param names + } } return self; @@ -4934,50 +4971,32 @@ pub const WipFunction = struct { pub fn block(self: *WipFunction, incoming: u32, name: []const u8) Allocator.Error!Block.Index { try self.blocks.ensureUnusedCapacity(self.builder.gpa, 1); - if (self.builder.useLibLlvm()) try self.llvm.blocks.ensureUnusedCapacity(self.builder.gpa, 1); const index: Block.Index = @enumFromInt(self.blocks.items.len); - const final_name = if (self.builder.strip) .empty else try self.builder.string(name); + const final_name = if (self.strip) .empty else try self.builder.string(name); self.blocks.appendAssumeCapacity(.{ .name = final_name, .incoming = incoming, .instructions = .{}, }); - if (self.builder.useLibLlvm()) self.llvm.blocks.appendAssumeCapacity( - self.builder.llvm.context.appendBasicBlock( - self.function.toLlvm(self.builder), - final_name.slice(self.builder).?, - ), - ); return index; } pub fn ret(self: *WipFunction, val: Value) Allocator.Error!Instruction.Index { assert(val.typeOfWip(self) == self.function.typeOf(self.builder).functionReturn(self.builder)); try self.ensureUnusedExtraCapacity(1, NoExtra, 0); - const instruction = try self.addInst(null, .{ .tag = .ret, .data = @intFromEnum(val) }); - if (self.builder.useLibLlvm()) self.llvm.instructions.appendAssumeCapacity( - self.llvm.builder.buildRet(val.toLlvm(self)), - ); - return instruction; + return try self.addInst(null, .{ .tag = .ret, .data = @intFromEnum(val) }); } pub fn retVoid(self: *WipFunction) Allocator.Error!Instruction.Index { try self.ensureUnusedExtraCapacity(1, NoExtra, 0); - const instruction = try self.addInst(null, .{ .tag = .@"ret void", .data = undefined }); - if (self.builder.useLibLlvm()) self.llvm.instructions.appendAssumeCapacity( - self.llvm.builder.buildRetVoid(), - ); - return instruction; + return try self.addInst(null, .{ .tag = .@"ret void", .data = undefined }); } pub fn br(self: *WipFunction, dest: Block.Index) Allocator.Error!Instruction.Index { try self.ensureUnusedExtraCapacity(1, NoExtra, 0); const instruction = try self.addInst(null, .{ .tag = .br, .data = @intFromEnum(dest) }); dest.ptr(self).branches += 1; - if (self.builder.useLibLlvm()) self.llvm.instructions.appendAssumeCapacity( - self.llvm.builder.buildBr(dest.toLlvm(self)), - ); return instruction; } @@ -4999,9 +5018,6 @@ pub const WipFunction = struct { }); then.ptr(self).branches += 1; @"else".ptr(self).branches += 1; - if (self.builder.useLibLlvm()) self.llvm.instructions.appendAssumeCapacity( - self.llvm.builder.buildCondBr(cond.toLlvm(self), then.toLlvm(self), @"else".toLlvm(self)), - ); return instruction; } @@ -5022,8 +5038,6 @@ pub const WipFunction = struct { extra.trail.nextMut(extra.data.cases_len, Block.Index, wip)[self.index] = dest; self.index += 1; dest.ptr(wip).branches += 1; - if (wip.builder.useLibLlvm()) - self.instruction.toLlvm(wip).addCase(val.toLlvm(wip.builder), dest.toLlvm(wip)); } pub fn finish(self: WipSwitch, wip: *WipFunction) void { @@ -5050,18 +5064,12 @@ pub const WipFunction = struct { }); _ = self.extra.addManyAsSliceAssumeCapacity(cases_len * 2); default.ptr(self).branches += 1; - if (self.builder.useLibLlvm()) self.llvm.instructions.appendAssumeCapacity( - self.llvm.builder.buildSwitch(val.toLlvm(self), default.toLlvm(self), @intCast(cases_len)), - ); return .{ .index = 0, .instruction = instruction }; } pub fn @"unreachable"(self: *WipFunction) Allocator.Error!Instruction.Index { try self.ensureUnusedExtraCapacity(1, NoExtra, 0); const instruction = try self.addInst(null, .{ .tag = .@"unreachable", .data = undefined }); - if (self.builder.useLibLlvm()) self.llvm.instructions.appendAssumeCapacity( - self.llvm.builder.buildUnreachable(), - ); return instruction; } @@ -5079,17 +5087,6 @@ pub const WipFunction = struct { } try self.ensureUnusedExtraCapacity(1, NoExtra, 0); const instruction = try self.addInst(name, .{ .tag = tag, .data = @intFromEnum(val) }); - if (self.builder.useLibLlvm()) { - switch (tag) { - .fneg => self.llvm.builder.setFastMath(false), - .@"fneg fast" => self.llvm.builder.setFastMath(true), - else => unreachable, - } - self.llvm.instructions.appendAssumeCapacity(switch (tag) { - .fneg, .@"fneg fast" => &llvm.Builder.buildFNeg, - else => unreachable, - }(self.llvm.builder, val.toLlvm(self), instruction.llvmName(self))); - } return instruction.toValue(); } @@ -5157,56 +5154,6 @@ pub const WipFunction = struct { .tag = tag, .data = self.addExtraAssumeCapacity(Instruction.Binary{ .lhs = lhs, .rhs = rhs }), }); - if (self.builder.useLibLlvm()) { - switch (tag) { - .fadd, - .fdiv, - .fmul, - .frem, - .fsub, - => self.llvm.builder.setFastMath(false), - .@"fadd fast", - .@"fdiv fast", - .@"fmul fast", - .@"frem fast", - .@"fsub fast", - => self.llvm.builder.setFastMath(true), - else => {}, - } - self.llvm.instructions.appendAssumeCapacity(switch (tag) { - .add => &llvm.Builder.buildAdd, - .@"add nsw" => &llvm.Builder.buildNSWAdd, - .@"add nuw" => &llvm.Builder.buildNUWAdd, - .@"and" => &llvm.Builder.buildAnd, - .ashr => &llvm.Builder.buildAShr, - .@"ashr exact" => &llvm.Builder.buildAShrExact, - .fadd, .@"fadd fast" => &llvm.Builder.buildFAdd, - .fdiv, .@"fdiv fast" => &llvm.Builder.buildFDiv, - .fmul, .@"fmul fast" => &llvm.Builder.buildFMul, - .frem, .@"frem fast" => &llvm.Builder.buildFRem, - .fsub, .@"fsub fast" => &llvm.Builder.buildFSub, - .lshr => &llvm.Builder.buildLShr, - .@"lshr exact" => &llvm.Builder.buildLShrExact, - .mul => &llvm.Builder.buildMul, - .@"mul nsw" => &llvm.Builder.buildNSWMul, - .@"mul nuw" => &llvm.Builder.buildNUWMul, - .@"or" => &llvm.Builder.buildOr, - .sdiv => &llvm.Builder.buildSDiv, - .@"sdiv exact" => &llvm.Builder.buildExactSDiv, - .shl => &llvm.Builder.buildShl, - .@"shl nsw" => &llvm.Builder.buildNSWShl, - .@"shl nuw" => &llvm.Builder.buildNUWShl, - .srem => &llvm.Builder.buildSRem, - .sub => &llvm.Builder.buildSub, - .@"sub nsw" => &llvm.Builder.buildNSWSub, - .@"sub nuw" => &llvm.Builder.buildNUWSub, - .udiv => &llvm.Builder.buildUDiv, - .@"udiv exact" => &llvm.Builder.buildExactUDiv, - .urem => &llvm.Builder.buildURem, - .xor => &llvm.Builder.buildXor, - else => unreachable, - }(self.llvm.builder, lhs.toLlvm(self), rhs.toLlvm(self), instruction.llvmName(self))); - } return instruction.toValue(); } @@ -5226,13 +5173,6 @@ pub const WipFunction = struct { .index = index, }), }); - if (self.builder.useLibLlvm()) self.llvm.instructions.appendAssumeCapacity( - self.llvm.builder.buildExtractElement( - val.toLlvm(self), - index.toLlvm(self), - instruction.llvmName(self), - ), - ); return instruction.toValue(); } @@ -5254,14 +5194,6 @@ pub const WipFunction = struct { .index = index, }), }); - if (self.builder.useLibLlvm()) self.llvm.instructions.appendAssumeCapacity( - self.llvm.builder.buildInsertElement( - val.toLlvm(self), - elem.toLlvm(self), - index.toLlvm(self), - instruction.llvmName(self), - ), - ); return instruction.toValue(); } @@ -5284,14 +5216,6 @@ pub const WipFunction = struct { .mask = mask, }), }); - if (self.builder.useLibLlvm()) self.llvm.instructions.appendAssumeCapacity( - self.llvm.builder.buildShuffleVector( - lhs.toLlvm(self), - rhs.toLlvm(self), - mask.toLlvm(self), - instruction.llvmName(self), - ), - ); return instruction.toValue(); } @@ -5303,10 +5227,9 @@ pub const WipFunction = struct { ) Allocator.Error!Value { const scalar_ty = try ty.changeLength(1, self.builder); const mask_ty = try ty.changeScalar(.i32, self.builder); - const zero = try self.builder.intConst(.i32, 0); const poison = try self.builder.poisonValue(scalar_ty); - const mask = try self.builder.splatValue(mask_ty, zero); - const scalar = try self.insertElement(poison, elem, zero.toValue(), name); + const mask = try self.builder.splatValue(mask_ty, .@"0"); + const scalar = try self.insertElement(poison, elem, .@"0", name); return self.shuffleVector(scalar, poison, mask, name); } @@ -5327,13 +5250,6 @@ pub const WipFunction = struct { }), }); self.extra.appendSliceAssumeCapacity(indices); - if (self.builder.useLibLlvm()) { - const llvm_name = instruction.llvmName(self); - var cur = val.toLlvm(self); - for (indices) |index| - cur = self.llvm.builder.buildExtractValue(cur, @intCast(index), llvm_name); - self.llvm.instructions.appendAssumeCapacity(cur); - } return instruction.toValue(); } @@ -5356,35 +5272,6 @@ pub const WipFunction = struct { }), }); self.extra.appendSliceAssumeCapacity(indices); - if (self.builder.useLibLlvm()) { - const ExpectedContents = [expected_gep_indices_len]*llvm.Value; - var stack align(@alignOf(ExpectedContents)) = - std.heap.stackFallback(@sizeOf(ExpectedContents), self.builder.gpa); - const allocator = stack.get(); - - const llvm_name = instruction.llvmName(self); - const llvm_vals = try allocator.alloc(*llvm.Value, indices.len); - defer allocator.free(llvm_vals); - llvm_vals[0] = val.toLlvm(self); - for (llvm_vals[1..], llvm_vals[0 .. llvm_vals.len - 1], indices[0 .. indices.len - 1]) | - *cur_val, - prev_val, - index, - | cur_val.* = self.llvm.builder.buildExtractValue(prev_val, @intCast(index), llvm_name); - - var depth: usize = llvm_vals.len; - var cur = elem.toLlvm(self); - while (depth > 0) { - depth -= 1; - cur = self.llvm.builder.buildInsertValue( - llvm_vals[depth], - cur, - @intCast(indices[depth]), - llvm_name, - ); - } - self.llvm.instructions.appendAssumeCapacity(cur); - } return instruction.toValue(); } @@ -5420,19 +5307,13 @@ pub const WipFunction = struct { }, .data = self.addExtraAssumeCapacity(Instruction.Alloca{ .type = ty, - .len = len, + .len = switch (len) { + .none => .@"1", + else => len, + }, .info = .{ .alignment = alignment, .addr_space = addr_space }, }), }); - if (self.builder.useLibLlvm()) { - const llvm_instruction = self.llvm.builder.buildAllocaInAddressSpace( - ty.toLlvm(self.builder), - @intFromEnum(addr_space), - instruction.llvmName(self), - ); - if (alignment.toByteUnits()) |bytes| llvm_instruction.setAlignment(@intCast(bytes)); - self.llvm.instructions.appendAssumeCapacity(llvm_instruction); - } return instruction.toValue(); } @@ -5478,17 +5359,6 @@ pub const WipFunction = struct { .ptr = ptr, }), }); - if (self.builder.useLibLlvm()) { - const llvm_instruction = self.llvm.builder.buildLoad( - ty.toLlvm(self.builder), - ptr.toLlvm(self), - instruction.llvmName(self), - ); - if (access_kind == .@"volatile") llvm_instruction.setVolatile(.True); - if (ordering != .none) llvm_instruction.setOrdering(ordering.toLlvm()); - if (alignment.toByteUnits()) |bytes| llvm_instruction.setAlignment(@intCast(bytes)); - self.llvm.instructions.appendAssumeCapacity(llvm_instruction); - } return instruction.toValue(); } @@ -5532,13 +5402,6 @@ pub const WipFunction = struct { .ptr = ptr, }), }); - if (self.builder.useLibLlvm()) { - const llvm_instruction = self.llvm.builder.buildStore(val.toLlvm(self), ptr.toLlvm(self)); - if (access_kind == .@"volatile") llvm_instruction.setVolatile(.True); - if (ordering != .none) llvm_instruction.setOrdering(ordering.toLlvm()); - if (alignment.toByteUnits()) |bytes| llvm_instruction.setAlignment(@intCast(bytes)); - self.llvm.instructions.appendAssumeCapacity(llvm_instruction); - } return instruction; } @@ -5556,13 +5419,6 @@ pub const WipFunction = struct { .success_ordering = ordering, }), }); - if (self.builder.useLibLlvm()) self.llvm.instructions.appendAssumeCapacity( - self.llvm.builder.buildFence( - ordering.toLlvm(), - llvm.Bool.fromBool(sync_scope == .singlethread), - "", - ), - ); return instruction; } @@ -5605,25 +5461,6 @@ pub const WipFunction = struct { .new = new, }), }); - if (self.builder.useLibLlvm()) { - const llvm_instruction = self.llvm.builder.buildAtomicCmpXchg( - ptr.toLlvm(self), - cmp.toLlvm(self), - new.toLlvm(self), - success_ordering.toLlvm(), - failure_ordering.toLlvm(), - llvm.Bool.fromBool(sync_scope == .singlethread), - ); - if (kind == .weak) llvm_instruction.setWeak(.True); - if (access_kind == .@"volatile") llvm_instruction.setVolatile(.True); - if (alignment.toByteUnits()) |bytes| llvm_instruction.setAlignment(@intCast(bytes)); - const llvm_name = instruction.llvmName(self); - if (llvm_name.len > 0) llvm_instruction.setValueName( - llvm_name.ptr, - @intCast(llvm_name.len), - ); - self.llvm.instructions.appendAssumeCapacity(llvm_instruction); - } return instruction.toValue(); } @@ -5656,23 +5493,6 @@ pub const WipFunction = struct { .val = val, }), }); - if (self.builder.useLibLlvm()) { - const llvm_instruction = self.llvm.builder.buildAtomicRmw( - operation.toLlvm(), - ptr.toLlvm(self), - val.toLlvm(self), - ordering.toLlvm(), - llvm.Bool.fromBool(sync_scope == .singlethread), - ); - if (access_kind == .@"volatile") llvm_instruction.setVolatile(.True); - if (alignment.toByteUnits()) |bytes| llvm_instruction.setAlignment(@intCast(bytes)); - const llvm_name = instruction.llvmName(self); - if (llvm_name.len > 0) llvm_instruction.setValueName( - llvm_name.ptr, - @intCast(llvm_name.len), - ); - self.llvm.instructions.appendAssumeCapacity(llvm_instruction); - } return instruction.toValue(); } @@ -5732,28 +5552,6 @@ pub const WipFunction = struct { }), }); self.extra.appendSliceAssumeCapacity(@ptrCast(indices)); - if (self.builder.useLibLlvm()) { - const ExpectedContents = [expected_gep_indices_len]*llvm.Value; - var stack align(@alignOf(ExpectedContents)) = - std.heap.stackFallback(@sizeOf(ExpectedContents), self.builder.gpa); - const allocator = stack.get(); - - const llvm_indices = try allocator.alloc(*llvm.Value, indices.len); - defer allocator.free(llvm_indices); - for (llvm_indices, indices) |*llvm_index, index| llvm_index.* = index.toLlvm(self); - - self.llvm.instructions.appendAssumeCapacity(switch (kind) { - .normal => &llvm.Builder.buildGEP, - .inbounds => &llvm.Builder.buildInBoundsGEP, - }( - self.llvm.builder, - ty.toLlvm(self.builder), - base.toLlvm(self), - llvm_indices.ptr, - @intCast(llvm_indices.len), - instruction.llvmName(self), - )); - } return instruction.toValue(); } @@ -5765,9 +5563,7 @@ pub const WipFunction = struct { name: []const u8, ) Allocator.Error!Value { assert(ty.isStruct(self.builder)); - return self.gep(.inbounds, ty, base, &.{ - try self.builder.intValue(.i32, 0), try self.builder.intValue(.i32, index), - }, name); + return self.gep(.inbounds, ty, base, &.{ .@"0", try self.builder.intValue(.i32, index) }, name); } pub fn conv( @@ -5815,22 +5611,6 @@ pub const WipFunction = struct { .type = ty, }), }); - if (self.builder.useLibLlvm()) self.llvm.instructions.appendAssumeCapacity(switch (tag) { - .addrspacecast => &llvm.Builder.buildAddrSpaceCast, - .bitcast => &llvm.Builder.buildBitCast, - .fpext => &llvm.Builder.buildFPExt, - .fptosi => &llvm.Builder.buildFPToSI, - .fptoui => &llvm.Builder.buildFPToUI, - .fptrunc => &llvm.Builder.buildFPTrunc, - .inttoptr => &llvm.Builder.buildIntToPtr, - .ptrtoint => &llvm.Builder.buildPtrToInt, - .sext => &llvm.Builder.buildSExt, - .sitofp => &llvm.Builder.buildSIToFP, - .trunc => &llvm.Builder.buildTrunc, - .uitofp => &llvm.Builder.buildUIToFP, - .zext => &llvm.Builder.buildZExt, - else => unreachable, - }(self.llvm.builder, val.toLlvm(self), ty.toLlvm(self.builder), instruction.llvmName(self))); return instruction.toValue(); } @@ -5843,7 +5623,7 @@ pub const WipFunction = struct { ) Allocator.Error!Value { return self.cmpTag(switch (cond) { inline else => |tag| @field(Instruction.Tag, "icmp " ++ @tagName(tag)), - }, @intFromEnum(cond), lhs, rhs, name); + }, lhs, rhs, name); } pub fn fcmp( @@ -5861,7 +5641,7 @@ pub const WipFunction = struct { .fast => "fast ", } ++ @tagName(cond_tag)), }, - }, @intFromEnum(cond), lhs, rhs, name); + }, lhs, rhs, name); } pub const WipPhi = struct { @@ -5877,7 +5657,7 @@ pub const WipFunction = struct { vals: []const Value, blocks: []const Block.Index, wip: *WipFunction, - ) (if (build_options.have_llvm) Allocator.Error else error{})!void { + ) void { const incoming_len = self.block.ptrConst(wip).incoming; assert(vals.len == incoming_len and blocks.len == incoming_len); const instruction = wip.instructions.get(@intFromEnum(self.instruction)); @@ -5885,26 +5665,6 @@ pub const WipFunction = struct { for (vals) |val| assert(val.typeOfWip(wip) == extra.data.type); @memcpy(extra.trail.nextMut(incoming_len, Value, wip), vals); @memcpy(extra.trail.nextMut(incoming_len, Block.Index, wip), blocks); - if (wip.builder.useLibLlvm()) { - const ExpectedContents = extern struct { - values: [expected_incoming_len]*llvm.Value, - blocks: [expected_incoming_len]*llvm.BasicBlock, - }; - var stack align(@alignOf(ExpectedContents)) = - std.heap.stackFallback(@sizeOf(ExpectedContents), wip.builder.gpa); - const allocator = stack.get(); - - const llvm_vals = try allocator.alloc(*llvm.Value, incoming_len); - defer allocator.free(llvm_vals); - const llvm_blocks = try allocator.alloc(*llvm.BasicBlock, incoming_len); - defer allocator.free(llvm_blocks); - - for (llvm_vals, vals) |*llvm_val, incoming_val| llvm_val.* = incoming_val.toLlvm(wip); - for (llvm_blocks, blocks) |*llvm_block, incoming_block| - llvm_block.* = incoming_block.toLlvm(wip); - self.instruction.toLlvm(wip) - .addIncoming(llvm_vals.ptr, llvm_blocks.ptr, @intCast(incoming_len)); - } } }; @@ -5970,53 +5730,6 @@ pub const WipFunction = struct { }), }); self.extra.appendSliceAssumeCapacity(@ptrCast(args)); - if (self.builder.useLibLlvm()) { - const ExpectedContents = [expected_args_len]*llvm.Value; - var stack align(@alignOf(ExpectedContents)) = - std.heap.stackFallback(@sizeOf(ExpectedContents), self.builder.gpa); - const allocator = stack.get(); - - const llvm_args = try allocator.alloc(*llvm.Value, args.len); - defer allocator.free(llvm_args); - for (llvm_args, args) |*llvm_arg, arg_val| llvm_arg.* = arg_val.toLlvm(self); - - switch (kind) { - .normal, - .musttail, - .notail, - .tail, - => self.llvm.builder.setFastMath(false), - .fast, - .musttail_fast, - .notail_fast, - .tail_fast, - => self.llvm.builder.setFastMath(true), - } - const llvm_instruction = self.llvm.builder.buildCall( - ty.toLlvm(self.builder), - callee.toLlvm(self), - llvm_args.ptr, - @intCast(llvm_args.len), - switch (ret_ty) { - .void => "", - else => instruction.llvmName(self), - }, - ); - llvm_instruction.setInstructionCallConv(call_conv.toLlvm()); - llvm_instruction.setTailCallKind(switch (kind) { - .normal, .fast => .None, - .musttail, .musttail_fast => .MustTail, - .notail, .notail_fast => .NoTail, - .tail, .tail_fast => .Tail, - }); - for (0.., function_attributes.slice(self.builder)) |index, attributes| { - for (attributes.slice(self.builder)) |attribute| llvm_instruction.addCallSiteAttribute( - @as(llvm.AttributeIndex, @intCast(index)) -% 1, - attribute.toLlvm(self.builder), - ); - } - self.llvm.instructions.appendAssumeCapacity(llvm_instruction); - } return instruction.toValue(); } @@ -6117,16 +5830,25 @@ pub const WipFunction = struct { .type = ty, }), }); - if (self.builder.useLibLlvm()) self.llvm.instructions.appendAssumeCapacity( - self.llvm.builder.buildVAArg( - list.toLlvm(self), - ty.toLlvm(self.builder), - instruction.llvmName(self), - ), - ); return instruction.toValue(); } + pub fn debugValue(self: *WipFunction, value: Value) Allocator.Error!Metadata { + if (self.strip) return .none; + return switch (value.unwrap()) { + .instruction => |instr_index| blk: { + const gop = try self.debug_values.getOrPut(self.builder.gpa, instr_index); + + const metadata: Metadata = @enumFromInt(Metadata.first_local_metadata + gop.index); + if (!gop.found_existing) gop.key_ptr.* = instr_index; + + break :blk metadata; + }, + .constant => |constant| try self.builder.debugConstant(constant), + .metadata => |metadata| metadata, + }; + } + pub fn finish(self: *WipFunction) Allocator.Error!void { const gpa = self.builder.gpa; const function = self.function.ptr(self.builder); @@ -6146,6 +5868,7 @@ pub const WipFunction = struct { @intFromEnum(instruction) ].toValue(), .constant => |constant| constant.toValue(), + .metadata => |metadata| metadata.toValue(), }; } } = .{ .items = try gpa.alloc(Instruction.Index, self.instructions.len) }; @@ -6154,9 +5877,15 @@ pub const WipFunction = struct { const names = try gpa.alloc(String, final_instructions_len); errdefer gpa.free(names); - const metadata = - if (self.builder.strip) null else try gpa.alloc(Metadata, final_instructions_len); - errdefer if (metadata) |new_metadata| gpa.free(new_metadata); + const value_indices = try gpa.alloc(u32, final_instructions_len); + errdefer gpa.free(value_indices); + + var debug_locations: std.AutoHashMapUnmanaged(Instruction.Index, DebugLocation) = .{}; + errdefer debug_locations.deinit(gpa); + try debug_locations.ensureUnusedCapacity(gpa, @intCast(self.debug_locations.count())); + + const debug_values = try gpa.alloc(Instruction.Index, self.debug_values.count()); + errdefer gpa.free(debug_values); var wip_extra: struct { index: Instruction.ExtraIndex = 0, @@ -6179,7 +5908,7 @@ pub const WipFunction = struct { Instruction.Alloca.Info, Instruction.Call.Info, => @bitCast(value), - else => @compileError("bad field type: " ++ @typeName(field.type)), + else => @compileError("bad field type: " ++ field.name ++ ": " ++ @typeName(field.type)), }; wip_extra.index += 1; } @@ -6210,8 +5939,10 @@ pub const WipFunction = struct { gpa.free(function.blocks); function.blocks = &.{}; gpa.free(function.names[0..function.instructions.len]); - if (function.metadata) |old_metadata| gpa.free(old_metadata[0..function.instructions.len]); - function.metadata = null; + function.debug_locations.deinit(gpa); + function.debug_locations = .{}; + gpa.free(function.debug_values); + function.debug_values = &.{}; gpa.free(function.extra); function.extra = &.{}; @@ -6238,33 +5969,76 @@ pub const WipFunction = struct { var wip_name: struct { next_name: String = @enumFromInt(0), + next_unique_name: std.AutoHashMap(String, String), + builder: *Builder, - fn map(wip_name: *@This(), old_name: String) String { - if (old_name != .empty) return old_name; + fn map(wip_name: *@This(), name: String, sep: []const u8) Allocator.Error!String { + switch (name) { + .none => return .none, + .empty => { + assert(wip_name.next_name != .none); + defer wip_name.next_name = @enumFromInt(@intFromEnum(wip_name.next_name) + 1); + return wip_name.next_name; + }, + _ => { + assert(!name.isAnon()); + const gop = try wip_name.next_unique_name.getOrPut(name); + if (!gop.found_existing) { + gop.value_ptr.* = @enumFromInt(0); + return name; + } - const new_name = wip_name.next_name; - wip_name.next_name = @enumFromInt(@intFromEnum(new_name) + 1); - return new_name; + while (true) { + gop.value_ptr.* = @enumFromInt(@intFromEnum(gop.value_ptr.*) + 1); + const unique_name = try wip_name.builder.fmt("{r}{s}{r}", .{ + name.fmt(wip_name.builder), + sep, + gop.value_ptr.fmt(wip_name.builder), + }); + const unique_gop = try wip_name.next_unique_name.getOrPut(unique_name); + if (!unique_gop.found_existing) { + unique_gop.value_ptr.* = @enumFromInt(0); + return unique_name; + } + } + }, + } } - } = .{}; + } = .{ + .next_unique_name = std.AutoHashMap(String, String).init(gpa), + .builder = self.builder, + }; + defer wip_name.next_unique_name.deinit(); + + var value_index: u32 = 0; for (0..params_len) |param_index| { const old_argument_index: Instruction.Index = @enumFromInt(param_index); const new_argument_index: Instruction.Index = @enumFromInt(function.instructions.len); const argument = self.instructions.get(@intFromEnum(old_argument_index)); assert(argument.tag == .arg); assert(argument.data == param_index); + value_indices[function.instructions.len] = value_index; + value_index += 1; function.instructions.appendAssumeCapacity(argument); - names[@intFromEnum(new_argument_index)] = wip_name.map( - if (self.builder.strip) .empty else self.names.items[@intFromEnum(old_argument_index)], + names[@intFromEnum(new_argument_index)] = try wip_name.map( + if (self.strip) .empty else self.names.items[@intFromEnum(old_argument_index)], + ".", ); + if (self.debug_locations.get(old_argument_index)) |location| { + debug_locations.putAssumeCapacity(new_argument_index, location); + } + if (self.debug_values.getIndex(old_argument_index)) |index| { + debug_values[index] = new_argument_index; + } } for (self.blocks.items) |current_block| { const new_block_index: Instruction.Index = @enumFromInt(function.instructions.len); + value_indices[function.instructions.len] = value_index; function.instructions.appendAssumeCapacity(.{ .tag = .block, .data = current_block.incoming, }); - names[@intFromEnum(new_block_index)] = wip_name.map(current_block.name); + names[@intFromEnum(new_block_index)] = try wip_name.map(current_block.name, ""); for (current_block.instructions.items) |old_instruction_index| { const new_instruction_index: Instruction.Index = @enumFromInt(function.instructions.len); @@ -6565,10 +6339,21 @@ pub const WipFunction = struct { }, } function.instructions.appendAssumeCapacity(instruction); - names[@intFromEnum(new_instruction_index)] = wip_name.map(if (self.builder.strip) + names[@intFromEnum(new_instruction_index)] = try wip_name.map(if (self.strip) if (old_instruction_index.hasResultWip(self)) .empty else .none else - self.names.items[@intFromEnum(old_instruction_index)]); + self.names.items[@intFromEnum(old_instruction_index)], "."); + + if (self.debug_locations.get(old_instruction_index)) |location| { + debug_locations.putAssumeCapacity(new_instruction_index, location); + } + + if (self.debug_values.getIndex(old_instruction_index)) |index| { + debug_values[index] = new_instruction_index; + } + + value_indices[@intFromEnum(new_instruction_index)] = value_index; + if (old_instruction_index.hasResultWip(self)) value_index += 1; } } @@ -6576,28 +6361,26 @@ pub const WipFunction = struct { function.extra = wip_extra.finish(); function.blocks = blocks; function.names = names.ptr; - function.metadata = if (metadata) |new_metadata| new_metadata.ptr else null; + function.value_indices = value_indices.ptr; + function.strip = self.strip; + function.debug_locations = debug_locations; + function.debug_values = debug_values; } pub fn deinit(self: *WipFunction) void { self.extra.deinit(self.builder.gpa); - self.metadata.deinit(self.builder.gpa); + self.debug_values.deinit(self.builder.gpa); + self.debug_locations.deinit(self.builder.gpa); self.names.deinit(self.builder.gpa); self.instructions.deinit(self.builder.gpa); for (self.blocks.items) |*b| b.instructions.deinit(self.builder.gpa); self.blocks.deinit(self.builder.gpa); - if (self.builder.useLibLlvm()) { - self.llvm.instructions.deinit(self.builder.gpa); - self.llvm.blocks.deinit(self.builder.gpa); - self.llvm.builder.dispose(); - } self.* = undefined; } fn cmpTag( self: *WipFunction, tag: Instruction.Tag, - cond: u32, lhs: Value, rhs: Value, name: []const u8, @@ -6657,113 +6440,6 @@ pub const WipFunction = struct { .rhs = rhs, }), }); - if (self.builder.useLibLlvm()) { - switch (tag) { - .@"fcmp false", - .@"fcmp oeq", - .@"fcmp oge", - .@"fcmp ogt", - .@"fcmp ole", - .@"fcmp olt", - .@"fcmp one", - .@"fcmp ord", - .@"fcmp true", - .@"fcmp ueq", - .@"fcmp uge", - .@"fcmp ugt", - .@"fcmp ule", - .@"fcmp ult", - .@"fcmp une", - .@"fcmp uno", - => self.llvm.builder.setFastMath(false), - .@"fcmp fast false", - .@"fcmp fast oeq", - .@"fcmp fast oge", - .@"fcmp fast ogt", - .@"fcmp fast ole", - .@"fcmp fast olt", - .@"fcmp fast one", - .@"fcmp fast ord", - .@"fcmp fast true", - .@"fcmp fast ueq", - .@"fcmp fast uge", - .@"fcmp fast ugt", - .@"fcmp fast ule", - .@"fcmp fast ult", - .@"fcmp fast une", - .@"fcmp fast uno", - => self.llvm.builder.setFastMath(true), - .@"icmp eq", - .@"icmp ne", - .@"icmp sge", - .@"icmp sgt", - .@"icmp sle", - .@"icmp slt", - .@"icmp uge", - .@"icmp ugt", - .@"icmp ule", - .@"icmp ult", - => {}, - else => unreachable, - } - self.llvm.instructions.appendAssumeCapacity(switch (tag) { - .@"fcmp false", - .@"fcmp fast false", - .@"fcmp fast oeq", - .@"fcmp fast oge", - .@"fcmp fast ogt", - .@"fcmp fast ole", - .@"fcmp fast olt", - .@"fcmp fast one", - .@"fcmp fast ord", - .@"fcmp fast true", - .@"fcmp fast ueq", - .@"fcmp fast uge", - .@"fcmp fast ugt", - .@"fcmp fast ule", - .@"fcmp fast ult", - .@"fcmp fast une", - .@"fcmp fast uno", - .@"fcmp oeq", - .@"fcmp oge", - .@"fcmp ogt", - .@"fcmp ole", - .@"fcmp olt", - .@"fcmp one", - .@"fcmp ord", - .@"fcmp true", - .@"fcmp ueq", - .@"fcmp uge", - .@"fcmp ugt", - .@"fcmp ule", - .@"fcmp ult", - .@"fcmp une", - .@"fcmp uno", - => self.llvm.builder.buildFCmp( - @enumFromInt(cond), - lhs.toLlvm(self), - rhs.toLlvm(self), - instruction.llvmName(self), - ), - .@"icmp eq", - .@"icmp ne", - .@"icmp sge", - .@"icmp sgt", - .@"icmp sle", - .@"icmp slt", - .@"icmp uge", - .@"icmp ugt", - .@"icmp ule", - .@"icmp ult", - => self.llvm.builder.buildICmp( - @enumFromInt(cond), - lhs.toLlvm(self), - rhs.toLlvm(self), - instruction.llvmName(self), - ), - else => unreachable, - }); - } return instruction.toValue(); } @@ -6785,16 +6461,6 @@ pub const WipFunction = struct { .data = self.addExtraAssumeCapacity(Instruction.Phi{ .type = ty }), }); _ = self.extra.addManyAsSliceAssumeCapacity(incoming * 2); - if (self.builder.useLibLlvm()) { - switch (tag) { - .phi => self.llvm.builder.setFastMath(false), - .@"phi fast" => self.llvm.builder.setFastMath(true), - else => unreachable, - } - self.llvm.instructions.appendAssumeCapacity( - self.llvm.builder.buildPhi(ty.toLlvm(self.builder), instruction.llvmName(self)), - ); - } return .{ .block = self.cursor.block, .instruction = instruction }; } @@ -6822,19 +6488,6 @@ pub const WipFunction = struct { .rhs = rhs, }), }); - if (self.builder.useLibLlvm()) { - switch (tag) { - .select => self.llvm.builder.setFastMath(false), - .@"select fast" => self.llvm.builder.setFastMath(true), - else => unreachable, - } - self.llvm.instructions.appendAssumeCapacity(self.llvm.builder.buildSelect( - cond.toLlvm(self), - lhs.toLlvm(self), - rhs.toLlvm(self), - instruction.llvmName(self), - )); - } return instruction.toValue(); } @@ -6857,28 +6510,27 @@ pub const WipFunction = struct { ) Allocator.Error!Instruction.Index { const block_instructions = &self.cursor.block.ptr(self).instructions; try self.instructions.ensureUnusedCapacity(self.builder.gpa, 1); - if (!self.builder.strip) try self.names.ensureUnusedCapacity(self.builder.gpa, 1); + if (!self.strip) { + try self.names.ensureUnusedCapacity(self.builder.gpa, 1); + try self.debug_locations.ensureUnusedCapacity(self.builder.gpa, 1); + } try block_instructions.ensureUnusedCapacity(self.builder.gpa, 1); - if (self.builder.useLibLlvm()) - try self.llvm.instructions.ensureUnusedCapacity(self.builder.gpa, 1); const final_name = if (name) |n| - if (self.builder.strip) .empty else try self.builder.string(n) + if (self.strip) .empty else try self.builder.string(n) else .none; - if (self.builder.useLibLlvm()) self.llvm.builder.positionBuilder( - self.cursor.block.toLlvm(self), - for (block_instructions.items[self.cursor.instruction..]) |instruction_index| { - const llvm_instruction = - self.llvm.instructions.items[@intFromEnum(instruction_index)]; - // TODO: remove when constant propagation is implemented - if (!llvm_instruction.isConstant().toBool()) break llvm_instruction; - } else null, - ); - const index: Instruction.Index = @enumFromInt(self.instructions.len); self.instructions.appendAssumeCapacity(instruction); - if (!self.builder.strip) self.names.appendAssumeCapacity(final_name); + if (!self.strip) { + self.names.appendAssumeCapacity(final_name); + if (block_instructions.items.len == 0 or + !std.meta.eql(self.debug_location, self.prev_debug_location)) + { + self.debug_locations.putAssumeCapacity(index, self.debug_location); + self.prev_debug_location = self.debug_location; + } + } block_instructions.insertAssumeCapacity(self.cursor.instruction, index); self.cursor.instruction += 1; return index; @@ -6901,7 +6553,7 @@ pub const WipFunction = struct { Instruction.Alloca.Info, Instruction.Call.Info, => @bitCast(value), - else => @compileError("bad field type: " ++ @typeName(field.type)), + else => @compileError("bad field type: " ++ field.name ++ ": " ++ @typeName(field.type)), }); } return result; @@ -6949,7 +6601,7 @@ pub const WipFunction = struct { Instruction.Alloca.Info, Instruction.Call.Info, => @bitCast(value), - else => @compileError("bad field type: " ++ @typeName(field.type)), + else => @compileError("bad field type: " ++ field.name ++ ": " ++ @typeName(field.type)), }; return .{ .data = result, @@ -6977,24 +6629,6 @@ pub const FloatCondition = enum(u4) { ult = 12, ule = 13, une = 14, - - fn toLlvm(self: FloatCondition) llvm.RealPredicate { - return switch (self) { - .oeq => .OEQ, - .ogt => .OGT, - .oge => .OGE, - .olt => .OLT, - .ole => .OLE, - .one => .ONE, - .ord => .ORD, - .uno => .UNO, - .ueq => .UEQ, - .ugt => .UGT, - .uge => .UGE, - .ult => .ULT, - .uno => .UNE, - }; - } }; pub const IntegerCondition = enum(u6) { @@ -7008,20 +6642,6 @@ pub const IntegerCondition = enum(u6) { sge = 39, slt = 40, sle = 41, - - fn toLlvm(self: IntegerCondition) llvm.IntPredicate { - return switch (self) { - .eq => .EQ, - .ne => .NE, - .ugt => .UGT, - .uge => .UGE, - .ult => .ULT, - .sgt => .SGT, - .sge => .SGE, - .slt => .SLT, - .sle => .SLE, - }; - } }; pub const MemoryAccessKind = enum(u1) { @@ -7058,10 +6678,10 @@ pub const AtomicOrdering = enum(u3) { none = 0, unordered = 1, monotonic = 2, - acquire = 4, - release = 5, - acq_rel = 6, - seq_cst = 7, + acquire = 3, + release = 4, + acq_rel = 5, + seq_cst = 6, pub fn format( self: AtomicOrdering, @@ -7071,18 +6691,6 @@ pub const AtomicOrdering = enum(u3) { ) @TypeOf(writer).Error!void { if (self != .none) try writer.print("{s}{s}", .{ prefix, @tagName(self) }); } - - fn toLlvm(self: AtomicOrdering) llvm.AtomicOrdering { - return switch (self) { - .none => .NotAtomic, - .unordered => .Unordered, - .monotonic => .Monotonic, - .acquire => .Acquire, - .release => .Release, - .acq_rel => .AcquireRelease, - .seq_cst => .SequentiallyConsistent, - }; - } }; const MemoryAccessInfo = packed struct(u32) { @@ -7095,7 +6703,8 @@ const MemoryAccessInfo = packed struct(u32) { _: u13 = undefined, }; -pub const FastMath = packed struct(u32) { +pub const FastMath = packed struct(u8) { + unsafe_algebra: bool = false, // Legacy nnan: bool = false, ninf: bool = false, nsz: bool = false, @@ -7130,11 +6739,13 @@ pub const FastMathKind = enum { pub const Constant = enum(u32) { false, true, + @"0", + @"1", none, - no_init = 1 << 31, + no_init = (1 << 30) - 1, _, - const first_global: Constant = @enumFromInt(1 << 30); + const first_global: Constant = @enumFromInt(1 << 29); pub const Tag = enum(u7) { positive_integer, @@ -7152,7 +6763,6 @@ pub const Constant = enum(u32) { packed_structure, array, string, - string_null, vector, splat, zeroinitializer, @@ -7212,6 +6822,49 @@ pub const Constant = enum(u32) { @"asm sideeffect inteldialect unwind", @"asm alignstack inteldialect unwind", @"asm sideeffect alignstack inteldialect unwind", + + pub fn toBinaryOpcode(self: Tag) BinaryOpcode { + return switch (self) { + .add, + .@"add nsw", + .@"add nuw", + => .add, + .sub, + .@"sub nsw", + .@"sub nuw", + => .sub, + .mul, + .@"mul nsw", + .@"mul nuw", + => .mul, + .shl => .shl, + .lshr => .lshr, + .ashr => .ashr, + .@"and" => .@"and", + .@"or" => .@"or", + .xor => .xor, + else => unreachable, + }; + } + + pub fn toCastOpcode(self: Tag) CastOpcode { + return switch (self) { + .trunc => .trunc, + .zext => .zext, + .sext => .sext, + .fptoui => .fptoui, + .fptosi => .fptosi, + .uitofp => .uitofp, + .sitofp => .sitofp, + .fptrunc => .fptrunc, + .fpext => .fpext, + .ptrtoint => .ptrtoint, + .inttoptr => .inttoptr, + .bitcast => .bitcast, + .addrspacecast => .addrspacecast, + else => unreachable, + }; + } }; pub const Item = struct { @@ -7364,11 +7017,8 @@ pub const Constant = enum(u32) { .vector, => builder.constantExtraData(Aggregate, item.data).type, .splat => builder.constantExtraData(Splat, item.data).type, - .string, - .string_null, - => builder.arrayTypeAssumeCapacity( - @as(String, @enumFromInt(item.data)).slice(builder).?.len + - @intFromBool(item.tag == .string_null), + .string => builder.arrayTypeAssumeCapacity( + @as(String, @enumFromInt(item.data)).slice(builder).?.len, .i8, ), .blockaddress => builder.ptrTypeAssumeCapacity( @@ -7574,7 +7224,7 @@ pub const Constant = enum(u32) { @ptrCast(data.builder.constant_limbs.items[item.data..][0..Integer.limbs]); const limbs = data.builder.constant_limbs .items[item.data + Integer.limbs ..][0..extra.limbs_len]; - const bigint = std.math.big.int.Const{ + const bigint: std.math.big.int.Const = .{ .limbs = limbs, .positive = tag == .positive_integer, }; @@ -7616,17 +7266,31 @@ pub const Constant = enum(u32) { }; } }; + const Mantissa64 = std.meta.FieldType(Float.Repr(f64), .mantissa); const Exponent32 = std.meta.FieldType(Float.Repr(f32), .exponent); const Exponent64 = std.meta.FieldType(Float.Repr(f64), .exponent); + const repr: Float.Repr(f32) = @bitCast(item.data); + const denormal_shift = switch (repr.exponent) { + std.math.minInt(Exponent32) => @as( + std.math.Log2Int(Mantissa64), + @clz(repr.mantissa), + ) + 1, + else => 0, + }; try writer.print("0x{X:0>16}", .{@as(u64, @bitCast(Float.Repr(f64){ .mantissa = std.math.shl( - std.meta.FieldType(Float.Repr(f64), .mantissa), + Mantissa64, repr.mantissa, - std.math.floatMantissaBits(f64) - std.math.floatMantissaBits(f32), + std.math.floatMantissaBits(f64) - std.math.floatMantissaBits(f32) + + denormal_shift, ), .exponent = switch (repr.exponent) { - std.math.minInt(Exponent32) => std.math.minInt(Exponent64), + std.math.minInt(Exponent32) => if (repr.mantissa > 0) + @as(Exponent64, std.math.floatExponentMin(f32) + + std.math.floatExponentMax(f64)) - denormal_shift + else + std.math.minInt(Exponent64), else => @as(Exponent64, repr.exponent) + (std.math.floatExponentMax(f64) - std.math.floatExponentMax(f32)), std.math.maxInt(Exponent32) => std.math.maxInt(Exponent64), @@ -7703,13 +7367,9 @@ pub const Constant = enum(u32) { } try writer.writeByte('>'); }, - inline .string, - .string_null, - => |tag| try writer.print("c{\"" ++ switch (tag) { - .string => "", - .string_null => "@", - else => unreachable, - } ++ "}", .{@as(String, @enumFromInt(item.data)).fmt(data.builder)}), + .string => try writer.print("c{\"}", .{ + @as(String, @enumFromInt(item.data)).fmt(data.builder), + }), .blockaddress => |tag| { const extra = data.builder.constantExtraData(BlockAddress, item.data); const function = extra.function.ptrConst(data.builder); @@ -7859,40 +7519,37 @@ pub const Constant = enum(u32) { pub fn fmt(self: Constant, builder: *Builder) std.fmt.Formatter(format) { return .{ .data = .{ .constant = self, .builder = builder } }; } - - pub fn toLlvm(self: Constant, builder: *const Builder) *llvm.Value { - assert(builder.useLibLlvm()); - const llvm_value = switch (self.unwrap()) { - .constant => |constant| builder.llvm.constants.items[constant], - .global => |global| return global.toLlvm(builder), - }; - const global = builder.llvm.replacements.get(llvm_value) orelse return llvm_value; - return global.toLlvm(builder); - } }; pub const Value = enum(u32) { none = std.math.maxInt(u31), false = first_constant + @intFromEnum(Constant.false), true = first_constant + @intFromEnum(Constant.true), + @"0" = first_constant + @intFromEnum(Constant.@"0"), + @"1" = first_constant + @intFromEnum(Constant.@"1"), _, - const first_constant = 1 << 31; + const first_constant = 1 << 30; + const first_metadata = 1 << 31; pub fn unwrap(self: Value) union(enum) { instruction: Function.Instruction.Index, constant: Constant, + metadata: Metadata, } { return if (@intFromEnum(self) < first_constant) .{ .instruction = @enumFromInt(@intFromEnum(self)) } + else if (@intFromEnum(self) < first_metadata) + .{ .constant = @enumFromInt(@intFromEnum(self) - first_constant) } else - .{ .constant = @enumFromInt(@intFromEnum(self) - first_constant) }; + .{ .metadata = @enumFromInt(@intFromEnum(self) - first_metadata) }; } pub fn typeOfWip(self: Value, wip: *const WipFunction) Type { return switch (self.unwrap()) { .instruction => |instruction| instruction.typeOfWip(wip), .constant => |constant| constant.typeOf(wip.builder), + .metadata => .metadata, }; } @@ -7900,12 +7557,13 @@ pub const Value = enum(u32) { return switch (self.unwrap()) { .instruction => |instruction| instruction.typeOf(function, builder), .constant => |constant| constant.typeOf(builder), + .metadata => .metadata, }; } pub fn toConst(self: Value) ?Constant { return switch (self.unwrap()) { - .instruction => null, + .instruction, .metadata => null, .constant => |constant| constant, }; } @@ -7931,34 +7589,717 @@ pub const Value = enum(u32) { .constant = constant, .builder = data.builder, }, fmt_str, fmt_opts, writer), + .metadata => unreachable, } } pub fn fmt(self: Value, function: Function.Index, builder: *Builder) std.fmt.Formatter(format) { return .{ .data = .{ .value = self, .function = function, .builder = builder } }; } +}; - pub fn toLlvm(self: Value, wip: *const WipFunction) *llvm.Value { - return switch (self.unwrap()) { - .instruction => |instruction| instruction.toLlvm(wip), - .constant => |constant| constant.toLlvm(wip.builder), - }; +pub const MetadataString = enum(u32) { + none = 0, + _, + + pub fn slice(self: MetadataString, builder: *const Builder) []const u8 { + const index = @intFromEnum(self); + const start = builder.metadata_string_indices.items[index]; + const end = builder.metadata_string_indices.items[index + 1]; + return builder.metadata_string_bytes.items[start..end]; + } + + const Adapter = struct { + builder: *const Builder, + pub fn hash(_: Adapter, key: []const u8) u32 { + return @truncate(std.hash.Wyhash.hash(0, key)); + } + pub fn eql(ctx: Adapter, lhs_key: []const u8, _: void, rhs_index: usize) bool { + const rhs_metadata_string: MetadataString = @enumFromInt(rhs_index); + return std.mem.eql(u8, lhs_key, rhs_metadata_string.slice(ctx.builder)); + } + }; + + const FormatData = struct { + metadata_string: MetadataString, + builder: *const Builder, + }; + fn format( + data: FormatData, + comptime _: []const u8, + _: std.fmt.FormatOptions, + writer: anytype, + ) @TypeOf(writer).Error!void { + try printEscapedString(data.metadata_string.slice(data.builder), .always_quote, writer); + } + fn fmt(self: MetadataString, builder: *const Builder) std.fmt.Formatter(format) { + return .{ .data = .{ .metadata_string = self, .builder = builder } }; } }; -pub const Metadata = enum(u32) { _ }; +pub const Metadata = enum(u32) { + none = 0, + _, + + const first_forward_reference = 1 << 29; + const first_local_metadata = 1 << 30; + + pub const Tag = enum(u6) { + none, + file, + compile_unit, + @"compile_unit optimized", + subprogram, + @"subprogram local", + @"subprogram definition", + @"subprogram local definition", + @"subprogram optimized", + @"subprogram optimized local", + @"subprogram optimized definition", + @"subprogram optimized local definition", + lexical_block, + location, + basic_bool_type, + basic_unsigned_type, + basic_signed_type, + basic_float_type, + composite_struct_type, + composite_union_type, + composite_enumeration_type, + composite_array_type, + composite_vector_type, + derived_pointer_type, + derived_member_type, + subroutine_type, + enumerator_unsigned, + enumerator_signed_positive, + enumerator_signed_negative, + subrange, + tuple, + module_flag, + expression, + local_var, + parameter, + global_var, + @"global_var local", + global_var_expression, + constant, + + pub fn isInline(tag: Tag) bool { + return switch (tag) { + .none, + .expression, + .constant, + => true, + .file, + .compile_unit, + .@"compile_unit optimized", + .subprogram, + .@"subprogram local", + .@"subprogram definition", + .@"subprogram local definition", + .@"subprogram optimized", + .@"subprogram optimized local", + .@"subprogram optimized definition", + .@"subprogram optimized local definition", + .lexical_block, + .location, + .basic_bool_type, + .basic_unsigned_type, + .basic_signed_type, + .basic_float_type, + .composite_struct_type, + .composite_union_type, + .composite_enumeration_type, + .composite_array_type, + .composite_vector_type, + .derived_pointer_type, + .derived_member_type, + .subroutine_type, + .enumerator_unsigned, + .enumerator_signed_positive, + .enumerator_signed_negative, + .subrange, + .tuple, + .module_flag, + .local_var, + .parameter, + .global_var, + .@"global_var local", + .global_var_expression, + => false, + }; + } + }; + + pub fn isInline(self: Metadata, builder: *const Builder) bool { + return builder.metadata_items.items(.tag)[@intFromEnum(self)].isInline(); + } + + pub fn unwrap(self: Metadata, builder: *const Builder) Metadata { + var metadata = self; + while (@intFromEnum(metadata) >= Metadata.first_forward_reference and + @intFromEnum(metadata) < Metadata.first_local_metadata) + { + const index = @intFromEnum(metadata) - Metadata.first_forward_reference; + metadata = builder.metadata_forward_references.items[index]; + assert(metadata != .none); + } + return metadata; + } + + pub const Item = struct { + tag: Tag, + data: ExtraIndex, + + const ExtraIndex = u32; + }; + + pub const DIFlags = packed struct(u32) { + Visibility: enum(u2) { Zero, Private, Protected, Public } = .Zero, + FwdDecl: bool = false, + AppleBlock: bool = false, + ReservedBit4: u1 = 0, + Virtual: bool = false, + Artificial: bool = false, + Explicit: bool = false, + Prototyped: bool = false, + ObjcClassComplete: bool = false, + ObjectPointer: bool = false, + Vector: bool = false, + StaticMember: bool = false, + LValueReference: bool = false, + RValueReference: bool = false, + ExportSymbols: bool = false, + Inheritance: enum(u2) { + Zero, + SingleInheritance, + MultipleInheritance, + VirtualInheritance, + } = .Zero, + IntroducedVirtual: bool = false, + BitField: bool = false, + NoReturn: bool = false, + ReservedBit21: u1 = 0, + TypePassbyValue: bool = false, + TypePassbyReference: bool = false, + EnumClass: bool = false, + Thunk: bool = false, + NonTrivial: bool = false, + BigEndian: bool = false, + LittleEndian: bool = false, + AllCallsDescribed: bool = false, + Unused: u2 = 0, + + pub fn format( + self: DIFlags, + comptime _: []const u8, + _: std.fmt.FormatOptions, + writer: anytype, + ) @TypeOf(writer).Error!void { + var need_pipe = false; + inline for (@typeInfo(DIFlags).Struct.fields) |field| { + switch (@typeInfo(field.type)) { + .Bool => if (@field(self, field.name)) { + if (need_pipe) try writer.writeAll(" | ") else need_pipe = true; + try writer.print("DIFlag{s}", .{field.name}); + }, + .Enum => if (@field(self, field.name) != .Zero) { + if (need_pipe) try writer.writeAll(" | ") else need_pipe = true; + try writer.print("DIFlag{s}", .{@tagName(@field(self, field.name))}); + }, + .Int => assert(@field(self, field.name) == 0), + else => @compileError("bad field type: " ++ field.name ++ ": " ++ + @typeName(field.type)), + } + } + if (!need_pipe) try writer.writeByte('0'); + } + }; + + pub const File = struct { + filename: MetadataString, + directory: MetadataString, + }; + + pub const CompileUnit = struct { + pub const Options = struct { + optimized: bool, + }; + + file: Metadata, + producer: MetadataString, + enums: Metadata, + globals: Metadata, + }; + + pub const Subprogram = struct { + pub const Options = struct { + di_flags: DIFlags, + sp_flags: DISPFlags, + }; + + pub const DISPFlags = packed struct(u32) { + Virtuality: enum(u2) { Zero, Virtual, PureVirtual } = .Zero, + LocalToUnit: bool = false, + Definition: bool = false, + Optimized: bool = false, + Pure: bool = false, + Elemental: bool = false, + Recursive: bool = false, + MainSubprogram: bool = false, + Deleted: bool = false, + ReservedBit10: u1 = 0, + ObjCDirect: bool = false, + Unused: u20 = 0, + + pub fn format( + self: DISPFlags, + comptime _: []const u8, + _: std.fmt.FormatOptions, + writer: anytype, + ) @TypeOf(writer).Error!void { + var need_pipe = false; + inline for (@typeInfo(DISPFlags).Struct.fields) |field| { + switch (@typeInfo(field.type)) { + .Bool => if (@field(self, field.name)) { + if (need_pipe) try writer.writeAll(" | ") else need_pipe = true; + try writer.print("DISPFlag{s}", .{field.name}); + }, + .Enum => if (@field(self, field.name) != .Zero) { + if (need_pipe) try writer.writeAll(" | ") else need_pipe = true; + try writer.print("DISPFlag{s}", .{@tagName(@field(self, field.name))}); + }, + .Int => assert(@field(self, field.name) == 0), + else => @compileError("bad field type: " ++ field.name ++ ": " ++ + @typeName(field.type)), + } + } + if (!need_pipe) try writer.writeByte('0'); + } + }; + + file: Metadata, + name: MetadataString, + linkage_name: MetadataString, + line: u32, + scope_line: u32, + ty: Metadata, + di_flags: DIFlags, + compile_unit: Metadata, + }; + + pub const LexicalBlock = struct { + scope: Metadata, + file: Metadata, + line: u32, + column: u32, + }; + + pub const Location = struct { + line: u32, + column: u32, + scope: Metadata, + inlined_at: Metadata, + }; + + pub const BasicType = struct { + name: MetadataString, + size_in_bits_lo: u32, + size_in_bits_hi: u32, + + pub fn bitSize(self: BasicType) u64 { + return @as(u64, self.size_in_bits_hi) << 32 | self.size_in_bits_lo; + } + }; + + pub const CompositeType = struct { + name: MetadataString, + file: Metadata, + scope: Metadata, + line: u32, + underlying_type: Metadata, + size_in_bits_lo: u32, + size_in_bits_hi: u32, + align_in_bits_lo: u32, + align_in_bits_hi: u32, + fields_tuple: Metadata, + + pub fn bitSize(self: CompositeType) u64 { + return @as(u64, self.size_in_bits_hi) << 32 | self.size_in_bits_lo; + } + pub fn bitAlign(self: CompositeType) u64 { + return @as(u64, self.align_in_bits_hi) << 32 | self.align_in_bits_lo; + } + }; + + pub const DerivedType = struct { + name: MetadataString, + file: Metadata, + scope: Metadata, + line: u32, + underlying_type: Metadata, + size_in_bits_lo: u32, + size_in_bits_hi: u32, + align_in_bits_lo: u32, + align_in_bits_hi: u32, + offset_in_bits_lo: u32, + offset_in_bits_hi: u32, + + pub fn bitSize(self: DerivedType) u64 { + return @as(u64, self.size_in_bits_hi) << 32 | self.size_in_bits_lo; + } + pub fn bitAlign(self: DerivedType) u64 { + return @as(u64, self.align_in_bits_hi) << 32 | self.align_in_bits_lo; + } + pub fn bitOffset(self: DerivedType) u64 { + return @as(u64, self.offset_in_bits_hi) << 32 | self.offset_in_bits_lo; + } + }; + + pub const SubroutineType = struct { + types_tuple: Metadata, + }; + + pub const Enumerator = struct { + name: MetadataString, + bit_width: u32, + limbs_index: u32, + limbs_len: u32, + }; + + pub const Subrange = struct { + lower_bound: Metadata, + count: Metadata, + }; + + pub const Expression = struct { + elements_len: u32, + + // elements: [elements_len]u32 + }; + + pub const Tuple = struct { + elements_len: u32, + + // elements: [elements_len]Metadata + }; + + pub const ModuleFlag = struct { + behavior: Metadata, + name: MetadataString, + constant: Metadata, + }; + + pub const LocalVar = struct { + name: MetadataString, + file: Metadata, + scope: Metadata, + line: u32, + ty: Metadata, + }; -pub const InitError = error{ - InvalidLlvmTriple, -} || Allocator.Error; + pub const Parameter = struct { + name: MetadataString, + file: Metadata, + scope: Metadata, + line: u32, + ty: Metadata, + arg_no: u32, + }; -pub fn init(options: Options) InitError!Builder { + pub const GlobalVar = struct { + pub const Options = struct { + local: bool, + }; + + name: MetadataString, + linkage_name: MetadataString, + file: Metadata, + scope: Metadata, + line: u32, + ty: Metadata, + variable: Variable.Index, + }; + + pub const GlobalVarExpression = struct { + variable: Metadata, + expression: Metadata, + }; + + pub fn toValue(self: Metadata) Value { + return @enumFromInt(Value.first_metadata + @intFromEnum(self)); + } + + const Formatter = struct { + builder: *Builder, + need_comma: bool, + map: std.AutoArrayHashMapUnmanaged(union(enum) { + metadata: Metadata, + debug_location: DebugLocation.Location, + }, void) = .{}, + + const FormatData = struct { + formatter: *Formatter, + prefix: []const u8 = "", + node: Node, + + const Node = union(enum) { + none, + @"inline": Metadata, + index: u32, + + local_value: ValueData, + local_metadata: ValueData, + local_inline: Metadata, + local_index: u32, + + string: MetadataString, + bool: bool, + u32: u32, + u64: u64, + di_flags: DIFlags, + sp_flags: Subprogram.DISPFlags, + raw: []const u8, + + const ValueData = struct { + value: Value, + function: Function.Index, + }; + }; + }; + fn format( + data: FormatData, + comptime fmt_str: []const u8, + fmt_opts: std.fmt.FormatOptions, + writer: anytype, + ) @TypeOf(writer).Error!void { + if (data.node == .none) return; + + const is_specialized = fmt_str.len > 0 and fmt_str[0] == 'S'; + const recurse_fmt_str = if (is_specialized) fmt_str[1..] else fmt_str; + + if (data.formatter.need_comma) try writer.writeAll(", "); + defer data.formatter.need_comma = true; + try writer.writeAll(data.prefix); + + const builder = data.formatter.builder; + switch (data.node) { + .none => unreachable, + .@"inline" => |node| { + const needed_comma = data.formatter.need_comma; + defer data.formatter.need_comma = needed_comma; + data.formatter.need_comma = false; + + const item = builder.metadata_items.get(@intFromEnum(node)); + switch (item.tag) { + .expression => { + var extra = builder.metadataExtraDataTrail(Expression, item.data); + const elements = extra.trail.next(extra.data.elements_len, u32, builder); + try writer.writeAll("!DIExpression("); + for (elements) |element| try format(.{ + .formatter = data.formatter, + .node = .{ .u64 = element }, + }, "%", fmt_opts, writer); + try writer.writeByte(')'); + }, + .constant => try Constant.format(.{ + .constant = @enumFromInt(item.data), + .builder = builder, + }, recurse_fmt_str, fmt_opts, writer), + else => unreachable, + } + }, + .index => |node| try writer.print("!{d}", .{node}), + inline .local_value, .local_metadata => |node, tag| try Value.format(.{ + .value = node.value, + .function = node.function, + .builder = builder, + }, switch (tag) { + .local_value => recurse_fmt_str, + .local_metadata => "%", + else => unreachable, + }, fmt_opts, writer), + inline .local_inline, .local_index => |node, tag| { + if (comptime std.mem.eql(u8, recurse_fmt_str, "%")) + try writer.print("{%} ", .{Type.metadata.fmt(builder)}); + try format(.{ + .formatter = data.formatter, + .node = @unionInit(FormatData.Node, @tagName(tag)["local_".len..], node), + }, "%", fmt_opts, writer); + }, + .string => |node| try writer.print((if (is_specialized) "" else "!") ++ "{}", .{ + node.fmt(builder), + }), + inline .bool, + .u32, + .u64, + .di_flags, + .sp_flags, + => |node| try writer.print("{}", .{node}), + .raw => |node| try writer.writeAll(node), + } + } + inline fn fmt(formatter: *Formatter, prefix: []const u8, node: anytype) switch (@TypeOf(node)) { + Metadata => Allocator.Error, + else => error{}, + }!std.fmt.Formatter(format) { + const Node = @TypeOf(node); + const MaybeNode = switch (@typeInfo(Node)) { + .Optional => Node, + .Null => ?noreturn, + else => ?Node, + }; + const Some = @typeInfo(MaybeNode).Optional.child; + return .{ .data = .{ + .formatter = formatter, + .prefix = prefix, + .node = if (@as(MaybeNode, node)) |some| switch (@typeInfo(Some)) { + .Enum => |enum_info| switch (Some) { + Metadata => switch (some) { + .none => .none, + else => try formatter.refUnwrapped(some.unwrap(formatter.builder)), + }, + MetadataString => .{ .string = some }, + else => if (enum_info.is_exhaustive) + .{ .raw = @tagName(some) } + else + @compileError("unknown type to format: " ++ @typeName(Node)), + }, + .EnumLiteral => .{ .raw = @tagName(some) }, + .Bool => .{ .bool = some }, + .Struct => switch (Some) { + DIFlags => .{ .di_flags = some }, + Subprogram.DISPFlags => .{ .sp_flags = some }, + else => @compileError("unknown type to format: " ++ @typeName(Node)), + }, + .Int, .ComptimeInt => .{ .u64 = some }, + .Pointer => .{ .raw = some }, + else => @compileError("unknown type to format: " ++ @typeName(Node)), + } else switch (@typeInfo(Node)) { + .Optional, .Null => .none, + else => unreachable, + }, + } }; + } + inline fn fmtLocal( + formatter: *Formatter, + prefix: []const u8, + value: Value, + function: Function.Index, + ) Allocator.Error!std.fmt.Formatter(format) { + return .{ .data = .{ + .formatter = formatter, + .prefix = prefix, + .node = switch (value.unwrap()) { + .instruction, .constant => .{ .local_value = .{ + .value = value, + .function = function, + } }, + .metadata => |metadata| if (value == .none) .none else node: { + const unwrapped = metadata.unwrap(formatter.builder); + break :node if (@intFromEnum(unwrapped) >= first_local_metadata) + .{ .local_metadata = .{ + .value = function.ptrConst(formatter.builder).debug_values[ + @intFromEnum(unwrapped) - first_local_metadata + ].toValue(), + .function = function, + } } + else switch (try formatter.refUnwrapped(unwrapped)) { + .@"inline" => |node| .{ .local_inline = node }, + .index => |node| .{ .local_index = node }, + else => unreachable, + }; + }, + }, + } }; + } + fn refUnwrapped(formatter: *Formatter, node: Metadata) Allocator.Error!FormatData.Node { + assert(node != .none); + assert(@intFromEnum(node) < first_forward_reference); + const builder = formatter.builder; + const unwrapped_metadata = node.unwrap(builder); + const tag = formatter.builder.metadata_items.items(.tag)[@intFromEnum(unwrapped_metadata)]; + switch (tag) { + .none => unreachable, + .expression, .constant => return .{ .@"inline" = unwrapped_metadata }, + else => { + assert(!tag.isInline()); + const gop = try formatter.map.getOrPut(builder.gpa, .{ .metadata = unwrapped_metadata }); + return .{ .index = @intCast(gop.index) }; + }, + } + } + + inline fn specialized( + formatter: *Formatter, + distinct: enum { @"!", @"distinct !" }, + node: enum { + DIFile, + DICompileUnit, + DISubprogram, + DILexicalBlock, + DILocation, + DIBasicType, + DICompositeType, + DIDerivedType, + DISubroutineType, + DIEnumerator, + DISubrange, + DILocalVariable, + DIGlobalVariable, + DIGlobalVariableExpression, + }, + nodes: anytype, + writer: anytype, + ) !void { + comptime var fmt_str: []const u8 = ""; + const names = comptime std.meta.fieldNames(@TypeOf(nodes)); + comptime var fields: [2 + names.len]std.builtin.Type.StructField = undefined; + inline for (fields[0..2], .{ "distinct", "node" }) |*field, name| { + fmt_str = fmt_str ++ "{[" ++ name ++ "]s}"; + field.* = .{ + .name = name, + .type = []const u8, + .default_value = null, + .is_comptime = false, + .alignment = 0, + }; + } + fmt_str = fmt_str ++ "("; + inline for (fields[2..], names) |*field, name| { + fmt_str = fmt_str ++ "{[" ++ name ++ "]S}"; + field.* = .{ + .name = name, + .type = std.fmt.Formatter(format), + .default_value = null, + .is_comptime = false, + .alignment = 0, + }; + } + fmt_str = fmt_str ++ ")\n"; + + var fmt_args: @Type(.{ .Struct = .{ + .layout = .Auto, + .fields = &fields, + .decls = &.{}, + .is_tuple = false, + } }) = undefined; + fmt_args.distinct = @tagName(distinct); + fmt_args.node = @tagName(node); + inline for (names) |name| @field(fmt_args, name) = try formatter.fmt( + name ++ ": ", + @field(nodes, name), + ); + try writer.print(fmt_str, fmt_args); + } + }; +}; + +pub fn init(options: Options) Allocator.Error!Builder { var self = Builder{ .gpa = options.allocator, - .use_lib_llvm = options.use_lib_llvm, .strip = options.strip, - .llvm = undefined, - .source_filename = .none, .data_layout = .none, .target_triple = .none, @@ -7980,6 +8321,8 @@ pub fn init(options: Options) InitError!Builder { .attributes_indices = .{}, .attributes_extra = .{}, + .function_attributes_set = .{}, + .globals = .{}, .next_unnamed_global = @enumFromInt(0), .next_replaced_global = .none, @@ -7992,19 +8335,16 @@ pub fn init(options: Options) InitError!Builder { .constant_items = .{}, .constant_extra = .{}, .constant_limbs = .{}, - }; - if (self.useLibLlvm()) self.llvm = .{ - .context = llvm.Context.create(), - .module = null, - .target = null, - .di_builder = null, - .di_compile_unit = null, - .attribute_kind_ids = null, - .attributes = .{}, - .types = .{}, - .globals = .{}, - .constants = .{}, - .replacements = .{}, + + .metadata_map = .{}, + .metadata_items = .{}, + .metadata_extra = .{}, + .metadata_limbs = .{}, + .metadata_forward_references = .{}, + .metadata_named = .{}, + .metadata_string_map = .{}, + .metadata_string_indices = .{}, + .metadata_string_bytes = .{}, }; errdefer self.deinit(); @@ -8012,51 +8352,20 @@ pub fn init(options: Options) InitError!Builder { assert(try self.string("") == .empty); if (options.name.len > 0) self.source_filename = try self.string(options.name); - if (self.useLibLlvm()) { - initializeLLVMTarget(options.target.cpu.arch); - self.llvm.module = llvm.Module.createWithName( - (self.source_filename.slice(&self) orelse ""), - self.llvm.context, - ); - } if (options.triple.len > 0) { self.target_triple = try self.string(options.triple); - - if (self.useLibLlvm()) { - var error_message: [*:0]const u8 = undefined; - var target: *llvm.Target = undefined; - if (llvm.Target.getFromTriple( - self.target_triple.slice(&self).?, - &target, - &error_message, - ).toBool()) { - defer llvm.disposeMessage(error_message); - - log.err("LLVM failed to parse '{s}': {s}", .{ - self.target_triple.slice(&self).?, - error_message, - }); - return InitError.InvalidLlvmTriple; - } - self.llvm.target = target; - self.llvm.module.?.setTarget(self.target_triple.slice(&self).?); - } } { const static_len = @typeInfo(Type).Enum.fields.len - 1; try self.type_map.ensureTotalCapacity(self.gpa, static_len); try self.type_items.ensureTotalCapacity(self.gpa, static_len); - if (self.useLibLlvm()) try self.llvm.types.ensureTotalCapacity(self.gpa, static_len); inline for (@typeInfo(Type.Simple).Enum.fields) |simple_field| { const result = self.getOrPutTypeNoExtraAssumeCapacity( .{ .tag = .simple, .data = simple_field.value }, ); assert(result.new and result.type == @field(Type, simple_field.name)); - if (self.useLibLlvm()) self.llvm.types.appendAssumeCapacity( - @field(llvm.Context, simple_field.name ++ "Type")(self.llvm.context), - ); } inline for (.{ 1, 8, 16, 29, 32, 64, 80, 128 }) |bits| assert(self.intTypeAssumeCapacity(bits) == @@ -8069,10 +8378,6 @@ pub fn init(options: Options) InitError!Builder { } { - if (self.useLibLlvm()) { - self.llvm.attribute_kind_ids = try self.gpa.create([Attribute.Kind.len]c_uint); - @memset(self.llvm.attribute_kind_ids.?, 0); - } try self.attributes_indices.append(self.gpa, 0); assert(try self.attrs(&.{}) == .none); assert(try self.fnAttrs(&.{}) == .none); @@ -8080,26 +8385,62 @@ pub fn init(options: Options) InitError!Builder { assert(try self.intConst(.i1, 0) == .false); assert(try self.intConst(.i1, 1) == .true); + 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); + + try self.metadata_string_indices.append(self.gpa, 0); + assert(try self.metadataString("") == .none); return self; } -pub fn deinit(self: *Builder) void { - if (self.useLibLlvm()) { - var replacement_it = self.llvm.replacements.keyIterator(); - while (replacement_it.next()) |replacement| replacement.*.deleteGlobalValue(); - self.llvm.replacements.deinit(self.gpa); - self.llvm.constants.deinit(self.gpa); - self.llvm.globals.deinit(self.gpa); - self.llvm.types.deinit(self.gpa); - self.llvm.attributes.deinit(self.gpa); - if (self.llvm.attribute_kind_ids) |attribute_kind_ids| self.gpa.destroy(attribute_kind_ids); - if (self.llvm.di_builder) |di_builder| di_builder.dispose(); - if (self.llvm.module) |module| module.dispose(); - self.llvm.context.dispose(); - } +pub fn clearAndFree(self: *Builder) void { + self.module_asm.clearAndFree(self.gpa); + + self.string_map.clearAndFree(self.gpa); + self.string_indices.clearAndFree(self.gpa); + self.string_bytes.clearAndFree(self.gpa); + + self.types.clearAndFree(self.gpa); + self.next_unique_type_id.clearAndFree(self.gpa); + self.type_map.clearAndFree(self.gpa); + self.type_items.clearAndFree(self.gpa); + self.type_extra.clearAndFree(self.gpa); + + self.attributes.clearAndFree(self.gpa); + self.attributes_map.clearAndFree(self.gpa); + self.attributes_indices.clearAndFree(self.gpa); + self.attributes_extra.clearAndFree(self.gpa); + + self.function_attributes_set.clearAndFree(self.gpa); + + self.globals.clearAndFree(self.gpa); + self.next_unique_global_id.clearAndFree(self.gpa); + self.aliases.clearAndFree(self.gpa); + self.variables.clearAndFree(self.gpa); + for (self.functions.items) |*function| function.deinit(self.gpa); + self.functions.clearAndFree(self.gpa); + self.constant_map.clearAndFree(self.gpa); + self.constant_items.shrinkAndFree(self.gpa, 0); + self.constant_extra.clearAndFree(self.gpa); + self.constant_limbs.clearAndFree(self.gpa); + + self.metadata_map.clearAndFree(self.gpa); + self.metadata_items.shrinkAndFree(self.gpa, 0); + self.metadata_extra.clearAndFree(self.gpa); + self.metadata_limbs.clearAndFree(self.gpa); + self.metadata_forward_references.clearAndFree(self.gpa); + self.metadata_named.clearAndFree(self.gpa); + + self.metadata_string_map.clearAndFree(self.gpa); + self.metadata_string_indices.clearAndFree(self.gpa); + self.metadata_string_bytes.clearAndFree(self.gpa); +} + +pub fn deinit(self: *Builder) void { self.module_asm.deinit(self.gpa); self.string_map.deinit(self.gpa); @@ -8117,6 +8458,8 @@ pub fn deinit(self: *Builder) void { self.attributes_indices.deinit(self.gpa); self.attributes_extra.deinit(self.gpa); + self.function_attributes_set.deinit(self.gpa); + self.globals.deinit(self.gpa); self.next_unique_global_id.deinit(self.gpa); self.aliases.deinit(self.gpa); @@ -8129,199 +8472,18 @@ pub fn deinit(self: *Builder) void { self.constant_extra.deinit(self.gpa); self.constant_limbs.deinit(self.gpa); - self.* = undefined; -} - -pub fn initializeLLVMTarget(arch: std.Target.Cpu.Arch) void { - switch (arch) { - .aarch64, .aarch64_be, .aarch64_32 => { - llvm.LLVMInitializeAArch64Target(); - llvm.LLVMInitializeAArch64TargetInfo(); - llvm.LLVMInitializeAArch64TargetMC(); - llvm.LLVMInitializeAArch64AsmPrinter(); - llvm.LLVMInitializeAArch64AsmParser(); - }, - .amdgcn => { - llvm.LLVMInitializeAMDGPUTarget(); - llvm.LLVMInitializeAMDGPUTargetInfo(); - llvm.LLVMInitializeAMDGPUTargetMC(); - llvm.LLVMInitializeAMDGPUAsmPrinter(); - llvm.LLVMInitializeAMDGPUAsmParser(); - }, - .thumb, .thumbeb, .arm, .armeb => { - llvm.LLVMInitializeARMTarget(); - llvm.LLVMInitializeARMTargetInfo(); - llvm.LLVMInitializeARMTargetMC(); - llvm.LLVMInitializeARMAsmPrinter(); - llvm.LLVMInitializeARMAsmParser(); - }, - .avr => { - llvm.LLVMInitializeAVRTarget(); - llvm.LLVMInitializeAVRTargetInfo(); - llvm.LLVMInitializeAVRTargetMC(); - llvm.LLVMInitializeAVRAsmPrinter(); - llvm.LLVMInitializeAVRAsmParser(); - }, - .bpfel, .bpfeb => { - llvm.LLVMInitializeBPFTarget(); - llvm.LLVMInitializeBPFTargetInfo(); - llvm.LLVMInitializeBPFTargetMC(); - llvm.LLVMInitializeBPFAsmPrinter(); - llvm.LLVMInitializeBPFAsmParser(); - }, - .hexagon => { - llvm.LLVMInitializeHexagonTarget(); - llvm.LLVMInitializeHexagonTargetInfo(); - llvm.LLVMInitializeHexagonTargetMC(); - llvm.LLVMInitializeHexagonAsmPrinter(); - llvm.LLVMInitializeHexagonAsmParser(); - }, - .lanai => { - llvm.LLVMInitializeLanaiTarget(); - llvm.LLVMInitializeLanaiTargetInfo(); - llvm.LLVMInitializeLanaiTargetMC(); - llvm.LLVMInitializeLanaiAsmPrinter(); - llvm.LLVMInitializeLanaiAsmParser(); - }, - .mips, .mipsel, .mips64, .mips64el => { - llvm.LLVMInitializeMipsTarget(); - llvm.LLVMInitializeMipsTargetInfo(); - llvm.LLVMInitializeMipsTargetMC(); - llvm.LLVMInitializeMipsAsmPrinter(); - llvm.LLVMInitializeMipsAsmParser(); - }, - .msp430 => { - llvm.LLVMInitializeMSP430Target(); - llvm.LLVMInitializeMSP430TargetInfo(); - llvm.LLVMInitializeMSP430TargetMC(); - llvm.LLVMInitializeMSP430AsmPrinter(); - llvm.LLVMInitializeMSP430AsmParser(); - }, - .nvptx, .nvptx64 => { - llvm.LLVMInitializeNVPTXTarget(); - llvm.LLVMInitializeNVPTXTargetInfo(); - llvm.LLVMInitializeNVPTXTargetMC(); - llvm.LLVMInitializeNVPTXAsmPrinter(); - // There is no LLVMInitializeNVPTXAsmParser function available. - }, - .powerpc, .powerpcle, .powerpc64, .powerpc64le => { - llvm.LLVMInitializePowerPCTarget(); - llvm.LLVMInitializePowerPCTargetInfo(); - llvm.LLVMInitializePowerPCTargetMC(); - llvm.LLVMInitializePowerPCAsmPrinter(); - llvm.LLVMInitializePowerPCAsmParser(); - }, - .riscv32, .riscv64 => { - llvm.LLVMInitializeRISCVTarget(); - llvm.LLVMInitializeRISCVTargetInfo(); - llvm.LLVMInitializeRISCVTargetMC(); - llvm.LLVMInitializeRISCVAsmPrinter(); - llvm.LLVMInitializeRISCVAsmParser(); - }, - .sparc, .sparc64, .sparcel => { - llvm.LLVMInitializeSparcTarget(); - llvm.LLVMInitializeSparcTargetInfo(); - llvm.LLVMInitializeSparcTargetMC(); - llvm.LLVMInitializeSparcAsmPrinter(); - llvm.LLVMInitializeSparcAsmParser(); - }, - .s390x => { - llvm.LLVMInitializeSystemZTarget(); - llvm.LLVMInitializeSystemZTargetInfo(); - llvm.LLVMInitializeSystemZTargetMC(); - llvm.LLVMInitializeSystemZAsmPrinter(); - llvm.LLVMInitializeSystemZAsmParser(); - }, - .wasm32, .wasm64 => { - llvm.LLVMInitializeWebAssemblyTarget(); - llvm.LLVMInitializeWebAssemblyTargetInfo(); - llvm.LLVMInitializeWebAssemblyTargetMC(); - llvm.LLVMInitializeWebAssemblyAsmPrinter(); - llvm.LLVMInitializeWebAssemblyAsmParser(); - }, - .x86, .x86_64 => { - llvm.LLVMInitializeX86Target(); - llvm.LLVMInitializeX86TargetInfo(); - llvm.LLVMInitializeX86TargetMC(); - llvm.LLVMInitializeX86AsmPrinter(); - llvm.LLVMInitializeX86AsmParser(); - }, - .xtensa => { - if (build_options.llvm_has_xtensa) { - llvm.LLVMInitializeXtensaTarget(); - llvm.LLVMInitializeXtensaTargetInfo(); - llvm.LLVMInitializeXtensaTargetMC(); - // There is no LLVMInitializeXtensaAsmPrinter function. - llvm.LLVMInitializeXtensaAsmParser(); - } - }, - .xcore => { - llvm.LLVMInitializeXCoreTarget(); - llvm.LLVMInitializeXCoreTargetInfo(); - llvm.LLVMInitializeXCoreTargetMC(); - llvm.LLVMInitializeXCoreAsmPrinter(); - // There is no LLVMInitializeXCoreAsmParser function. - }, - .m68k => { - if (build_options.llvm_has_m68k) { - llvm.LLVMInitializeM68kTarget(); - llvm.LLVMInitializeM68kTargetInfo(); - llvm.LLVMInitializeM68kTargetMC(); - llvm.LLVMInitializeM68kAsmPrinter(); - llvm.LLVMInitializeM68kAsmParser(); - } - }, - .csky => { - if (build_options.llvm_has_csky) { - llvm.LLVMInitializeCSKYTarget(); - llvm.LLVMInitializeCSKYTargetInfo(); - llvm.LLVMInitializeCSKYTargetMC(); - // There is no LLVMInitializeCSKYAsmPrinter function. - llvm.LLVMInitializeCSKYAsmParser(); - } - }, - .ve => { - llvm.LLVMInitializeVETarget(); - llvm.LLVMInitializeVETargetInfo(); - llvm.LLVMInitializeVETargetMC(); - llvm.LLVMInitializeVEAsmPrinter(); - llvm.LLVMInitializeVEAsmParser(); - }, - .arc => { - if (build_options.llvm_has_arc) { - llvm.LLVMInitializeARCTarget(); - llvm.LLVMInitializeARCTargetInfo(); - llvm.LLVMInitializeARCTargetMC(); - llvm.LLVMInitializeARCAsmPrinter(); - // There is no LLVMInitializeARCAsmParser function. - } - }, + self.metadata_map.deinit(self.gpa); + self.metadata_items.deinit(self.gpa); + self.metadata_extra.deinit(self.gpa); + self.metadata_limbs.deinit(self.gpa); + self.metadata_forward_references.deinit(self.gpa); + self.metadata_named.deinit(self.gpa); - // LLVM backends that have no initialization functions. - .tce, - .tcele, - .r600, - .le32, - .le64, - .amdil, - .amdil64, - .hsail, - .hsail64, - .shave, - .spir, - .spir64, - .kalimba, - .renderscript32, - .renderscript64, - .dxil, - .loongarch32, - .loongarch64, - => {}, + self.metadata_string_map.deinit(self.gpa); + self.metadata_string_indices.deinit(self.gpa); + self.metadata_string_bytes.deinit(self.gpa); - .spu_2 => unreachable, // LLVM does not support this backend - .spirv32 => unreachable, // LLVM does not support this backend - .spirv64 => unreachable, // LLVM does not support this backend - } + self.* = undefined; } pub fn setModuleAsm(self: *Builder) std.ArrayListUnmanaged(u8).Writer { @@ -8336,24 +8498,25 @@ pub fn appendModuleAsm(self: *Builder) std.ArrayListUnmanaged(u8).Writer { pub fn finishModuleAsm(self: *Builder) Allocator.Error!void { if (self.module_asm.getLastOrNull()) |last| if (last != '\n') try self.module_asm.append(self.gpa, '\n'); - if (self.useLibLlvm()) - self.llvm.module.?.setModuleInlineAsm(self.module_asm.items.ptr, self.module_asm.items.len); } pub fn string(self: *Builder, bytes: []const u8) Allocator.Error!String { - try self.string_bytes.ensureUnusedCapacity(self.gpa, bytes.len + 1); + try self.string_bytes.ensureUnusedCapacity(self.gpa, bytes.len); try self.string_indices.ensureUnusedCapacity(self.gpa, 1); try self.string_map.ensureUnusedCapacity(self.gpa, 1); const gop = self.string_map.getOrPutAssumeCapacityAdapted(bytes, String.Adapter{ .builder = self }); if (!gop.found_existing) { self.string_bytes.appendSliceAssumeCapacity(bytes); - self.string_bytes.appendAssumeCapacity(0); self.string_indices.appendAssumeCapacity(@intCast(self.string_bytes.items.len)); } return String.fromIndex(gop.index); } +pub fn stringNull(self: *Builder, bytes: [:0]const u8) Allocator.Error!String { + return self.string(bytes[0 .. bytes.len + 1]); +} + pub fn stringIfExists(self: *const Builder, bytes: []const u8) ?String { return String.fromIndex( self.string_map.getIndexAdapted(bytes, String.Adapter{ .builder = self }) orelse return null, @@ -8362,16 +8525,25 @@ pub fn stringIfExists(self: *const Builder, bytes: []const u8) ?String { pub fn fmt(self: *Builder, comptime fmt_str: []const u8, fmt_args: anytype) Allocator.Error!String { try self.string_map.ensureUnusedCapacity(self.gpa, 1); - try self.string_bytes.ensureUnusedCapacity(self.gpa, @intCast(std.fmt.count(fmt_str ++ .{0}, fmt_args))); + try self.string_bytes.ensureUnusedCapacity(self.gpa, @intCast(std.fmt.count(fmt_str, fmt_args))); try self.string_indices.ensureUnusedCapacity(self.gpa, 1); return self.fmtAssumeCapacity(fmt_str, fmt_args); } pub fn fmtAssumeCapacity(self: *Builder, comptime fmt_str: []const u8, fmt_args: anytype) String { - const start = self.string_bytes.items.len; - self.string_bytes.writer(self.gpa).print(fmt_str ++ .{0}, fmt_args) catch unreachable; - const bytes: []const u8 = self.string_bytes.items[start .. self.string_bytes.items.len - 1]; + self.string_bytes.writer(undefined).print(fmt_str, fmt_args) catch unreachable; + return self.trailingStringAssumeCapacity(); +} +pub fn trailingString(self: *Builder) Allocator.Error!String { + try self.string_indices.ensureUnusedCapacity(self.gpa, 1); + try self.string_map.ensureUnusedCapacity(self.gpa, 1); + return self.trailingStringAssumeCapacity(); +} + +pub fn trailingStringAssumeCapacity(self: *Builder) String { + const start = self.string_indices.getLast(); + const bytes: []const u8 = self.string_bytes.items[start..]; const gop = self.string_map.getOrPutAssumeCapacityAdapted(bytes, String.Adapter{ .builder = self }); if (gop.found_existing) { self.string_bytes.shrinkRetainingCapacity(start); @@ -8435,7 +8607,7 @@ pub fn structType( pub fn opaqueType(self: *Builder, name: String) Allocator.Error!Type { try self.string_map.ensureUnusedCapacity(self.gpa, 1); if (name.slice(self)) |id| { - const count: usize = comptime std.fmt.count("{d}" ++ .{0}, .{std.math.maxInt(u32)}); + const count: usize = comptime std.fmt.count("{d}", .{std.math.maxInt(u32)}); try self.string_bytes.ensureUnusedCapacity(self.gpa, id.len + count); } try self.string_indices.ensureUnusedCapacity(self.gpa, 1); @@ -8449,98 +8621,17 @@ pub fn namedTypeSetBody( self: *Builder, named_type: Type, body_type: Type, -) (if (build_options.have_llvm) Allocator.Error else error{})!void { +) void { const named_item = self.type_items.items[@intFromEnum(named_type)]; self.type_extra.items[named_item.data + std.meta.fieldIndex(Type.NamedStructure, "body").?] = @intFromEnum(body_type); - if (self.useLibLlvm()) { - const body_item = self.type_items.items[@intFromEnum(body_type)]; - var body_extra = self.typeExtraDataTrail(Type.Structure, body_item.data); - const body_fields = body_extra.trail.next(body_extra.data.fields_len, Type, self); - const llvm_fields = try self.gpa.alloc(*llvm.Type, body_fields.len); - defer self.gpa.free(llvm_fields); - for (llvm_fields, body_fields) |*llvm_field, body_field| llvm_field.* = body_field.toLlvm(self); - self.llvm.types.items[@intFromEnum(named_type)].structSetBody( - llvm_fields.ptr, - @intCast(llvm_fields.len), - switch (body_item.tag) { - .structure => .False, - .packed_structure => .True, - else => unreachable, - }, - ); - } } pub fn attr(self: *Builder, attribute: Attribute) Allocator.Error!Attribute.Index { try self.attributes.ensureUnusedCapacity(self.gpa, 1); - if (self.useLibLlvm()) try self.llvm.attributes.ensureUnusedCapacity(self.gpa, 1); const gop = self.attributes.getOrPutAssumeCapacity(attribute.toStorage()); - if (!gop.found_existing) { - gop.value_ptr.* = {}; - if (self.useLibLlvm()) self.llvm.attributes.appendAssumeCapacity(switch (attribute) { - else => llvm_attr: { - const llvm_kind_id = attribute.getKind().toLlvm(self); - if (llvm_kind_id.* == 0) { - const name = @tagName(attribute); - llvm_kind_id.* = llvm.getEnumAttributeKindForName(name.ptr, name.len); - assert(llvm_kind_id.* != 0); - } - break :llvm_attr switch (attribute) { - else => switch (attribute) { - inline else => |value| self.llvm.context.createEnumAttribute( - llvm_kind_id.*, - switch (@TypeOf(value)) { - void => 0, - u32 => value, - Attribute.FpClass, - Attribute.AllocKind, - Attribute.Memory, - => @as(u32, @bitCast(value)), - Alignment => value.toByteUnits() orelse 0, - Attribute.AllocSize, - Attribute.VScaleRange, - => @bitCast(value.toLlvm()), - Attribute.UwTable => @intFromEnum(value), - else => @compileError( - "bad payload type: " ++ @typeName(@TypeOf(value)), - ), - }, - ), - .byval, - .byref, - .preallocated, - .inalloca, - .sret, - .elementtype, - .string, - .none, - => unreachable, - }, - .byval, - .byref, - .preallocated, - .inalloca, - .sret, - .elementtype, - => |ty| self.llvm.context.createTypeAttribute(llvm_kind_id.*, ty.toLlvm(self)), - .string, .none => unreachable, - }; - }, - .string => |string_attr| llvm_attr: { - const kind = string_attr.kind.slice(self).?; - const value = string_attr.value.slice(self).?; - break :llvm_attr self.llvm.context.createStringAttribute( - kind.ptr, - @intCast(kind.len), - value.ptr, - @intCast(value.len), - ); - }, - .none => unreachable, - }); - } + if (!gop.found_existing) gop.value_ptr.* = {}; return @enumFromInt(gop.index); } @@ -8557,12 +8648,16 @@ pub fn attrs(self: *Builder, attributes: []Attribute.Index) Allocator.Error!Attr } pub fn fnAttrs(self: *Builder, fn_attributes: []const Attributes) Allocator.Error!FunctionAttributes { - return @enumFromInt(try self.attrGeneric(@ptrCast( + try self.function_attributes_set.ensureUnusedCapacity(self.gpa, 1); + const function_attributes: FunctionAttributes = @enumFromInt(try self.attrGeneric(@ptrCast( fn_attributes[0..if (std.mem.lastIndexOfNone(Attributes, fn_attributes, &.{.none})) |last| last + 1 else 0], ))); + + _ = self.function_attributes_set.getOrPutAssumeCapacity(function_attributes); + return function_attributes; } pub fn addGlobal(self: *Builder, name: String, global: Global) Allocator.Error!Global.Index { @@ -8586,7 +8681,6 @@ pub fn addGlobalAssumeCapacity(self: *Builder, name: String, global: Global) Glo global_gop.value_ptr.* = global; const global_index: Global.Index = @enumFromInt(global_gop.index); global_index.updateDsoLocal(self); - global_index.updateName(self); return global_index; } @@ -8622,12 +8716,6 @@ pub fn addAliasAssumeCapacity( addr_space: AddrSpace, aliasee: Constant, ) Alias.Index { - if (self.useLibLlvm()) self.llvm.globals.appendAssumeCapacity(self.llvm.module.?.addAlias( - ty.toLlvm(self), - @intFromEnum(addr_space), - aliasee.toLlvm(self), - name.slice(self).?, - )); const alias_index: Alias.Index = @enumFromInt(self.aliases.items.len); self.aliases.appendAssumeCapacity(.{ .global = self.addGlobalAssumeCapacity(name, .{ .addr_space = addr_space, @@ -8656,13 +8744,6 @@ pub fn addVariableAssumeCapacity( name: String, addr_space: AddrSpace, ) Variable.Index { - if (self.useLibLlvm()) self.llvm.globals.appendAssumeCapacity( - self.llvm.module.?.addGlobalInAddressSpace( - ty.toLlvm(self), - name.slice(self).?, - @intFromEnum(addr_space), - ), - ); const variable_index: Variable.Index = @enumFromInt(self.variables.items.len); self.variables.appendAssumeCapacity(.{ .global = self.addGlobalAssumeCapacity(name, .{ .addr_space = addr_space, @@ -8692,19 +8773,15 @@ pub fn addFunctionAssumeCapacity( addr_space: AddrSpace, ) Function.Index { assert(ty.isFunction(self)); - if (self.useLibLlvm()) self.llvm.globals.appendAssumeCapacity( - self.llvm.module.?.addFunctionInAddressSpace( - name.slice(self).?, - ty.toLlvm(self), - @intFromEnum(addr_space), - ), - ); const function_index: Function.Index = @enumFromInt(self.functions.items.len); - self.functions.appendAssumeCapacity(.{ .global = self.addGlobalAssumeCapacity(name, .{ - .addr_space = addr_space, - .type = ty, - .kind = .{ .function = function_index }, - }) }); + self.functions.appendAssumeCapacity(.{ + .global = self.addGlobalAssumeCapacity(name, .{ + .addr_space = addr_space, + .type = ty, + .kind = .{ .function = function_index }, + }), + .strip = undefined, + }); return function_index; } @@ -8714,7 +8791,6 @@ pub fn getIntrinsic( overload: []const Type, ) Allocator.Error!Function.Index { const ExpectedContents = extern union { - name: [expected_intrinsic_name_len]u8, attrs: extern struct { params: [expected_args_len]Type, fn_attrs: [FunctionAttributes.params_index + expected_args_len]Attributes, @@ -8727,12 +8803,10 @@ pub fn getIntrinsic( const allocator = stack.get(); const name = name: { - var buffer = std.ArrayList(u8).init(allocator); - defer buffer.deinit(); - - try buffer.writer().print("llvm.{s}", .{@tagName(id)}); - for (overload) |ty| try buffer.writer().print(".{m}", .{ty.fmt(self)}); - break :name try self.string(buffer.items); + const writer = self.string_bytes.writer(self.gpa); + try writer.print("llvm.{s}", .{@tagName(id)}); + for (overload) |ty| try writer.print(".{m}", .{ty.fmt(self)}); + break :name try self.trailingString(); }; if (self.getGlobal(name)) |global| return global.ptrConst(self).kind.function; @@ -8826,7 +8900,6 @@ pub fn bigIntConst(self: *Builder, ty: Type, value: std.math.big.int.Const) Allo try self.constant_map.ensureUnusedCapacity(self.gpa, 1); try self.constant_items.ensureUnusedCapacity(self.gpa, 1); try self.constant_limbs.ensureUnusedCapacity(self.gpa, Constant.Integer.limbs + value.limbs.len); - if (self.useLibLlvm()) try self.llvm.constants.ensureUnusedCapacity(self.gpa, 1); return self.bigIntConstAssumeCapacity(ty, value); } @@ -8977,16 +9050,6 @@ pub fn stringValue(self: *Builder, val: String) Allocator.Error!Value { return (try self.stringConst(val)).toValue(); } -pub fn stringNullConst(self: *Builder, val: String) Allocator.Error!Constant { - try self.ensureUnusedTypeCapacity(1, Type.Array, 0); - try self.ensureUnusedConstantCapacity(1, NoExtra, 0); - return self.stringNullConstAssumeCapacity(val); -} - -pub fn stringNullValue(self: *Builder, val: String) Allocator.Error!Value { - return (try self.stringNullConst(val)).toValue(); -} - pub fn vectorConst(self: *Builder, ty: Type, vals: []const Constant) Allocator.Error!Constant { try self.ensureUnusedConstantCapacity(1, Constant.Aggregate, vals.len); return self.vectorConstAssumeCapacity(ty, vals); @@ -9244,72 +9307,20 @@ pub fn asmValue( return (try self.asmConst(ty, info, assembly, constraints)).toValue(); } -pub fn verify(self: *Builder) error{}!bool { - if (self.useLibLlvm()) { - var error_message: [*:0]const u8 = undefined; - // verifyModule always allocs the error_message even if there is no error - defer llvm.disposeMessage(error_message); - - if (self.llvm.module.?.verify(.ReturnStatus, &error_message).toBool()) { - log.err("failed verification of LLVM module:\n{s}\n", .{error_message}); - return false; - } - } - return true; -} - -pub fn writeBitcodeToFile(self: *Builder, path: []const u8) Allocator.Error!bool { - const path_z = try self.gpa.dupeZ(u8, path); - defer self.gpa.free(path_z); - return self.writeBitcodeToFileZ(path_z); -} - -pub fn writeBitcodeToFileZ(self: *Builder, path: [*:0]const u8) bool { - if (self.useLibLlvm()) { - const error_code = self.llvm.module.?.writeBitcodeToFile(path); - if (error_code != 0) { - log.err("failed dumping LLVM module to \"{s}\": {d}", .{ path, error_code }); - return false; - } - } else { - log.err("writing bitcode without libllvm not implemented", .{}); - return false; - } - return true; -} - pub fn dump(self: *Builder) void { - if (self.useLibLlvm()) - self.llvm.module.?.dump() - else - self.print(std.io.getStdErr().writer()) catch {}; + self.print(std.io.getStdErr().writer()) catch {}; } pub fn printToFile(self: *Builder, path: []const u8) Allocator.Error!bool { - const path_z = try self.gpa.dupeZ(u8, path); - defer self.gpa.free(path_z); - return self.printToFileZ(path_z); -} - -pub fn printToFileZ(self: *Builder, path: [*:0]const u8) bool { - if (self.useLibLlvm()) { - var error_message: [*:0]const u8 = undefined; - if (self.llvm.module.?.printModuleToFile(path, &error_message).toBool()) { - defer llvm.disposeMessage(error_message); - log.err("failed printing LLVM module to \"{s}\": {s}", .{ path, error_message }); - return false; - } - } else { - var file = std.fs.cwd().createFileZ(path, .{}) catch |err| { - log.err("failed printing LLVM module to \"{s}\": {s}", .{ path, @errorName(err) }); - return false; - }; - defer file.close(); - self.print(file.writer()) catch |err| { - log.err("failed printing LLVM module to \"{s}\": {s}", .{ path, @errorName(err) }); - return false; - }; - } + var file = std.fs.cwd().createFile(path, .{}) catch |err| { + log.err("failed printing LLVM module to \"{s}\": {s}", .{ path, @errorName(err) }); + return false; + }; + defer file.close(); + self.print(file.writer()) catch |err| { + log.err("failed printing LLVM module to \"{s}\": {s}", .{ path, @errorName(err) }); + return false; + }; return true; } @@ -9324,9 +9335,11 @@ pub fn printUnbuffered( writer: anytype, ) (@TypeOf(writer).Error || Allocator.Error)!void { var need_newline = false; + var metadata_formatter: Metadata.Formatter = .{ .builder = self, .need_comma = undefined }; + defer metadata_formatter.map.deinit(self.gpa); if (self.source_filename != .none or self.data_layout != .none or self.target_triple != .none) { - if (need_newline) try writer.writeByte('\n'); + if (need_newline) try writer.writeByte('\n') else need_newline = true; if (self.source_filename != .none) try writer.print( \\; ModuleID = '{s}' \\source_filename = {"} @@ -9340,40 +9353,40 @@ pub fn printUnbuffered( \\target triple = {"} \\ , .{self.target_triple.fmt(self)}); - need_newline = true; } if (self.module_asm.items.len > 0) { - if (need_newline) try writer.writeByte('\n'); + if (need_newline) try writer.writeByte('\n') else need_newline = true; var line_it = std.mem.tokenizeScalar(u8, self.module_asm.items, '\n'); while (line_it.next()) |line| { try writer.writeAll("module asm "); try printEscapedString(line, .always_quote, writer); try writer.writeByte('\n'); } - need_newline = true; } if (self.types.count() > 0) { - if (need_newline) try writer.writeByte('\n'); + if (need_newline) try writer.writeByte('\n') else need_newline = true; for (self.types.keys(), self.types.values()) |id, ty| try writer.print( \\%{} = type {} \\ , .{ id.fmt(self), ty.fmt(self) }); - need_newline = true; } if (self.variables.items.len > 0) { - if (need_newline) try writer.writeByte('\n'); + if (need_newline) try writer.writeByte('\n') else need_newline = true; for (self.variables.items) |variable| { if (variable.global.getReplacement(self) != .none) continue; const global = variable.global.ptrConst(self); + metadata_formatter.need_comma = true; + defer metadata_formatter.need_comma = undefined; try writer.print( - \\{} ={}{}{}{}{ }{}{ }{} {s} {%}{ }{, } + \\{} ={}{}{}{}{ }{}{ }{} {s} {%}{ }{, }{} \\ , .{ variable.global.fmt(self), - global.linkage, + Linkage.fmtOptional(if (global.linkage == .external and + variable.init != .no_init) null else global.linkage), global.preemption, global.visibility, global.dll_storage_class, @@ -9385,18 +9398,20 @@ pub fn printUnbuffered( global.type.fmt(self), variable.init.fmt(self), variable.alignment, + try metadata_formatter.fmt("!dbg ", global.dbg), }); } - need_newline = true; } if (self.aliases.items.len > 0) { - if (need_newline) try writer.writeByte('\n'); + if (need_newline) try writer.writeByte('\n') else need_newline = true; for (self.aliases.items) |alias| { if (alias.global.getReplacement(self) != .none) continue; const global = alias.global.ptrConst(self); + metadata_formatter.need_comma = true; + defer metadata_formatter.need_comma = undefined; try writer.print( - \\{} ={}{}{}{}{ }{} alias {%}, {%} + \\{} ={}{}{}{}{ }{} alias {%}, {%}{} \\ , .{ alias.global.fmt(self), @@ -9408,9 +9423,9 @@ pub fn printUnbuffered( global.unnamed_addr, global.type.fmt(self), alias.aliasee.fmt(self), + try metadata_formatter.fmt("!dbg ", global.dbg), }); } - need_newline = true; } var attribute_groups: std.AutoArrayHashMapUnmanaged(Attributes, void) = .{}; @@ -9418,7 +9433,7 @@ pub fn printUnbuffered( for (0.., self.functions.items) |function_i, function| { if (function.global.getReplacement(self) != .none) continue; - if (need_newline) try writer.writeByte('\n'); + if (need_newline) try writer.writeByte('\n') else need_newline = true; const function_index: Function.Index = @enumFromInt(function_i); const global = function.global.ptrConst(self); const params_len = global.type.functionParameters(self).len; @@ -9464,13 +9479,30 @@ pub fn printUnbuffered( if (function_attributes != .none) try writer.print(" #{d}", .{ (try attribute_groups.getOrPutValue(self.gpa, function_attributes, {})).index, }); - try writer.print("{ }", .{function.alignment}); + { + metadata_formatter.need_comma = false; + defer metadata_formatter.need_comma = undefined; + try writer.print("{ }{}", .{ + function.alignment, + try metadata_formatter.fmt(" !dbg ", global.dbg), + }); + } if (function.instructions.len > 0) { var block_incoming_len: u32 = undefined; try writer.writeAll(" {\n"); + var maybe_dbg_index: ?u32 = null; for (params_len..function.instructions.len) |instruction_i| { const instruction_index: Function.Instruction.Index = @enumFromInt(instruction_i); const instruction = function.instructions.get(@intFromEnum(instruction_index)); + if (function.debug_locations.get(instruction_index)) |debug_location| switch (debug_location) { + .no_location => maybe_dbg_index = null, + .location => |location| { + const gop = try metadata_formatter.map.getOrPut(self.gpa, .{ + .debug_location = location, + }); + maybe_dbg_index = @intCast(gop.index); + }, + }; switch (instruction.tag) { .add, .@"add nsw", @@ -9555,7 +9587,7 @@ pub fn printUnbuffered( .xor, => |tag| { const extra = function.extraData(Function.Instruction.Binary, instruction.data); - try writer.print(" %{} = {s} {%}, {}\n", .{ + try writer.print(" %{} = {s} {%}, {}", .{ instruction_index.name(&function).fmt(self), @tagName(tag), extra.lhs.fmt(function_index, self), @@ -9577,7 +9609,7 @@ pub fn printUnbuffered( .zext, => |tag| { const extra = function.extraData(Function.Instruction.Cast, instruction.data); - try writer.print(" %{} = {s} {%} to {%}\n", .{ + try writer.print(" %{} = {s} {%} to {%}", .{ instruction_index.name(&function).fmt(self), @tagName(tag), extra.val.fmt(function_index, self), @@ -9588,11 +9620,14 @@ pub fn printUnbuffered( .@"alloca inalloca", => |tag| { const extra = function.extraData(Function.Instruction.Alloca, instruction.data); - try writer.print(" %{} = {s} {%}{,%}{, }{, }\n", .{ + try writer.print(" %{} = {s} {%}{,%}{, }{, }", .{ instruction_index.name(&function).fmt(self), @tagName(tag), extra.type.fmt(self), - extra.len.fmt(function_index, self), + Value.fmt(switch (extra.len) { + .@"1" => .none, + else => extra.len, + }, function_index, self), extra.info.alignment, extra.info.addr_space, }); @@ -9601,7 +9636,7 @@ pub fn printUnbuffered( .atomicrmw => |tag| { const extra = function.extraData(Function.Instruction.AtomicRmw, instruction.data); - try writer.print(" %{} = {s}{ } {s} {%}, {%}{ }{ }{, }\n", .{ + try writer.print(" %{} = {s}{ } {s} {%}, {%}{ }{ }{, }", .{ instruction_index.name(&function).fmt(self), @tagName(tag), extra.info.access_kind, @@ -9619,16 +9654,17 @@ pub fn printUnbuffered( if (@intFromEnum(instruction_index) > params_len) try writer.writeByte('\n'); try writer.print("{}:\n", .{name.fmt(self)}); + continue; }, .br => |tag| { const target: Function.Block.Index = @enumFromInt(instruction.data); - try writer.print(" {s} {%}\n", .{ + try writer.print(" {s} {%}", .{ @tagName(tag), target.toInst(&function).fmt(function_index, self), }); }, .br_cond => { const extra = function.extraData(Function.Instruction.BrCond, instruction.data); - try writer.print(" br {%}, {%}, {%}\n", .{ + try writer.print(" br {%}, {%}, {%}", .{ extra.cond.fmt(function_index, self), extra.then.toInst(&function).fmt(function_index, self), extra.@"else".toInst(&function).fmt(function_index, self), @@ -9668,10 +9704,12 @@ pub fn printUnbuffered( }); for (0.., args) |arg_index, arg| { if (arg_index > 0) try writer.writeAll(", "); - try writer.print("{%}{} {}", .{ + metadata_formatter.need_comma = false; + defer metadata_formatter.need_comma = undefined; + try writer.print("{%}{}{}", .{ arg.typeOf(function_index, self).fmt(self), extra.data.attributes.param(arg_index, self).fmt(self), - arg.fmt(function_index, self), + try metadata_formatter.fmtLocal(" ", arg, function_index), }); } try writer.writeByte(')'); @@ -9683,14 +9721,13 @@ pub fn printUnbuffered( {}, )).index, }); - try writer.writeByte('\n'); }, .cmpxchg, .@"cmpxchg weak", => |tag| { const extra = function.extraData(Function.Instruction.CmpXchg, instruction.data); - try writer.print(" %{} = {s}{ } {%}, {%}, {%}{ }{ }{ }{, }\n", .{ + try writer.print(" %{} = {s}{ } {%}, {%}, {%}{ }{ }{ }{, }", .{ instruction_index.name(&function).fmt(self), @tagName(tag), extra.info.access_kind, @@ -9706,7 +9743,7 @@ pub fn printUnbuffered( .extractelement => |tag| { const extra = function.extraData(Function.Instruction.ExtractElement, instruction.data); - try writer.print(" %{} = {s} {%}, {%}\n", .{ + try writer.print(" %{} = {s} {%}, {%}", .{ instruction_index.name(&function).fmt(self), @tagName(tag), extra.val.fmt(function_index, self), @@ -9725,7 +9762,6 @@ pub fn printUnbuffered( extra.data.val.fmt(function_index, self), }); for (indices) |index| try writer.print(", {d}", .{index}); - try writer.writeByte('\n'); }, .fence => |tag| { const info: MemoryAccessInfo = @bitCast(instruction.data); @@ -9739,7 +9775,7 @@ pub fn printUnbuffered( .@"fneg fast", => |tag| { const val: Value = @enumFromInt(instruction.data); - try writer.print(" %{} = {s} {%}\n", .{ + try writer.print(" %{} = {s} {%}", .{ instruction_index.name(&function).fmt(self), @tagName(tag), val.fmt(function_index, self), @@ -9762,12 +9798,11 @@ pub fn printUnbuffered( for (indices) |index| try writer.print(", {%}", .{ index.fmt(function_index, self), }); - try writer.writeByte('\n'); }, .insertelement => |tag| { const extra = function.extraData(Function.Instruction.InsertElement, instruction.data); - try writer.print(" %{} = {s} {%}, {%}, {%}\n", .{ + try writer.print(" %{} = {s} {%}, {%}, {%}", .{ instruction_index.name(&function).fmt(self), @tagName(tag), extra.val.fmt(function_index, self), @@ -9786,13 +9821,12 @@ pub fn printUnbuffered( extra.data.elem.fmt(function_index, self), }); for (indices) |index| try writer.print(", {d}", .{index}); - try writer.writeByte('\n'); }, .load, .@"load atomic", => |tag| { const extra = function.extraData(Function.Instruction.Load, instruction.data); - try writer.print(" %{} = {s}{ } {%}, {%}{ }{ }{, }\n", .{ + try writer.print(" %{} = {s}{ } {%}, {%}{ }{ }{, }", .{ instruction_index.name(&function).fmt(self), @tagName(tag), extra.info.access_kind, @@ -9822,23 +9856,22 @@ pub fn printUnbuffered( incoming_block.toInst(&function).fmt(function_index, self), }); } - try writer.writeByte('\n'); }, .ret => |tag| { const val: Value = @enumFromInt(instruction.data); - try writer.print(" {s} {%}\n", .{ + try writer.print(" {s} {%}", .{ @tagName(tag), val.fmt(function_index, self), }); }, .@"ret void", .@"unreachable", - => |tag| try writer.print(" {s}\n", .{@tagName(tag)}), + => |tag| try writer.print(" {s}", .{@tagName(tag)}), .select, .@"select fast", => |tag| { const extra = function.extraData(Function.Instruction.Select, instruction.data); - try writer.print(" %{} = {s} {%}, {%}, {%}\n", .{ + try writer.print(" %{} = {s} {%}, {%}, {%}", .{ instruction_index.name(&function).fmt(self), @tagName(tag), extra.cond.fmt(function_index, self), @@ -9849,7 +9882,7 @@ pub fn printUnbuffered( .shufflevector => |tag| { const extra = function.extraData(Function.Instruction.ShuffleVector, instruction.data); - try writer.print(" %{} = {s} {%}, {%}, {%}\n", .{ + try writer.print(" %{} = {s} {%}, {%}, {%}", .{ instruction_index.name(&function).fmt(self), @tagName(tag), extra.lhs.fmt(function_index, self), @@ -9861,7 +9894,7 @@ pub fn printUnbuffered( .@"store atomic", => |tag| { const extra = function.extraData(Function.Instruction.Store, instruction.data); - try writer.print(" {s}{ } {%}, {%}{ }{ }{, }\n", .{ + try writer.print(" {s}{ } {%}, {%}{ }{ }{, }", .{ @tagName(tag), extra.info.access_kind, extra.val.fmt(function_index, self), @@ -9889,11 +9922,11 @@ pub fn printUnbuffered( case_block.toInst(&function).fmt(function_index, self), }, ); - try writer.writeAll(" ]\n"); + try writer.writeAll(" ]"); }, .va_arg => |tag| { const extra = function.extraData(Function.Instruction.VaArg, instruction.data); - try writer.print(" %{} = {s} {%}, {%}\n", .{ + try writer.print(" %{} = {s} {%}, {%}", .{ instruction_index.name(&function).fmt(self), @tagName(tag), extra.list.fmt(function_index, self), @@ -9901,11 +9934,14 @@ pub fn printUnbuffered( }); }, } + + if (maybe_dbg_index) |dbg_index| { + try writer.print(", !dbg !{}\n", .{dbg_index}); + } else try writer.writeByte('\n'); } try writer.writeByte('}'); } try writer.writeByte('\n'); - need_newline = true; } if (attribute_groups.count() > 0) { @@ -9915,12 +9951,389 @@ pub fn printUnbuffered( \\attributes #{d} = {{{#"} }} \\ , .{ attribute_group_index, attribute_group.fmt(self) }); - need_newline = true; } -} -pub inline fn useLibLlvm(self: *const Builder) bool { - return build_options.have_llvm and self.use_lib_llvm; + if (self.metadata_named.count() > 0) { + if (need_newline) try writer.writeByte('\n') else need_newline = true; + for (self.metadata_named.keys(), self.metadata_named.values()) |name, data| { + const elements: []const Metadata = + @ptrCast(self.metadata_extra.items[data.index..][0..data.len]); + try writer.writeByte('!'); + try printEscapedString(name.slice(self), .quote_unless_valid_identifier, writer); + try writer.writeAll(" = !{"); + metadata_formatter.need_comma = false; + defer metadata_formatter.need_comma = undefined; + for (elements) |element| try writer.print("{}", .{try metadata_formatter.fmt("", element)}); + try writer.writeAll("}\n"); + } + } + + if (metadata_formatter.map.count() > 0) { + if (need_newline) try writer.writeByte('\n') else need_newline = true; + var metadata_index: usize = 0; + while (metadata_index < metadata_formatter.map.count()) : (metadata_index += 1) { + @setEvalBranchQuota(10_000); + try writer.print("!{} = ", .{metadata_index}); + metadata_formatter.need_comma = false; + defer metadata_formatter.need_comma = undefined; + + const key = metadata_formatter.map.keys()[metadata_index]; + const metadata_item = switch (key) { + .debug_location => |location| { + try metadata_formatter.specialized(.@"!", .DILocation, .{ + .line = location.line, + .column = location.column, + .scope = location.scope, + .inlinedAt = location.inlined_at, + .isImplicitCode = false, + }, writer); + continue; + }, + .metadata => |metadata| self.metadata_items.get(@intFromEnum(metadata)), + }; + + switch (metadata_item.tag) { + .none, .expression, .constant => unreachable, + .file => { + const extra = self.metadataExtraData(Metadata.File, metadata_item.data); + try metadata_formatter.specialized(.@"!", .DIFile, .{ + .filename = extra.filename, + .directory = extra.directory, + .checksumkind = null, + .checksum = null, + .source = null, + }, writer); + }, + .compile_unit, + .@"compile_unit optimized", + => |kind| { + const extra = self.metadataExtraData(Metadata.CompileUnit, metadata_item.data); + try metadata_formatter.specialized(.@"distinct !", .DICompileUnit, .{ + .language = .DW_LANG_C99, + .file = extra.file, + .producer = extra.producer, + .isOptimized = switch (kind) { + .compile_unit => false, + .@"compile_unit optimized" => true, + else => unreachable, + }, + .flags = null, + .runtimeVersion = 0, + .splitDebugFilename = null, + .emissionKind = .FullDebug, + .enums = extra.enums, + .retainedTypes = null, + .globals = extra.globals, + .imports = null, + .macros = null, + .dwoId = null, + .splitDebugInlining = false, + .debugInfoForProfiling = null, + .nameTableKind = null, + .rangesBaseAddress = null, + .sysroot = null, + .sdk = null, + }, writer); + }, + .subprogram, + .@"subprogram local", + .@"subprogram definition", + .@"subprogram local definition", + .@"subprogram optimized", + .@"subprogram optimized local", + .@"subprogram optimized definition", + .@"subprogram optimized local definition", + => |kind| { + const extra = self.metadataExtraData(Metadata.Subprogram, metadata_item.data); + try metadata_formatter.specialized(.@"distinct !", .DISubprogram, .{ + .name = extra.name, + .linkageName = extra.linkage_name, + .scope = extra.file, + .file = extra.file, + .line = extra.line, + .type = extra.ty, + .scopeLine = extra.scope_line, + .containingType = null, + .virtualIndex = null, + .thisAdjustment = null, + .flags = extra.di_flags, + .spFlags = @as(Metadata.Subprogram.DISPFlags, @bitCast(@as(u32, @as(u3, @intCast( + @intFromEnum(kind) - @intFromEnum(Metadata.Tag.subprogram), + ))) << 2)), + .unit = extra.compile_unit, + .templateParams = null, + .declaration = null, + .retainedNodes = null, + .thrownTypes = null, + .annotations = null, + .targetFuncName = null, + }, writer); + }, + .lexical_block => { + const extra = self.metadataExtraData(Metadata.LexicalBlock, metadata_item.data); + try metadata_formatter.specialized(.@"distinct !", .DILexicalBlock, .{ + .scope = extra.scope, + .file = extra.file, + .line = extra.line, + .column = extra.column, + }, writer); + }, + .location => { + const extra = self.metadataExtraData(Metadata.Location, metadata_item.data); + try metadata_formatter.specialized(.@"!", .DILocation, .{ + .line = extra.line, + .column = extra.column, + .scope = extra.scope, + .inlinedAt = extra.inlined_at, + .isImplicitCode = false, + }, writer); + }, + .basic_bool_type, + .basic_unsigned_type, + .basic_signed_type, + .basic_float_type, + => |kind| { + const extra = self.metadataExtraData(Metadata.BasicType, metadata_item.data); + try metadata_formatter.specialized(.@"!", .DIBasicType, .{ + .tag = null, + .name = switch (extra.name) { + .none => null, + else => extra.name, + }, + .size = extra.bitSize(), + .@"align" = null, + .encoding = @as(enum { + DW_ATE_boolean, + DW_ATE_unsigned, + DW_ATE_signed, + DW_ATE_float, + }, switch (kind) { + .basic_bool_type => .DW_ATE_boolean, + .basic_unsigned_type => .DW_ATE_unsigned, + .basic_signed_type => .DW_ATE_signed, + .basic_float_type => .DW_ATE_float, + else => unreachable, + }), + .flags = null, + }, writer); + }, + .composite_struct_type, + .composite_union_type, + .composite_enumeration_type, + .composite_array_type, + .composite_vector_type, + => |kind| { + const extra = self.metadataExtraData(Metadata.CompositeType, metadata_item.data); + try metadata_formatter.specialized(.@"!", .DICompositeType, .{ + .tag = @as(enum { + DW_TAG_structure_type, + DW_TAG_union_type, + DW_TAG_enumeration_type, + DW_TAG_array_type, + }, switch (kind) { + .composite_struct_type => .DW_TAG_structure_type, + .composite_union_type => .DW_TAG_union_type, + .composite_enumeration_type => .DW_TAG_enumeration_type, + .composite_array_type, .composite_vector_type => .DW_TAG_array_type, + else => unreachable, + }), + .name = switch (extra.name) { + .none => null, + else => extra.name, + }, + .scope = extra.scope, + .file = null, + .line = null, + .baseType = extra.underlying_type, + .size = extra.bitSize(), + .@"align" = extra.bitAlign(), + .offset = null, + .flags = null, + .elements = extra.fields_tuple, + .runtimeLang = null, + .vtableHolder = null, + .templateParams = null, + .identifier = null, + .discriminator = null, + .dataLocation = null, + .associated = null, + .allocated = null, + .rank = null, + .annotations = null, + }, writer); + }, + .derived_pointer_type, + .derived_member_type, + => |kind| { + const extra = self.metadataExtraData(Metadata.DerivedType, metadata_item.data); + try metadata_formatter.specialized(.@"!", .DIDerivedType, .{ + .tag = @as(enum { + DW_TAG_pointer_type, + DW_TAG_member, + }, switch (kind) { + .derived_pointer_type => .DW_TAG_pointer_type, + .derived_member_type => .DW_TAG_member, + else => unreachable, + }), + .name = switch (extra.name) { + .none => null, + else => extra.name, + }, + .scope = extra.scope, + .file = null, + .line = null, + .baseType = extra.underlying_type, + .size = extra.bitSize(), + .@"align" = extra.bitAlign(), + .offset = switch (extra.bitOffset()) { + 0 => null, + else => |bit_offset| bit_offset, + }, + .flags = null, + .extraData = null, + .dwarfAddressSpace = null, + .annotations = null, + }, writer); + }, + .subroutine_type => { + const extra = self.metadataExtraData(Metadata.SubroutineType, metadata_item.data); + try metadata_formatter.specialized(.@"!", .DISubroutineType, .{ + .flags = null, + .cc = null, + .types = extra.types_tuple, + }, writer); + }, + .enumerator_unsigned, + .enumerator_signed_positive, + .enumerator_signed_negative, + => |kind| { + const extra = self.metadataExtraData(Metadata.Enumerator, metadata_item.data); + + const ExpectedContents = extern struct { + string: [(64 * 8 / std.math.log2(10)) + 2]u8, + limbs: [ + std.math.big.int.calcToStringLimbsBufferLen( + 64 / @sizeOf(std.math.big.Limb), + 10, + ) + ]std.math.big.Limb, + }; + var stack align(@alignOf(ExpectedContents)) = + std.heap.stackFallback(@sizeOf(ExpectedContents), self.gpa); + const allocator = stack.get(); + + const limbs = self.metadata_limbs.items[extra.limbs_index..][0..extra.limbs_len]; + const bigint: std.math.big.int.Const = .{ + .limbs = limbs, + .positive = switch (kind) { + .enumerator_unsigned, + .enumerator_signed_positive, + => true, + .enumerator_signed_negative => false, + else => unreachable, + }, + }; + const str = try bigint.toStringAlloc(allocator, 10, undefined); + defer allocator.free(str); + + try metadata_formatter.specialized(.@"!", .DIEnumerator, .{ + .name = extra.name, + .value = str, + .isUnsigned = switch (kind) { + .enumerator_unsigned => true, + .enumerator_signed_positive, .enumerator_signed_negative => false, + else => unreachable, + }, + }, writer); + }, + .subrange => { + const extra = self.metadataExtraData(Metadata.Subrange, metadata_item.data); + try metadata_formatter.specialized(.@"!", .DISubrange, .{ + .count = extra.count, + .lowerBound = extra.lower_bound, + .upperBound = null, + .stride = null, + }, writer); + }, + .tuple => { + var extra = self.metadataExtraDataTrail(Metadata.Tuple, metadata_item.data); + const elements = extra.trail.next(extra.data.elements_len, Metadata, self); + try writer.writeAll("!{"); + 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", .{ + .behavior = try metadata_formatter.fmt("", extra.behavior), + .name = try metadata_formatter.fmt("", extra.name), + .constant = try metadata_formatter.fmt("", extra.constant), + }); + }, + .local_var => { + const extra = self.metadataExtraData(Metadata.LocalVar, metadata_item.data); + try metadata_formatter.specialized(.@"!", .DILocalVariable, .{ + .name = extra.name, + .arg = null, + .scope = extra.scope, + .file = extra.file, + .line = extra.line, + .type = extra.ty, + .flags = null, + .@"align" = null, + .annotations = null, + }, writer); + }, + .parameter => { + const extra = self.metadataExtraData(Metadata.Parameter, metadata_item.data); + try metadata_formatter.specialized(.@"!", .DILocalVariable, .{ + .name = extra.name, + .arg = extra.arg_no, + .scope = extra.scope, + .file = extra.file, + .line = extra.line, + .type = extra.ty, + .flags = null, + .@"align" = null, + .annotations = null, + }, writer); + }, + .global_var, + .@"global_var local", + => |kind| { + const extra = self.metadataExtraData(Metadata.GlobalVar, metadata_item.data); + try metadata_formatter.specialized(.@"distinct !", .DIGlobalVariable, .{ + .name = extra.name, + .linkageName = extra.linkage_name, + .scope = extra.scope, + .file = extra.file, + .line = extra.line, + .type = extra.ty, + .isLocal = switch (kind) { + .global_var => false, + .@"global_var local" => true, + else => unreachable, + }, + .isDefinition = true, + .declaration = null, + .templateParams = null, + .@"align" = null, + .annotations = null, + }, writer); + }, + .global_var_expression => { + const extra = + self.metadataExtraData(Metadata.GlobalVarExpression, metadata_item.data); + try metadata_formatter.specialized(.@"!", .DIGlobalVariableExpression, .{ + .@"var" = extra.variable, + .expr = extra.expression, + }, writer); + }, + } + } + } } const NoExtra = struct {}; @@ -9954,10 +10367,9 @@ fn printEscapedString( } fn ensureUnusedGlobalCapacity(self: *Builder, name: String) Allocator.Error!void { - if (self.useLibLlvm()) try self.llvm.globals.ensureUnusedCapacity(self.gpa, 1); try self.string_map.ensureUnusedCapacity(self.gpa, 1); if (name.slice(self)) |id| { - const count: usize = comptime std.fmt.count("{d}" ++ .{0}, .{std.math.maxInt(u32)}); + const count: usize = comptime std.fmt.count("{d}", .{std.math.maxInt(u32)}); try self.string_bytes.ensureUnusedCapacity(self.gpa, id.len + count); } try self.string_indices.ensureUnusedCapacity(self.gpa, 1); @@ -9970,7 +10382,7 @@ fn fnTypeAssumeCapacity( ret: Type, params: []const Type, comptime kind: Type.Function.Kind, -) (if (build_options.have_llvm) Allocator.Error else error{})!Type { +) Type { const tag: Type.Tag = switch (kind) { .normal => .function, .vararg => .vararg_function, @@ -10007,20 +10419,6 @@ fn fnTypeAssumeCapacity( }), }); self.type_extra.appendSliceAssumeCapacity(@ptrCast(params)); - if (self.useLibLlvm()) { - const llvm_params = try self.gpa.alloc(*llvm.Type, params.len); - defer self.gpa.free(llvm_params); - for (llvm_params, params) |*llvm_param, param| llvm_param.* = param.toLlvm(self); - self.llvm.types.appendAssumeCapacity(llvm.functionType( - ret.toLlvm(self), - llvm_params.ptr, - @intCast(llvm_params.len), - switch (kind) { - .normal => .False, - .vararg => .True, - }, - )); - } } return @enumFromInt(gop.index); } @@ -10028,8 +10426,6 @@ fn fnTypeAssumeCapacity( fn intTypeAssumeCapacity(self: *Builder, bits: u24) Type { assert(bits > 0); const result = self.getOrPutTypeNoExtraAssumeCapacity(.{ .tag = .integer, .data = bits }); - if (self.useLibLlvm() and result.new) - self.llvm.types.appendAssumeCapacity(self.llvm.context.intType(bits)); return result.type; } @@ -10037,8 +10433,6 @@ fn ptrTypeAssumeCapacity(self: *Builder, addr_space: AddrSpace) Type { const result = self.getOrPutTypeNoExtraAssumeCapacity( .{ .tag = .pointer, .data = @intFromEnum(addr_space) }, ); - if (self.useLibLlvm() and result.new) - self.llvm.types.appendAssumeCapacity(self.llvm.context.pointerType(@intFromEnum(addr_space))); return result.type; } @@ -10076,10 +10470,6 @@ fn vectorTypeAssumeCapacity( .tag = tag, .data = self.addTypeExtraAssumeCapacity(data), }); - if (self.useLibLlvm()) self.llvm.types.appendAssumeCapacity(switch (kind) { - .normal => &llvm.Type.vectorType, - .scalable => &llvm.Type.scalableVectorType, - }(child.toLlvm(self), @intCast(len))); } return @enumFromInt(gop.index); } @@ -10109,9 +10499,6 @@ fn arrayTypeAssumeCapacity(self: *Builder, len: u64, child: Type) Type { .tag = .small_array, .data = self.addTypeExtraAssumeCapacity(data), }); - if (self.useLibLlvm()) self.llvm.types.appendAssumeCapacity( - child.toLlvm(self).arrayType2(len), - ); } return @enumFromInt(gop.index); } else { @@ -10142,9 +10529,6 @@ fn arrayTypeAssumeCapacity(self: *Builder, len: u64, child: Type) Type { .tag = .array, .data = self.addTypeExtraAssumeCapacity(data), }); - if (self.useLibLlvm()) self.llvm.types.appendAssumeCapacity( - child.toLlvm(self).arrayType2(len), - ); } return @enumFromInt(gop.index); } @@ -10154,7 +10538,7 @@ fn structTypeAssumeCapacity( self: *Builder, comptime kind: Type.Structure.Kind, fields: []const Type, -) (if (build_options.have_llvm) Allocator.Error else error{})!Type { +) Type { const tag: Type.Tag = switch (kind) { .normal => .structure, .@"packed" => .packed_structure, @@ -10186,25 +10570,6 @@ fn structTypeAssumeCapacity( }), }); self.type_extra.appendSliceAssumeCapacity(@ptrCast(fields)); - if (self.useLibLlvm()) { - const ExpectedContents = [expected_fields_len]*llvm.Type; - var stack align(@alignOf(ExpectedContents)) = - std.heap.stackFallback(@sizeOf(ExpectedContents), self.gpa); - const allocator = stack.get(); - - const llvm_fields = try allocator.alloc(*llvm.Type, fields.len); - defer allocator.free(llvm_fields); - for (llvm_fields, fields) |*llvm_field, field| llvm_field.* = field.toLlvm(self); - - self.llvm.types.appendAssumeCapacity(self.llvm.context.structType( - llvm_fields.ptr, - @intCast(llvm_fields.len), - switch (kind) { - .normal => .False, - .@"packed" => .True, - }, - )); - } } return @enumFromInt(gop.index); } @@ -10246,9 +10611,6 @@ fn opaqueTypeAssumeCapacity(self: *Builder, name: String) Type { }); const result: Type = @enumFromInt(gop.index); type_gop.value_ptr.* = result; - if (self.useLibLlvm()) self.llvm.types.appendAssumeCapacity( - self.llvm.context.structCreateNamed(id.slice(self) orelse ""), - ); return result; } @@ -10271,7 +10633,6 @@ fn ensureUnusedTypeCapacity( self.gpa, count * (@typeInfo(Extra).Struct.fields.len + trail_len), ); - if (self.useLibLlvm()) try self.llvm.types.ensureUnusedCapacity(self.gpa, count); } fn getOrPutTypeNoExtraAssumeCapacity(self: *Builder, item: Type.Item) struct { new: bool, type: Type } { @@ -10305,7 +10666,7 @@ fn addTypeExtraAssumeCapacity(self: *Builder, extra: anytype) Type.Item.ExtraInd self.type_extra.appendAssumeCapacity(switch (field.type) { u32 => value, String, Type => @intFromEnum(value), - else => @compileError("bad field type: " ++ @typeName(field.type)), + else => @compileError("bad field type: " ++ field.name ++ ": " ++ @typeName(field.type)), }); } return result; @@ -10388,10 +10749,7 @@ fn bigIntConstAssumeCapacity( assert(type_item.tag == .integer); const bits = type_item.data; - const ExpectedContents = extern struct { - limbs: [64 / @sizeOf(std.math.big.Limb)]std.math.big.Limb, - llvm_limbs: if (build_options.have_llvm) [64 / @sizeOf(u64)]u64 else void, - }; + const ExpectedContents = [64 / @sizeOf(std.math.big.Limb)]std.math.big.Limb; var stack align(@alignOf(ExpectedContents)) = std.heap.stackFallback(@sizeOf(ExpectedContents), self.gpa); const allocator = stack.get(); @@ -10448,44 +10806,6 @@ fn bigIntConstAssumeCapacity( @ptrCast(self.constant_limbs.addManyAsArrayAssumeCapacity(Constant.Integer.limbs)); extra.* = .{ .type = ty, .limbs_len = @intCast(canonical_value.limbs.len) }; self.constant_limbs.appendSliceAssumeCapacity(canonical_value.limbs); - if (self.useLibLlvm()) { - const llvm_type = ty.toLlvm(self); - if (canonical_value.to(c_longlong)) |small| { - self.llvm.constants.appendAssumeCapacity(llvm_type.constInt(@bitCast(small), .True)); - } else |_| if (canonical_value.to(c_ulonglong)) |small| { - self.llvm.constants.appendAssumeCapacity(llvm_type.constInt(small, .False)); - } else |_| { - const llvm_limbs = try allocator.alloc(u64, std.math.divCeil( - usize, - if (canonical_value.positive) canonical_value.bitCountAbs() else bits, - @bitSizeOf(u64), - ) catch unreachable); - defer allocator.free(llvm_limbs); - var limb_index: usize = 0; - var borrow: std.math.big.Limb = 0; - for (llvm_limbs) |*result_limb| { - var llvm_limb: u64 = 0; - inline for (0..Constant.Integer.limbs) |shift| { - const limb = if (limb_index < canonical_value.limbs.len) - canonical_value.limbs[limb_index] - else - 0; - limb_index += 1; - llvm_limb |= @as(u64, limb) << shift * @bitSizeOf(std.math.big.Limb); - } - if (!canonical_value.positive) { - const overflow = @subWithOverflow(borrow, llvm_limb); - llvm_limb = overflow[0]; - borrow -%= overflow[1]; - assert(borrow == 0 or borrow == std.math.maxInt(std.math.big.Limb)); - } - result_limb.* = llvm_limb; - } - self.llvm.constants.appendAssumeCapacity( - llvm_type.constIntOfArbitraryPrecision(@intCast(llvm_limbs.len), llvm_limbs.ptr), - ); - } - } } return @enumFromInt(gop.index); } @@ -10494,13 +10814,6 @@ fn halfConstAssumeCapacity(self: *Builder, val: f16) Constant { const result = self.getOrPutConstantNoExtraAssumeCapacity( .{ .tag = .half, .data = @as(u16, @bitCast(val)) }, ); - if (self.useLibLlvm() and result.new) self.llvm.constants.appendAssumeCapacity( - if (std.math.isSignalNan(val)) - Type.i16.toLlvm(self).constInt(@as(u16, @bitCast(val)), .False) - .constBitCast(Type.half.toLlvm(self)) - else - Type.half.toLlvm(self).constReal(val), - ); return result.constant; } @@ -10509,16 +10822,6 @@ fn bfloatConstAssumeCapacity(self: *Builder, val: f32) Constant { const result = self.getOrPutConstantNoExtraAssumeCapacity( .{ .tag = .bfloat, .data = @bitCast(val) }, ); - if (self.useLibLlvm() and result.new) self.llvm.constants.appendAssumeCapacity( - if (std.math.isSignalNan(val)) - Type.i16.toLlvm(self).constInt(@as(u32, @bitCast(val)) >> 16, .False) - .constBitCast(Type.bfloat.toLlvm(self)) - else - Type.bfloat.toLlvm(self).constReal(val), - ); - - if (self.useLibLlvm() and result.new) - self.llvm.constants.appendAssumeCapacity(Type.bfloat.toLlvm(self).constReal(val)); return result.constant; } @@ -10526,13 +10829,6 @@ fn floatConstAssumeCapacity(self: *Builder, val: f32) Constant { const result = self.getOrPutConstantNoExtraAssumeCapacity( .{ .tag = .float, .data = @bitCast(val) }, ); - if (self.useLibLlvm() and result.new) self.llvm.constants.appendAssumeCapacity( - if (std.math.isSignalNan(val)) - Type.i32.toLlvm(self).constInt(@as(u32, @bitCast(val)), .False) - .constBitCast(Type.float.toLlvm(self)) - else - Type.float.toLlvm(self).constReal(val), - ); return result.constant; } @@ -10563,13 +10859,6 @@ fn doubleConstAssumeCapacity(self: *Builder, val: f64) Constant { .hi = @intCast(@as(u64, @bitCast(val)) >> 32), }), }); - if (self.useLibLlvm()) self.llvm.constants.appendAssumeCapacity( - if (std.math.isSignalNan(val)) - Type.i64.toLlvm(self).constInt(@as(u64, @bitCast(val)), .False) - .constBitCast(Type.double.toLlvm(self)) - else - Type.double.toLlvm(self).constReal(val), - ); } return @enumFromInt(gop.index); } @@ -10604,17 +10893,6 @@ fn fp128ConstAssumeCapacity(self: *Builder, val: f128) Constant { .hi_hi = @intCast(@as(u128, @bitCast(val)) >> 96), }), }); - if (self.useLibLlvm()) { - const llvm_limbs = [_]u64{ - @truncate(@as(u128, @bitCast(val))), - @intCast(@as(u128, @bitCast(val)) >> 64), - }; - self.llvm.constants.appendAssumeCapacity( - Type.i128.toLlvm(self) - .constIntOfArbitraryPrecision(@intCast(llvm_limbs.len), &llvm_limbs) - .constBitCast(Type.fp128.toLlvm(self)), - ); - } } return @enumFromInt(gop.index); } @@ -10648,17 +10926,6 @@ fn x86_fp80ConstAssumeCapacity(self: *Builder, val: f80) Constant { .hi = @intCast(@as(u80, @bitCast(val)) >> 64), }), }); - if (self.useLibLlvm()) { - const llvm_limbs = [_]u64{ - @truncate(@as(u80, @bitCast(val))), - @intCast(@as(u80, @bitCast(val)) >> 64), - }; - self.llvm.constants.appendAssumeCapacity( - Type.i80.toLlvm(self) - .constIntOfArbitraryPrecision(@intCast(llvm_limbs.len), &llvm_limbs) - .constBitCast(Type.x86_fp80.toLlvm(self)), - ); - } } return @enumFromInt(gop.index); } @@ -10693,14 +10960,6 @@ fn ppc_fp128ConstAssumeCapacity(self: *Builder, val: [2]f64) Constant { .hi_hi = @intCast(@as(u64, @bitCast(val[1])) >> 32), }), }); - if (self.useLibLlvm()) { - const llvm_limbs: [2]u64 = @bitCast(val); - self.llvm.constants.appendAssumeCapacity( - Type.i128.toLlvm(self) - .constIntOfArbitraryPrecision(@intCast(llvm_limbs.len), &llvm_limbs) - .constBitCast(Type.ppc_fp128.toLlvm(self)), - ); - } } return @enumFromInt(gop.index); } @@ -10710,8 +10969,6 @@ fn nullConstAssumeCapacity(self: *Builder, ty: Type) Constant { const result = self.getOrPutConstantNoExtraAssumeCapacity( .{ .tag = .null, .data = @intFromEnum(ty) }, ); - if (self.useLibLlvm() and result.new) - self.llvm.constants.appendAssumeCapacity(ty.toLlvm(self).constNull()); return result.constant; } @@ -10720,16 +10977,10 @@ fn noneConstAssumeCapacity(self: *Builder, ty: Type) Constant { const result = self.getOrPutConstantNoExtraAssumeCapacity( .{ .tag = .none, .data = @intFromEnum(ty) }, ); - if (self.useLibLlvm() and result.new) - self.llvm.constants.appendAssumeCapacity(ty.toLlvm(self).constNull()); return result.constant; } -fn structConstAssumeCapacity( - self: *Builder, - ty: Type, - vals: []const Constant, -) (if (build_options.have_llvm) Allocator.Error else error{})!Constant { +fn structConstAssumeCapacity(self: *Builder, ty: Type, vals: []const Constant) Constant { const type_item = self.type_items.items[@intFromEnum(ty)]; var extra = self.typeExtraDataTrail(Type.Structure, switch (type_item.tag) { .structure, .packed_structure => type_item.data, @@ -10756,28 +11007,10 @@ fn structConstAssumeCapacity( else => unreachable, }; const result = self.getOrPutConstantAggregateAssumeCapacity(tag, ty, vals); - if (self.useLibLlvm() and result.new) { - const ExpectedContents = [expected_fields_len]*llvm.Value; - var stack align(@alignOf(ExpectedContents)) = - std.heap.stackFallback(@sizeOf(ExpectedContents), self.gpa); - const allocator = stack.get(); - - const llvm_vals = try allocator.alloc(*llvm.Value, vals.len); - defer allocator.free(llvm_vals); - for (llvm_vals, vals) |*llvm_val, val| llvm_val.* = val.toLlvm(self); - - self.llvm.constants.appendAssumeCapacity( - ty.toLlvm(self).constNamedStruct(llvm_vals.ptr, @intCast(llvm_vals.len)), - ); - } return result.constant; } -fn arrayConstAssumeCapacity( - self: *Builder, - ty: Type, - vals: []const Constant, -) (if (build_options.have_llvm) Allocator.Error else error{})!Constant { +fn arrayConstAssumeCapacity(self: *Builder, ty: Type, vals: []const Constant) Constant { const type_item = self.type_items.items[@intFromEnum(ty)]; const type_extra: struct { len: u64, child: Type } = switch (type_item.tag) { inline .small_array, .array => |kind| extra: { @@ -10798,20 +11031,6 @@ fn arrayConstAssumeCapacity( } else return self.zeroInitConstAssumeCapacity(ty); const result = self.getOrPutConstantAggregateAssumeCapacity(.array, ty, vals); - if (self.useLibLlvm() and result.new) { - const ExpectedContents = [expected_fields_len]*llvm.Value; - var stack align(@alignOf(ExpectedContents)) = - std.heap.stackFallback(@sizeOf(ExpectedContents), self.gpa); - const allocator = stack.get(); - - const llvm_vals = try allocator.alloc(*llvm.Value, vals.len); - defer allocator.free(llvm_vals); - for (llvm_vals, vals) |*llvm_val, val| llvm_val.* = val.toLlvm(self); - - self.llvm.constants.appendAssumeCapacity( - type_extra.child.toLlvm(self).constArray2(llvm_vals.ptr, llvm_vals.len), - ); - } return result.constant; } @@ -10822,30 +11041,10 @@ fn stringConstAssumeCapacity(self: *Builder, val: String) Constant { const result = self.getOrPutConstantNoExtraAssumeCapacity( .{ .tag = .string, .data = @intFromEnum(val) }, ); - if (self.useLibLlvm() and result.new) self.llvm.constants.appendAssumeCapacity( - self.llvm.context.constString(slice.ptr, @intCast(slice.len), .True), - ); return result.constant; } -fn stringNullConstAssumeCapacity(self: *Builder, val: String) Constant { - const slice = val.slice(self).?; - const ty = self.arrayTypeAssumeCapacity(slice.len + 1, .i8); - if (std.mem.allEqual(u8, slice, 0)) return self.zeroInitConstAssumeCapacity(ty); - const result = self.getOrPutConstantNoExtraAssumeCapacity( - .{ .tag = .string_null, .data = @intFromEnum(val) }, - ); - if (self.useLibLlvm() and result.new) self.llvm.constants.appendAssumeCapacity( - self.llvm.context.constString(slice.ptr, @intCast(slice.len + 1), .True), - ); - return result.constant; -} - -fn vectorConstAssumeCapacity( - self: *Builder, - ty: Type, - vals: []const Constant, -) (if (build_options.have_llvm) Allocator.Error else error{})!Constant { +fn vectorConstAssumeCapacity(self: *Builder, ty: Type, vals: []const Constant) Constant { assert(ty.isVector(self)); assert(ty.vectorLen(self) == vals.len); for (vals) |val| assert(ty.childType(self) == val.typeOf(self)); @@ -10858,28 +11057,10 @@ fn vectorConstAssumeCapacity( } else return self.zeroInitConstAssumeCapacity(ty); const result = self.getOrPutConstantAggregateAssumeCapacity(.vector, ty, vals); - if (self.useLibLlvm() and result.new) { - const ExpectedContents = [expected_fields_len]*llvm.Value; - var stack align(@alignOf(ExpectedContents)) = - std.heap.stackFallback(@sizeOf(ExpectedContents), self.gpa); - const allocator = stack.get(); - - const llvm_vals = try allocator.alloc(*llvm.Value, vals.len); - defer allocator.free(llvm_vals); - for (llvm_vals, vals) |*llvm_val, val| llvm_val.* = val.toLlvm(self); - - self.llvm.constants.appendAssumeCapacity( - llvm.constVector(llvm_vals.ptr, @intCast(llvm_vals.len)), - ); - } return result.constant; } -fn splatConstAssumeCapacity( - self: *Builder, - ty: Type, - val: Constant, -) (if (build_options.have_llvm) Allocator.Error else error{})!Constant { +fn splatConstAssumeCapacity(self: *Builder, ty: Type, val: Constant) Constant { assert(ty.scalarType(self) == val.typeOf(self)); if (!ty.isVector(self)) return val; @@ -10909,20 +11090,6 @@ fn splatConstAssumeCapacity( .tag = .splat, .data = self.addConstantExtraAssumeCapacity(data), }); - if (self.useLibLlvm()) { - const ExpectedContents = [expected_fields_len]*llvm.Value; - var stack align(@alignOf(ExpectedContents)) = - std.heap.stackFallback(@sizeOf(ExpectedContents), self.gpa); - const allocator = stack.get(); - - const llvm_vals = try allocator.alloc(*llvm.Value, ty.vectorLen(self)); - defer allocator.free(llvm_vals); - @memset(llvm_vals, val.toLlvm(self)); - - self.llvm.constants.appendAssumeCapacity( - llvm.constVector(llvm_vals.ptr, @intCast(llvm_vals.len)), - ); - } } return @enumFromInt(gop.index); } @@ -10964,8 +11131,6 @@ fn zeroInitConstAssumeCapacity(self: *Builder, ty: Type) Constant { const result = self.getOrPutConstantNoExtraAssumeCapacity( .{ .tag = .zeroinitializer, .data = @intFromEnum(ty) }, ); - if (self.useLibLlvm() and result.new) - self.llvm.constants.appendAssumeCapacity(ty.toLlvm(self).constNull()); return result.constant; } @@ -10981,8 +11146,6 @@ fn undefConstAssumeCapacity(self: *Builder, ty: Type) Constant { const result = self.getOrPutConstantNoExtraAssumeCapacity( .{ .tag = .undef, .data = @intFromEnum(ty) }, ); - if (self.useLibLlvm() and result.new) - self.llvm.constants.appendAssumeCapacity(ty.toLlvm(self).getUndef()); return result.constant; } @@ -10998,8 +11161,6 @@ fn poisonConstAssumeCapacity(self: *Builder, ty: Type) Constant { const result = self.getOrPutConstantNoExtraAssumeCapacity( .{ .tag = .poison, .data = @intFromEnum(ty) }, ); - if (self.useLibLlvm() and result.new) - self.llvm.constants.appendAssumeCapacity(ty.toLlvm(self).getPoison()); return result.constant; } @@ -11032,9 +11193,6 @@ fn blockAddrConstAssumeCapacity( .tag = .blockaddress, .data = self.addConstantExtraAssumeCapacity(data), }); - if (self.useLibLlvm()) self.llvm.constants.appendAssumeCapacity( - function.toLlvm(self).blockAddress(block.toValue(self, function).toLlvm(self, function)), - ); } return @enumFromInt(gop.index); } @@ -11043,7 +11201,6 @@ fn dsoLocalEquivalentConstAssumeCapacity(self: *Builder, function: Function.Inde const result = self.getOrPutConstantNoExtraAssumeCapacity( .{ .tag = .dso_local_equivalent, .data = @intFromEnum(function) }, ); - if (self.useLibLlvm() and result.new) self.llvm.constants.appendAssumeCapacity(undefined); return result.constant; } @@ -11051,7 +11208,6 @@ fn noCfiConstAssumeCapacity(self: *Builder, function: Function.Index) Constant { const result = self.getOrPutConstantNoExtraAssumeCapacity( .{ .tag = .no_cfi, .data = @intFromEnum(function) }, ); - if (self.useLibLlvm() and result.new) self.llvm.constants.appendAssumeCapacity(undefined); return result.constant; } @@ -11141,22 +11297,6 @@ fn castConstAssumeCapacity(self: *Builder, tag: Constant.Tag, val: Constant, ty: .tag = tag, .data = self.addConstantExtraAssumeCapacity(data.cast), }); - if (self.useLibLlvm()) self.llvm.constants.appendAssumeCapacity(switch (tag) { - .trunc => &llvm.Value.constTrunc, - .zext => &llvm.Value.constZExt, - .sext => &llvm.Value.constSExt, - .fptrunc => &llvm.Value.constFPTrunc, - .fpext => &llvm.Value.constFPExt, - .fptoui => &llvm.Value.constFPToUI, - .fptosi => &llvm.Value.constFPToSI, - .uitofp => &llvm.Value.constUIToFP, - .sitofp => &llvm.Value.constSIToFP, - .ptrtoint => &llvm.Value.constPtrToInt, - .inttoptr => &llvm.Value.constIntToPtr, - .bitcast => &llvm.Value.constBitCast, - .addrspacecast => &llvm.Value.constAddrSpaceCast, - else => unreachable, - }(val.toLlvm(self), ty.toLlvm(self))); } return @enumFromInt(gop.index); } @@ -11168,7 +11308,7 @@ fn gepConstAssumeCapacity( base: Constant, inrange: ?u16, indices: []const Constant, -) (if (build_options.have_llvm) Allocator.Error else error{})!Constant { +) Constant { const tag: Constant.Tag = switch (kind) { .normal => .getelementptr, .inbounds => .@"getelementptr inbounds", @@ -11249,21 +11389,6 @@ fn gepConstAssumeCapacity( }), }); self.constant_extra.appendSliceAssumeCapacity(@ptrCast(indices)); - if (self.useLibLlvm()) { - const ExpectedContents = [expected_gep_indices_len]*llvm.Value; - var stack align(@alignOf(ExpectedContents)) = - std.heap.stackFallback(@sizeOf(ExpectedContents), self.gpa); - const allocator = stack.get(); - - const llvm_indices = try allocator.alloc(*llvm.Value, indices.len); - defer allocator.free(llvm_indices); - for (llvm_indices, indices) |*llvm_index, index| llvm_index.* = index.toLlvm(self); - - self.llvm.constants.appendAssumeCapacity(switch (kind) { - .normal => &llvm.Type.constGEP, - .inbounds => &llvm.Type.constInBoundsGEP, - }(ty.toLlvm(self), base.toLlvm(self), llvm_indices.ptr, @intCast(llvm_indices.len))); - } } return @enumFromInt(gop.index); } @@ -11298,9 +11423,6 @@ fn icmpConstAssumeCapacity( .tag = .icmp, .data = self.addConstantExtraAssumeCapacity(data), }); - if (self.useLibLlvm()) self.llvm.constants.appendAssumeCapacity( - llvm.constICmp(cond.toLlvm(), lhs.toLlvm(self), rhs.toLlvm(self)), - ); } return @enumFromInt(gop.index); } @@ -11335,9 +11457,6 @@ fn fcmpConstAssumeCapacity( .tag = .fcmp, .data = self.addConstantExtraAssumeCapacity(data), }); - if (self.useLibLlvm()) self.llvm.constants.appendAssumeCapacity( - llvm.constFCmp(cond.toLlvm(), lhs.toLlvm(self), rhs.toLlvm(self)), - ); } return @enumFromInt(gop.index); } @@ -11371,9 +11490,6 @@ fn extractElementConstAssumeCapacity( .tag = .extractelement, .data = self.addConstantExtraAssumeCapacity(data), }); - if (self.useLibLlvm()) self.llvm.constants.appendAssumeCapacity( - val.toLlvm(self).constExtractElement(index.toLlvm(self)), - ); } return @enumFromInt(gop.index); } @@ -11408,9 +11524,6 @@ fn insertElementConstAssumeCapacity( .tag = .insertelement, .data = self.addConstantExtraAssumeCapacity(data), }); - if (self.useLibLlvm()) self.llvm.constants.appendAssumeCapacity( - val.toLlvm(self).constInsertElement(elem.toLlvm(self), index.toLlvm(self)), - ); } return @enumFromInt(gop.index); } @@ -11449,9 +11562,6 @@ fn shuffleVectorConstAssumeCapacity( .tag = .shufflevector, .data = self.addConstantExtraAssumeCapacity(data), }); - if (self.useLibLlvm()) self.llvm.constants.appendAssumeCapacity( - lhs.toLlvm(self).constShuffleVector(rhs.toLlvm(self), mask.toLlvm(self)), - ); } return @enumFromInt(gop.index); } @@ -11506,18 +11616,6 @@ fn binConstAssumeCapacity( .tag = tag, .data = self.addConstantExtraAssumeCapacity(data.extra), }); - if (self.useLibLlvm()) self.llvm.constants.appendAssumeCapacity(switch (tag) { - .add => &llvm.Value.constAdd, - .sub => &llvm.Value.constSub, - .mul => &llvm.Value.constMul, - .shl => &llvm.Value.constShl, - .lshr => &llvm.Value.constLShr, - .ashr => &llvm.Value.constAShr, - .@"and" => &llvm.Value.constAnd, - .@"or" => &llvm.Value.constOr, - .xor => &llvm.Value.constXor, - else => unreachable, - }(lhs.toLlvm(self), rhs.toLlvm(self))); } return @enumFromInt(gop.index); } @@ -11560,21 +11658,6 @@ fn asmConstAssumeCapacity( .tag = data.tag, .data = self.addConstantExtraAssumeCapacity(data.extra), }); - if (self.useLibLlvm()) { - const assembly_slice = assembly.slice(self).?; - const constraints_slice = constraints.slice(self).?; - self.llvm.constants.appendAssumeCapacity(llvm.getInlineAsm( - ty.toLlvm(self), - assembly_slice.ptr, - assembly_slice.len, - constraints_slice.ptr, - constraints_slice.len, - llvm.Bool.fromBool(info.sideeffect), - llvm.Bool.fromBool(info.alignstack), - if (info.inteldialect) .Intel else .ATT, - llvm.Bool.fromBool(info.unwind), - )); - } } return @enumFromInt(gop.index); } @@ -11591,7 +11674,6 @@ fn ensureUnusedConstantCapacity( self.gpa, count * (@typeInfo(Extra).Struct.fields.len + trail_len), ); - if (self.useLibLlvm()) try self.llvm.constants.ensureUnusedCapacity(self.gpa, count); } fn getOrPutConstantNoExtraAssumeCapacity( @@ -11722,15 +11804,3286 @@ fn constantExtraData(self: *const Builder, comptime T: type, index: Constant.Ite return self.constantExtraDataTrail(T, index).data; } +fn ensureUnusedMetadataCapacity( + self: *Builder, + count: usize, + comptime Extra: type, + trail_len: usize, +) Allocator.Error!void { + try self.metadata_map.ensureUnusedCapacity(self.gpa, count); + try self.metadata_items.ensureUnusedCapacity(self.gpa, count); + try self.metadata_extra.ensureUnusedCapacity( + self.gpa, + count * (@typeInfo(Extra).Struct.fields.len + trail_len), + ); +} + +fn addMetadataExtraAssumeCapacity(self: *Builder, extra: anytype) Metadata.Item.ExtraIndex { + const result: Metadata.Item.ExtraIndex = @intCast(self.metadata_extra.items.len); + inline for (@typeInfo(@TypeOf(extra)).Struct.fields) |field| { + const value = @field(extra, field.name); + self.metadata_extra.appendAssumeCapacity(switch (field.type) { + u32 => value, + MetadataString, Metadata, Variable.Index, Value => @intFromEnum(value), + Metadata.DIFlags => @bitCast(value), + else => @compileError("bad field type: " ++ @typeName(field.type)), + }); + } + return result; +} + +const MetadataExtraDataTrail = struct { + index: Metadata.Item.ExtraIndex, + + fn nextMut(self: *MetadataExtraDataTrail, len: u32, comptime Item: type, builder: *Builder) []Item { + const items: []Item = @ptrCast(builder.metadata_extra.items[self.index..][0..len]); + self.index += @intCast(len); + return items; + } + + fn next( + self: *MetadataExtraDataTrail, + len: u32, + comptime Item: type, + builder: *const Builder, + ) []const Item { + const items: []const Item = @ptrCast(builder.metadata_extra.items[self.index..][0..len]); + self.index += @intCast(len); + return items; + } +}; + +fn metadataExtraDataTrail( + self: *const Builder, + comptime T: type, + index: Metadata.Item.ExtraIndex, +) struct { data: T, trail: MetadataExtraDataTrail } { + var result: T = undefined; + const fields = @typeInfo(T).Struct.fields; + inline for (fields, self.metadata_extra.items[index..][0..fields.len]) |field, value| + @field(result, field.name) = switch (field.type) { + u32 => value, + MetadataString, Metadata, Variable.Index, Value => @enumFromInt(value), + Metadata.DIFlags => @bitCast(value), + else => @compileError("bad field type: " ++ @typeName(field.type)), + }; + return .{ + .data = result, + .trail = .{ .index = index + @as(Metadata.Item.ExtraIndex, @intCast(fields.len)) }, + }; +} + +fn metadataExtraData(self: *const Builder, comptime T: type, index: Metadata.Item.ExtraIndex) T { + return self.metadataExtraDataTrail(T, index).data; +} + +pub fn metadataString(self: *Builder, bytes: []const u8) Allocator.Error!MetadataString { + try self.metadata_string_bytes.ensureUnusedCapacity(self.gpa, bytes.len); + try self.metadata_string_indices.ensureUnusedCapacity(self.gpa, 1); + try self.metadata_string_map.ensureUnusedCapacity(self.gpa, 1); + + const gop = self.metadata_string_map.getOrPutAssumeCapacityAdapted( + bytes, + MetadataString.Adapter{ .builder = self }, + ); + if (!gop.found_existing) { + self.metadata_string_bytes.appendSliceAssumeCapacity(bytes); + self.metadata_string_indices.appendAssumeCapacity(@intCast(self.metadata_string_bytes.items.len)); + } + return @enumFromInt(gop.index); +} + +pub fn metadataStringFromString(self: *Builder, str: String) Allocator.Error!MetadataString { + if (str == .none or str == .empty) return MetadataString.none; + return try self.metadataString(str.slice(self).?); +} + +pub fn metadataStringFmt(self: *Builder, comptime fmt_str: []const u8, fmt_args: anytype) Allocator.Error!MetadataString { + try self.metadata_string_map.ensureUnusedCapacity(self.gpa, 1); + try self.metadata_string_bytes.ensureUnusedCapacity(self.gpa, @intCast(std.fmt.count(fmt_str, fmt_args))); + try self.metadata_string_indices.ensureUnusedCapacity(self.gpa, 1); + return self.metadataStringFmtAssumeCapacity(fmt_str, fmt_args); +} + +pub fn metadataStringFmtAssumeCapacity(self: *Builder, comptime fmt_str: []const u8, fmt_args: anytype) MetadataString { + self.metadata_string_bytes.writer(undefined).print(fmt_str, fmt_args) catch unreachable; + return self.trailingMetadataStringAssumeCapacity(); +} + +pub fn trailingMetadataString(self: *Builder) Allocator.Error!MetadataString { + try self.metadata_string_indices.ensureUnusedCapacity(self.gpa, 1); + try self.metadata_string_map.ensureUnusedCapacity(self.gpa, 1); + return self.trailingMetadataStringAssumeCapacity(); +} + +pub fn trailingMetadataStringAssumeCapacity(self: *Builder) MetadataString { + const start = self.metadata_string_indices.getLast(); + const bytes: []const u8 = self.metadata_string_bytes.items[start..]; + const gop = self.metadata_string_map.getOrPutAssumeCapacityAdapted(bytes, String.Adapter{ .builder = self }); + if (gop.found_existing) { + self.metadata_string_bytes.shrinkRetainingCapacity(start); + } else { + self.metadata_string_indices.appendAssumeCapacity(@intCast(self.metadata_string_bytes.items.len)); + } + return @enumFromInt(gop.index); +} + +pub fn debugNamed(self: *Builder, name: MetadataString, operands: []const Metadata) Allocator.Error!void { + try self.metadata_extra.ensureUnusedCapacity(self.gpa, operands.len); + try self.metadata_named.ensureUnusedCapacity(self.gpa, 1); + self.debugNamedAssumeCapacity(name, operands); +} + +fn debugNone(self: *Builder) Allocator.Error!Metadata { + try self.ensureUnusedMetadataCapacity(1, NoExtra, 0); + return self.debugNoneAssumeCapacity(); +} + +pub fn debugFile( + self: *Builder, + filename: MetadataString, + directory: MetadataString, +) Allocator.Error!Metadata { + try self.ensureUnusedMetadataCapacity(1, Metadata.File, 0); + return self.debugFileAssumeCapacity(filename, directory); +} + +pub fn debugCompileUnit( + self: *Builder, + file: Metadata, + producer: MetadataString, + enums: Metadata, + globals: Metadata, + options: Metadata.CompileUnit.Options, +) Allocator.Error!Metadata { + try self.ensureUnusedMetadataCapacity(1, Metadata.CompileUnit, 0); + return self.debugCompileUnitAssumeCapacity(file, producer, enums, globals, options); +} + +pub fn debugSubprogram( + self: *Builder, + file: Metadata, + name: MetadataString, + linkage_name: MetadataString, + line: u32, + scope_line: u32, + ty: Metadata, + options: Metadata.Subprogram.Options, + compile_unit: Metadata, +) Allocator.Error!Metadata { + try self.ensureUnusedMetadataCapacity(1, Metadata.Subprogram, 0); + return self.debugSubprogramAssumeCapacity( + file, + name, + linkage_name, + line, + scope_line, + ty, + options, + compile_unit, + ); +} + +pub fn debugLexicalBlock(self: *Builder, scope: Metadata, file: Metadata, line: u32, column: u32) Allocator.Error!Metadata { + try self.ensureUnusedMetadataCapacity(1, Metadata.LexicalBlock, 0); + return self.debugLexicalBlockAssumeCapacity(scope, file, line, column); +} + +pub fn debugLocation(self: *Builder, line: u32, column: u32, scope: Metadata, inlined_at: Metadata) Allocator.Error!Metadata { + try self.ensureUnusedMetadataCapacity(1, Metadata.Location, 0); + return self.debugLocationAssumeCapacity(line, column, scope, inlined_at); +} + +pub fn debugBoolType(self: *Builder, name: MetadataString, size_in_bits: u64) Allocator.Error!Metadata { + try self.ensureUnusedMetadataCapacity(1, Metadata.BasicType, 0); + return self.debugBoolTypeAssumeCapacity(name, size_in_bits); +} + +pub fn debugUnsignedType(self: *Builder, name: MetadataString, size_in_bits: u64) Allocator.Error!Metadata { + try self.ensureUnusedMetadataCapacity(1, Metadata.BasicType, 0); + return self.debugUnsignedTypeAssumeCapacity(name, size_in_bits); +} + +pub fn debugSignedType(self: *Builder, name: MetadataString, size_in_bits: u64) Allocator.Error!Metadata { + try self.ensureUnusedMetadataCapacity(1, Metadata.BasicType, 0); + return self.debugSignedTypeAssumeCapacity(name, size_in_bits); +} + +pub fn debugFloatType(self: *Builder, name: MetadataString, size_in_bits: u64) Allocator.Error!Metadata { + try self.ensureUnusedMetadataCapacity(1, Metadata.BasicType, 0); + return self.debugFloatTypeAssumeCapacity(name, size_in_bits); +} + +pub fn debugForwardReference(self: *Builder) Allocator.Error!Metadata { + try self.metadata_forward_references.ensureUnusedCapacity(self.gpa, 1); + return self.debugForwardReferenceAssumeCapacity(); +} + +pub fn debugStructType( + self: *Builder, + name: MetadataString, + file: Metadata, + scope: Metadata, + line: u32, + underlying_type: Metadata, + size_in_bits: u64, + align_in_bits: u64, + fields_tuple: Metadata, +) Allocator.Error!Metadata { + try self.ensureUnusedMetadataCapacity(1, Metadata.CompositeType, 0); + return self.debugStructTypeAssumeCapacity( + name, + file, + scope, + line, + underlying_type, + size_in_bits, + align_in_bits, + fields_tuple, + ); +} + +pub fn debugUnionType( + self: *Builder, + name: MetadataString, + file: Metadata, + scope: Metadata, + line: u32, + underlying_type: Metadata, + size_in_bits: u64, + align_in_bits: u64, + fields_tuple: Metadata, +) Allocator.Error!Metadata { + try self.ensureUnusedMetadataCapacity(1, Metadata.CompositeType, 0); + return self.debugUnionTypeAssumeCapacity( + name, + file, + scope, + line, + underlying_type, + size_in_bits, + align_in_bits, + fields_tuple, + ); +} + +pub fn debugEnumerationType( + self: *Builder, + name: MetadataString, + file: Metadata, + scope: Metadata, + line: u32, + underlying_type: Metadata, + size_in_bits: u64, + align_in_bits: u64, + fields_tuple: Metadata, +) Allocator.Error!Metadata { + try self.ensureUnusedMetadataCapacity(1, Metadata.CompositeType, 0); + return self.debugEnumerationTypeAssumeCapacity( + name, + file, + scope, + line, + underlying_type, + size_in_bits, + align_in_bits, + fields_tuple, + ); +} + +pub fn debugArrayType( + self: *Builder, + name: MetadataString, + file: Metadata, + scope: Metadata, + line: u32, + underlying_type: Metadata, + size_in_bits: u64, + align_in_bits: u64, + fields_tuple: Metadata, +) Allocator.Error!Metadata { + try self.ensureUnusedMetadataCapacity(1, Metadata.CompositeType, 0); + return self.debugArrayTypeAssumeCapacity( + name, + file, + scope, + line, + underlying_type, + size_in_bits, + align_in_bits, + fields_tuple, + ); +} + +pub fn debugVectorType( + self: *Builder, + name: MetadataString, + file: Metadata, + scope: Metadata, + line: u32, + underlying_type: Metadata, + size_in_bits: u64, + align_in_bits: u64, + fields_tuple: Metadata, +) Allocator.Error!Metadata { + try self.ensureUnusedMetadataCapacity(1, Metadata.CompositeType, 0); + return self.debugVectorTypeAssumeCapacity( + name, + file, + scope, + line, + underlying_type, + size_in_bits, + align_in_bits, + fields_tuple, + ); +} + +pub fn debugPointerType( + self: *Builder, + name: MetadataString, + file: Metadata, + scope: Metadata, + line: u32, + underlying_type: Metadata, + size_in_bits: u64, + align_in_bits: u64, + offset_in_bits: u64, +) Allocator.Error!Metadata { + try self.ensureUnusedMetadataCapacity(1, Metadata.DerivedType, 0); + return self.debugPointerTypeAssumeCapacity( + name, + file, + scope, + line, + underlying_type, + size_in_bits, + align_in_bits, + offset_in_bits, + ); +} + +pub fn debugMemberType( + self: *Builder, + name: MetadataString, + file: Metadata, + scope: Metadata, + line: u32, + underlying_type: Metadata, + size_in_bits: u64, + align_in_bits: u64, + offset_in_bits: u64, +) Allocator.Error!Metadata { + try self.ensureUnusedMetadataCapacity(1, Metadata.DerivedType, 0); + return self.debugMemberTypeAssumeCapacity( + name, + file, + scope, + line, + underlying_type, + size_in_bits, + align_in_bits, + offset_in_bits, + ); +} + +pub fn debugSubroutineType( + self: *Builder, + types_tuple: Metadata, +) Allocator.Error!Metadata { + try self.ensureUnusedMetadataCapacity(1, Metadata.SubroutineType, 0); + return self.debugSubroutineTypeAssumeCapacity(types_tuple); +} + +pub fn debugEnumerator( + self: *Builder, + name: MetadataString, + unsigned: bool, + bit_width: u32, + value: std.math.big.int.Const, +) Allocator.Error!Metadata { + assert(!(unsigned and !value.positive)); + try self.ensureUnusedMetadataCapacity(1, Metadata.Enumerator, 0); + try self.metadata_limbs.ensureUnusedCapacity(self.gpa, value.limbs.len); + return self.debugEnumeratorAssumeCapacity(name, unsigned, bit_width, value); +} + +pub fn debugSubrange( + self: *Builder, + lower_bound: Metadata, + count: Metadata, +) Allocator.Error!Metadata { + try self.ensureUnusedMetadataCapacity(1, Metadata.Subrange, 0); + return self.debugSubrangeAssumeCapacity(lower_bound, count); +} + +pub fn debugExpression( + self: *Builder, + elements: []const u32, +) Allocator.Error!Metadata { + try self.ensureUnusedMetadataCapacity(1, Metadata.Expression, elements.len * @sizeOf(u32)); + return self.debugExpressionAssumeCapacity(elements); +} + +pub fn debugTuple( + self: *Builder, + elements: []const Metadata, +) Allocator.Error!Metadata { + try self.ensureUnusedMetadataCapacity(1, Metadata.Tuple, elements.len * @sizeOf(Metadata)); + return self.debugTupleAssumeCapacity(elements); +} + +pub fn debugModuleFlag( + self: *Builder, + behavior: Metadata, + name: MetadataString, + constant: Metadata, +) Allocator.Error!Metadata { + try self.ensureUnusedMetadataCapacity(1, Metadata.ModuleFlag, 0); + return self.debugModuleFlagAssumeCapacity(behavior, name, constant); +} + +pub fn debugLocalVar( + self: *Builder, + name: MetadataString, + file: Metadata, + scope: Metadata, + line: u32, + ty: Metadata, +) Allocator.Error!Metadata { + try self.ensureUnusedMetadataCapacity(1, Metadata.LocalVar, 0); + return self.debugLocalVarAssumeCapacity(name, file, scope, line, ty); +} + +pub fn debugParameter( + self: *Builder, + name: MetadataString, + file: Metadata, + scope: Metadata, + line: u32, + ty: Metadata, + arg_no: u32, +) Allocator.Error!Metadata { + try self.ensureUnusedMetadataCapacity(1, Metadata.Parameter, 0); + return self.debugParameterAssumeCapacity(name, file, scope, line, ty, arg_no); +} + +pub fn debugGlobalVar( + self: *Builder, + name: MetadataString, + linkage_name: MetadataString, + file: Metadata, + scope: Metadata, + line: u32, + ty: Metadata, + variable: Variable.Index, + options: Metadata.GlobalVar.Options, +) Allocator.Error!Metadata { + try self.ensureUnusedMetadataCapacity(1, Metadata.GlobalVar, 0); + return self.debugGlobalVarAssumeCapacity( + name, + linkage_name, + file, + scope, + line, + ty, + variable, + options, + ); +} + +pub fn debugGlobalVarExpression( + self: *Builder, + variable: Metadata, + expression: Metadata, +) Allocator.Error!Metadata { + try self.ensureUnusedMetadataCapacity(1, Metadata.GlobalVarExpression, 0); + return self.debugGlobalVarExpressionAssumeCapacity(variable, expression); +} + +pub fn debugConstant(self: *Builder, value: Constant) Allocator.Error!Metadata { + try self.ensureUnusedMetadataCapacity(1, NoExtra, 0); + return self.debugConstantAssumeCapacity(value); +} + +pub fn debugForwardReferenceSetType(self: *Builder, fwd_ref: Metadata, ty: Metadata) void { + assert( + @intFromEnum(fwd_ref) >= Metadata.first_forward_reference and + @intFromEnum(fwd_ref) <= Metadata.first_local_metadata, + ); + const index = @intFromEnum(fwd_ref) - Metadata.first_forward_reference; + self.metadata_forward_references.items[index] = ty; +} + +fn metadataSimpleAssumeCapacity(self: *Builder, tag: Metadata.Tag, value: anytype) Metadata { + const Key = struct { + tag: Metadata.Tag, + value: @TypeOf(value), + }; + const Adapter = struct { + builder: *const Builder, + pub fn hash(_: @This(), key: Key) u32 { + var hasher = std.hash.Wyhash.init(std.hash.uint32(@intFromEnum(key.tag))); + inline for (std.meta.fields(@TypeOf(value))) |field| { + hasher.update(std.mem.asBytes(&@field(key.value, field.name))); + } + return @truncate(hasher.final()); + } + + pub fn eql(ctx: @This(), lhs_key: Key, _: void, rhs_index: usize) bool { + if (lhs_key.tag != ctx.builder.metadata_items.items(.tag)[rhs_index]) return false; + const rhs_data = ctx.builder.metadata_items.items(.data)[rhs_index]; + const rhs_extra = ctx.builder.metadataExtraData(@TypeOf(value), rhs_data); + return std.meta.eql(lhs_key.value, rhs_extra); + } + }; + + const gop = self.metadata_map.getOrPutAssumeCapacityAdapted( + Key{ .tag = tag, .value = value }, + Adapter{ .builder = self }, + ); + + if (!gop.found_existing) { + gop.key_ptr.* = {}; + gop.value_ptr.* = {}; + self.metadata_items.appendAssumeCapacity(.{ + .tag = tag, + .data = self.addMetadataExtraAssumeCapacity(value), + }); + } + return @enumFromInt(gop.index); +} + +fn metadataDistinctAssumeCapacity(self: *Builder, tag: Metadata.Tag, value: anytype) Metadata { + const Key = struct { tag: Metadata.Tag, index: Metadata }; + const Adapter = struct { + pub fn hash(_: @This(), key: Key) u32 { + return @truncate(std.hash.Wyhash.hash( + std.hash.uint32(@intFromEnum(key.tag)), + std.mem.asBytes(&key.index), + )); + } + + pub fn eql(_: @This(), lhs_key: Key, _: void, rhs_index: usize) bool { + return @intFromEnum(lhs_key.index) == rhs_index; + } + }; + + const gop = self.metadata_map.getOrPutAssumeCapacityAdapted( + Key{ .tag = tag, .index = @enumFromInt(self.metadata_map.count()) }, + Adapter{}, + ); + + if (!gop.found_existing) { + gop.key_ptr.* = {}; + gop.value_ptr.* = {}; + self.metadata_items.appendAssumeCapacity(.{ + .tag = tag, + .data = self.addMetadataExtraAssumeCapacity(value), + }); + } + return @enumFromInt(gop.index); +} + +fn debugNamedAssumeCapacity(self: *Builder, name: MetadataString, operands: []const Metadata) void { + assert(!self.strip); + assert(name != .none); + const extra_index: u32 = @intCast(self.metadata_extra.items.len); + self.metadata_extra.appendSliceAssumeCapacity(@ptrCast(operands)); + + const gop = self.metadata_named.getOrPutAssumeCapacity(name); + gop.value_ptr.* = .{ + .index = extra_index, + .len = @intCast(operands.len), + }; +} + +pub fn debugNoneAssumeCapacity(self: *Builder) Metadata { + assert(!self.strip); + return self.metadataSimpleAssumeCapacity(.none, .{}); +} + +fn debugFileAssumeCapacity( + self: *Builder, + filename: MetadataString, + directory: MetadataString, +) Metadata { + assert(!self.strip); + return self.metadataSimpleAssumeCapacity(.file, Metadata.File{ + .filename = filename, + .directory = directory, + }); +} + +pub fn debugCompileUnitAssumeCapacity( + self: *Builder, + file: Metadata, + producer: MetadataString, + enums: Metadata, + globals: Metadata, + options: Metadata.CompileUnit.Options, +) Metadata { + assert(!self.strip); + return self.metadataDistinctAssumeCapacity( + if (options.optimized) .@"compile_unit optimized" else .compile_unit, + Metadata.CompileUnit{ + .file = file, + .producer = producer, + .enums = enums, + .globals = globals, + }, + ); +} + +fn debugSubprogramAssumeCapacity( + self: *Builder, + file: Metadata, + name: MetadataString, + linkage_name: MetadataString, + line: u32, + scope_line: u32, + ty: Metadata, + options: Metadata.Subprogram.Options, + compile_unit: Metadata, +) Metadata { + assert(!self.strip); + const tag: Metadata.Tag = @enumFromInt(@intFromEnum(Metadata.Tag.subprogram) + + @as(u3, @truncate(@as(u32, @bitCast(options.sp_flags)) >> 2))); + return self.metadataDistinctAssumeCapacity(tag, Metadata.Subprogram{ + .file = file, + .name = name, + .linkage_name = linkage_name, + .line = line, + .scope_line = scope_line, + .ty = ty, + .di_flags = options.di_flags, + .compile_unit = compile_unit, + }); +} + +fn debugLexicalBlockAssumeCapacity(self: *Builder, scope: Metadata, file: Metadata, line: u32, column: u32) Metadata { + assert(!self.strip); + return self.metadataSimpleAssumeCapacity(.lexical_block, Metadata.LexicalBlock{ + .scope = scope, + .file = file, + .line = line, + .column = column, + }); +} + +fn debugLocationAssumeCapacity(self: *Builder, line: u32, column: u32, scope: Metadata, inlined_at: Metadata) Metadata { + assert(!self.strip); + return self.metadataSimpleAssumeCapacity(.location, Metadata.Location{ + .line = line, + .column = column, + .scope = scope, + .inlined_at = inlined_at, + }); +} + +fn debugBoolTypeAssumeCapacity(self: *Builder, name: MetadataString, size_in_bits: u64) Metadata { + assert(!self.strip); + return self.metadataSimpleAssumeCapacity(.basic_bool_type, Metadata.BasicType{ + .name = name, + .size_in_bits_lo = @truncate(size_in_bits), + .size_in_bits_hi = @truncate(size_in_bits >> 32), + }); +} + +fn debugUnsignedTypeAssumeCapacity(self: *Builder, name: MetadataString, size_in_bits: u64) Metadata { + assert(!self.strip); + return self.metadataSimpleAssumeCapacity(.basic_unsigned_type, Metadata.BasicType{ + .name = name, + .size_in_bits_lo = @truncate(size_in_bits), + .size_in_bits_hi = @truncate(size_in_bits >> 32), + }); +} + +fn debugSignedTypeAssumeCapacity(self: *Builder, name: MetadataString, size_in_bits: u64) Metadata { + assert(!self.strip); + return self.metadataSimpleAssumeCapacity(.basic_signed_type, Metadata.BasicType{ + .name = name, + .size_in_bits_lo = @truncate(size_in_bits), + .size_in_bits_hi = @truncate(size_in_bits >> 32), + }); +} + +fn debugFloatTypeAssumeCapacity(self: *Builder, name: MetadataString, size_in_bits: u64) Metadata { + assert(!self.strip); + return self.metadataSimpleAssumeCapacity(.basic_float_type, Metadata.BasicType{ + .name = name, + .size_in_bits_lo = @truncate(size_in_bits), + .size_in_bits_hi = @truncate(size_in_bits >> 32), + }); +} + +fn debugForwardReferenceAssumeCapacity(self: *Builder) Metadata { + assert(!self.strip); + const index = Metadata.first_forward_reference + self.metadata_forward_references.items.len; + self.metadata_forward_references.appendAssumeCapacity(.none); + return @enumFromInt(index); +} + +fn debugStructTypeAssumeCapacity( + self: *Builder, + name: MetadataString, + file: Metadata, + scope: Metadata, + line: u32, + underlying_type: Metadata, + size_in_bits: u64, + align_in_bits: u64, + fields_tuple: Metadata, +) Metadata { + assert(!self.strip); + return self.debugCompositeTypeAssumeCapacity( + .composite_struct_type, + name, + file, + scope, + line, + underlying_type, + size_in_bits, + align_in_bits, + fields_tuple, + ); +} + +fn debugUnionTypeAssumeCapacity( + self: *Builder, + name: MetadataString, + file: Metadata, + scope: Metadata, + line: u32, + underlying_type: Metadata, + size_in_bits: u64, + align_in_bits: u64, + fields_tuple: Metadata, +) Metadata { + assert(!self.strip); + return self.debugCompositeTypeAssumeCapacity( + .composite_union_type, + name, + file, + scope, + line, + underlying_type, + size_in_bits, + align_in_bits, + fields_tuple, + ); +} + +fn debugEnumerationTypeAssumeCapacity( + self: *Builder, + name: MetadataString, + file: Metadata, + scope: Metadata, + line: u32, + underlying_type: Metadata, + size_in_bits: u64, + align_in_bits: u64, + fields_tuple: Metadata, +) Metadata { + assert(!self.strip); + return self.debugCompositeTypeAssumeCapacity( + .composite_enumeration_type, + name, + file, + scope, + line, + underlying_type, + size_in_bits, + align_in_bits, + fields_tuple, + ); +} + +fn debugArrayTypeAssumeCapacity( + self: *Builder, + name: MetadataString, + file: Metadata, + scope: Metadata, + line: u32, + underlying_type: Metadata, + size_in_bits: u64, + align_in_bits: u64, + fields_tuple: Metadata, +) Metadata { + assert(!self.strip); + return self.debugCompositeTypeAssumeCapacity( + .composite_array_type, + name, + file, + scope, + line, + underlying_type, + size_in_bits, + align_in_bits, + fields_tuple, + ); +} + +fn debugVectorTypeAssumeCapacity( + self: *Builder, + name: MetadataString, + file: Metadata, + scope: Metadata, + line: u32, + underlying_type: Metadata, + size_in_bits: u64, + align_in_bits: u64, + fields_tuple: Metadata, +) Metadata { + assert(!self.strip); + return self.debugCompositeTypeAssumeCapacity( + .composite_vector_type, + name, + file, + scope, + line, + underlying_type, + size_in_bits, + align_in_bits, + fields_tuple, + ); +} + +fn debugCompositeTypeAssumeCapacity( + self: *Builder, + tag: Metadata.Tag, + name: MetadataString, + file: Metadata, + scope: Metadata, + line: u32, + underlying_type: Metadata, + size_in_bits: u64, + align_in_bits: u64, + fields_tuple: Metadata, +) Metadata { + assert(!self.strip); + return self.metadataSimpleAssumeCapacity(tag, Metadata.CompositeType{ + .name = name, + .file = file, + .scope = scope, + .line = line, + .underlying_type = underlying_type, + .size_in_bits_lo = @truncate(size_in_bits), + .size_in_bits_hi = @truncate(size_in_bits >> 32), + .align_in_bits_lo = @truncate(align_in_bits), + .align_in_bits_hi = @truncate(align_in_bits >> 32), + .fields_tuple = fields_tuple, + }); +} + +fn debugPointerTypeAssumeCapacity( + self: *Builder, + name: MetadataString, + file: Metadata, + scope: Metadata, + line: u32, + underlying_type: Metadata, + size_in_bits: u64, + align_in_bits: u64, + offset_in_bits: u64, +) Metadata { + assert(!self.strip); + return self.metadataSimpleAssumeCapacity(.derived_pointer_type, Metadata.DerivedType{ + .name = name, + .file = file, + .scope = scope, + .line = line, + .underlying_type = underlying_type, + .size_in_bits_lo = @truncate(size_in_bits), + .size_in_bits_hi = @truncate(size_in_bits >> 32), + .align_in_bits_lo = @truncate(align_in_bits), + .align_in_bits_hi = @truncate(align_in_bits >> 32), + .offset_in_bits_lo = @truncate(offset_in_bits), + .offset_in_bits_hi = @truncate(offset_in_bits >> 32), + }); +} + +fn debugMemberTypeAssumeCapacity( + self: *Builder, + name: MetadataString, + file: Metadata, + scope: Metadata, + line: u32, + underlying_type: Metadata, + size_in_bits: u64, + align_in_bits: u64, + offset_in_bits: u64, +) Metadata { + assert(!self.strip); + return self.metadataSimpleAssumeCapacity(.derived_member_type, Metadata.DerivedType{ + .name = name, + .file = file, + .scope = scope, + .line = line, + .underlying_type = underlying_type, + .size_in_bits_lo = @truncate(size_in_bits), + .size_in_bits_hi = @truncate(size_in_bits >> 32), + .align_in_bits_lo = @truncate(align_in_bits), + .align_in_bits_hi = @truncate(align_in_bits >> 32), + .offset_in_bits_lo = @truncate(offset_in_bits), + .offset_in_bits_hi = @truncate(offset_in_bits >> 32), + }); +} + +fn debugSubroutineTypeAssumeCapacity( + self: *Builder, + types_tuple: Metadata, +) Metadata { + assert(!self.strip); + return self.metadataSimpleAssumeCapacity(.subroutine_type, Metadata.SubroutineType{ + .types_tuple = types_tuple, + }); +} + +fn debugEnumeratorAssumeCapacity( + self: *Builder, + name: MetadataString, + unsigned: bool, + bit_width: u32, + value: std.math.big.int.Const, +) Metadata { + assert(!self.strip); + const Key = struct { + tag: Metadata.Tag, + name: MetadataString, + bit_width: u32, + value: std.math.big.int.Const, + }; + const Adapter = struct { + builder: *const Builder, + pub fn hash(_: @This(), key: Key) u32 { + var hasher = std.hash.Wyhash.init(std.hash.uint32(@intFromEnum(key.tag))); + hasher.update(std.mem.asBytes(&key.name)); + hasher.update(std.mem.asBytes(&key.bit_width)); + hasher.update(std.mem.sliceAsBytes(key.value.limbs)); + return @truncate(hasher.final()); + } + + pub fn eql(ctx: @This(), lhs_key: Key, _: void, rhs_index: usize) bool { + if (lhs_key.tag != ctx.builder.metadata_items.items(.tag)[rhs_index]) return false; + const rhs_data = ctx.builder.metadata_items.items(.data)[rhs_index]; + const rhs_extra = ctx.builder.metadataExtraData(Metadata.Enumerator, rhs_data); + const limbs = ctx.builder.metadata_limbs + .items[rhs_extra.limbs_index..][0..rhs_extra.limbs_len]; + const rhs_value = std.math.big.int.Const{ + .limbs = limbs, + .positive = lhs_key.value.positive, + }; + return lhs_key.name == rhs_extra.name and + lhs_key.bit_width == rhs_extra.bit_width and + lhs_key.value.eql(rhs_value); + } + }; + + const tag: Metadata.Tag = if (unsigned) + .enumerator_unsigned + else if (value.positive) + .enumerator_signed_positive + else + .enumerator_signed_negative; + + assert(!(tag == .enumerator_unsigned and !value.positive)); + + const gop = self.metadata_map.getOrPutAssumeCapacityAdapted( + Key{ + .tag = tag, + .name = name, + .bit_width = bit_width, + .value = value, + }, + Adapter{ .builder = self }, + ); + + if (!gop.found_existing) { + gop.key_ptr.* = {}; + gop.value_ptr.* = {}; + self.metadata_items.appendAssumeCapacity(.{ + .tag = tag, + .data = self.addMetadataExtraAssumeCapacity(Metadata.Enumerator{ + .name = name, + .bit_width = bit_width, + .limbs_index = @intCast(self.metadata_limbs.items.len), + .limbs_len = @intCast(value.limbs.len), + }), + }); + self.metadata_limbs.appendSliceAssumeCapacity(value.limbs); + } + return @enumFromInt(gop.index); +} + +fn debugSubrangeAssumeCapacity( + self: *Builder, + lower_bound: Metadata, + count: Metadata, +) Metadata { + assert(!self.strip); + return self.metadataSimpleAssumeCapacity(.subrange, Metadata.Subrange{ + .lower_bound = lower_bound, + .count = count, + }); +} + +fn debugExpressionAssumeCapacity( + self: *Builder, + elements: []const u32, +) Metadata { + assert(!self.strip); + const Key = struct { + elements: []const u32, + }; + 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.expression))); + 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 (Metadata.Tag.expression != 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.Expression, rhs_data); + return std.mem.eql( + u32, + lhs_key.elements, + rhs_extra.trail.next(rhs_extra.data.elements_len, u32, ctx.builder), + ); + } + }; + + const gop = self.metadata_map.getOrPutAssumeCapacityAdapted( + Key{ .elements = elements }, + Adapter{ .builder = self }, + ); + + if (!gop.found_existing) { + gop.key_ptr.* = {}; + gop.value_ptr.* = {}; + self.metadata_items.appendAssumeCapacity(.{ + .tag = .expression, + .data = self.addMetadataExtraAssumeCapacity(Metadata.Expression{ + .elements_len = @intCast(elements.len), + }), + }); + self.metadata_extra.appendSliceAssumeCapacity(@ptrCast(elements)); + } + return @enumFromInt(gop.index); +} + +fn debugTupleAssumeCapacity( + self: *Builder, + elements: []const Metadata, +) Metadata { + assert(!self.strip); + const Key = struct { + 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 (Metadata.Tag.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.Tuple, rhs_data); + return 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{ .elements = elements }, + Adapter{ .builder = self }, + ); + + if (!gop.found_existing) { + gop.key_ptr.* = {}; + gop.value_ptr.* = {}; + self.metadata_items.appendAssumeCapacity(.{ + .tag = .tuple, + .data = self.addMetadataExtraAssumeCapacity(Metadata.Tuple{ + .elements_len = @intCast(elements.len), + }), + }); + self.metadata_extra.appendSliceAssumeCapacity(@ptrCast(elements)); + } + return @enumFromInt(gop.index); +} + +fn debugModuleFlagAssumeCapacity( + self: *Builder, + behavior: Metadata, + name: MetadataString, + constant: Metadata, +) Metadata { + assert(!self.strip); + return self.metadataSimpleAssumeCapacity(.module_flag, Metadata.ModuleFlag{ + .behavior = behavior, + .name = name, + .constant = constant, + }); +} + +fn debugLocalVarAssumeCapacity( + self: *Builder, + name: MetadataString, + file: Metadata, + scope: Metadata, + line: u32, + ty: Metadata, +) Metadata { + assert(!self.strip); + return self.metadataSimpleAssumeCapacity(.local_var, Metadata.LocalVar{ + .name = name, + .file = file, + .scope = scope, + .line = line, + .ty = ty, + }); +} + +fn debugParameterAssumeCapacity( + self: *Builder, + name: MetadataString, + file: Metadata, + scope: Metadata, + line: u32, + ty: Metadata, + arg_no: u32, +) Metadata { + assert(!self.strip); + return self.metadataSimpleAssumeCapacity(.parameter, Metadata.Parameter{ + .name = name, + .file = file, + .scope = scope, + .line = line, + .ty = ty, + .arg_no = arg_no, + }); +} + +fn debugGlobalVarAssumeCapacity( + self: *Builder, + name: MetadataString, + linkage_name: MetadataString, + file: Metadata, + scope: Metadata, + line: u32, + ty: Metadata, + variable: Variable.Index, + options: Metadata.GlobalVar.Options, +) Metadata { + assert(!self.strip); + return self.metadataDistinctAssumeCapacity( + if (options.local) .@"global_var local" else .global_var, + Metadata.GlobalVar{ + .name = name, + .linkage_name = linkage_name, + .file = file, + .scope = scope, + .line = line, + .ty = ty, + .variable = variable, + }, + ); +} + +fn debugGlobalVarExpressionAssumeCapacity( + self: *Builder, + variable: Metadata, + expression: Metadata, +) Metadata { + assert(!self.strip); + return self.metadataSimpleAssumeCapacity(.global_var_expression, Metadata.GlobalVarExpression{ + .variable = variable, + .expression = expression, + }); +} + +fn debugConstantAssumeCapacity(self: *Builder, constant: Constant) Metadata { + assert(!self.strip); + const Adapter = struct { + builder: *const Builder, + pub fn hash(_: @This(), key: Constant) u32 { + var hasher = comptime std.hash.Wyhash.init(std.hash.uint32(@intFromEnum(Metadata.Tag.constant))); + hasher.update(std.mem.asBytes(&key)); + return @truncate(hasher.final()); + } + + pub fn eql(ctx: @This(), lhs_key: Constant, _: void, rhs_index: usize) bool { + if (Metadata.Tag.constant != ctx.builder.metadata_items.items(.tag)[rhs_index]) return false; + const rhs_data: Constant = @enumFromInt(ctx.builder.metadata_items.items(.data)[rhs_index]); + return rhs_data == lhs_key; + } + }; + + const gop = self.metadata_map.getOrPutAssumeCapacityAdapted( + constant, + Adapter{ .builder = self }, + ); + + if (!gop.found_existing) { + gop.key_ptr.* = {}; + gop.value_ptr.* = {}; + self.metadata_items.appendAssumeCapacity(.{ + .tag = .constant, + .data = @intFromEnum(constant), + }); + } + return @enumFromInt(gop.index); +} + +pub fn toBitcode(self: *Builder, allocator: Allocator) bitcode_writer.Error![]const u32 { + const BitcodeWriter = bitcode_writer.BitcodeWriter(&.{ Type, FunctionAttributes }); + var bitcode = BitcodeWriter.init(allocator, .{ + std.math.log2_int_ceil(usize, self.type_items.items.len), + std.math.log2_int_ceil(usize, 1 + self.function_attributes_set.count()), + }); + errdefer bitcode.deinit(); + + // Write LLVM IR magic + try bitcode.writeBits(ir.MAGIC, 32); + + var record: std.ArrayListUnmanaged(u64) = .{}; + defer record.deinit(self.gpa); + + // IDENTIFICATION_BLOCK + { + const Identification = ir.Identification; + var identification_block = try bitcode.enterTopBlock(Identification); + + const producer = try std.fmt.allocPrint(self.gpa, "zig {d}.{d}.{d}", .{ + build_options.semver.major, + build_options.semver.minor, + build_options.semver.patch, + }); + defer self.gpa.free(producer); + + try identification_block.writeAbbrev(Identification.Version{ .string = producer }); + try identification_block.writeAbbrev(Identification.Epoch{ .epoch = 0 }); + + try identification_block.end(); + } + + // MODULE_BLOCK + { + const Module = ir.Module; + var module_block = try bitcode.enterTopBlock(Module); + + try module_block.writeAbbrev(Module.Version{}); + + if (self.target_triple.slice(self)) |triple| { + try module_block.writeAbbrev(Module.String{ + .code = 2, + .string = triple, + }); + } + + if (self.data_layout.slice(self)) |data_layout| { + try module_block.writeAbbrev(Module.String{ + .code = 3, + .string = data_layout, + }); + } + + if (self.source_filename.slice(self)) |source_filename| { + try module_block.writeAbbrev(Module.String{ + .code = 16, + .string = source_filename, + }); + } + + if (self.module_asm.items.len != 0) { + try module_block.writeAbbrev(Module.String{ + .code = 4, + .string = self.module_asm.items, + }); + } + + // TYPE_BLOCK + { + var type_block = try module_block.enterSubBlock(ir.Type, true); + + try type_block.writeAbbrev(ir.Type.NumEntry{ .num = @intCast(self.type_items.items.len) }); + + for (self.type_items.items, 0..) |item, i| { + const ty: Type = @enumFromInt(i); + + switch (item.tag) { + .simple => try type_block.writeAbbrev(ir.Type.Simple{ .code = @truncate(item.data) }), + .integer => try type_block.writeAbbrev(ir.Type.Integer{ .width = item.data }), + .structure, + .packed_structure, + => |kind| { + const is_packed = switch (kind) { + .structure => false, + .packed_structure => true, + else => unreachable, + }; + var extra = self.typeExtraDataTrail(Type.Structure, item.data); + try type_block.writeAbbrev(ir.Type.StructAnon{ + .is_packed = is_packed, + .types = extra.trail.next(extra.data.fields_len, Type, self), + }); + }, + .named_structure => { + const extra = self.typeExtraData(Type.NamedStructure, item.data); + try type_block.writeAbbrev(ir.Type.StructName{ + .string = extra.id.slice(self).?, + }); + + switch (extra.body) { + .none => try type_block.writeAbbrev(ir.Type.Opaque{}), + else => { + const real_struct = self.type_items.items[@intFromEnum(extra.body)]; + const is_packed: bool = switch (real_struct.tag) { + .structure => false, + .packed_structure => true, + else => unreachable, + }; + + var real_extra = self.typeExtraDataTrail(Type.Structure, real_struct.data); + try type_block.writeAbbrev(ir.Type.StructNamed{ + .is_packed = is_packed, + .types = real_extra.trail.next(real_extra.data.fields_len, Type, self), + }); + }, + } + }, + .array, + .small_array, + => try type_block.writeAbbrev(ir.Type.Array{ + .len = ty.aggregateLen(self), + .child = ty.childType(self), + }), + .vector, + .scalable_vector, + => try type_block.writeAbbrev(ir.Type.Vector{ + .len = ty.aggregateLen(self), + .child = ty.childType(self), + }), + .pointer => try type_block.writeAbbrev(ir.Type.Pointer{ + .addr_space = ty.pointerAddrSpace(self), + }), + .target => { + var extra = self.typeExtraDataTrail(Type.Target, item.data); + try type_block.writeAbbrev(ir.Type.StructName{ + .string = extra.data.name.slice(self).?, + }); + + const types = extra.trail.next(extra.data.types_len, Type, self); + const ints = extra.trail.next(extra.data.ints_len, u32, self); + + try type_block.writeAbbrev(ir.Type.Target{ + .num_types = extra.data.types_len, + .types = types, + .ints = ints, + }); + }, + .function, .vararg_function => |kind| { + const is_vararg = switch (kind) { + .function => false, + .vararg_function => true, + else => unreachable, + }; + var extra = self.typeExtraDataTrail(Type.Function, item.data); + try type_block.writeAbbrev(ir.Type.Function{ + .is_vararg = is_vararg, + .return_type = extra.data.ret, + .param_types = extra.trail.next(extra.data.params_len, Type, self), + }); + }, + } + } + + try type_block.end(); + } + + var attributes_set: std.AutoArrayHashMapUnmanaged(struct { + attributes: Attributes, + index: u32, + }, void) = .{}; + defer attributes_set.deinit(self.gpa); + + // PARAMATTR_GROUP_BLOCK + { + const ParamattrGroup = ir.ParamattrGroup; + + var paramattr_group_block = try module_block.enterSubBlock(ParamattrGroup, true); + + for (self.function_attributes_set.keys()) |func_attributes| { + for (func_attributes.slice(self), 0..) |attributes, i| { + const attributes_slice = attributes.slice(self); + if (attributes_slice.len == 0) continue; + + const attr_gop = try attributes_set.getOrPut(self.gpa, .{ + .attributes = attributes, + .index = @intCast(i), + }); + + if (attr_gop.found_existing) continue; + + record.clearRetainingCapacity(); + try record.ensureUnusedCapacity(self.gpa, 2); + + record.appendAssumeCapacity(attr_gop.index); + record.appendAssumeCapacity(switch (i) { + 0 => 0xffffffff, + else => i - 1, + }); + + for (attributes_slice) |attr_index| { + const kind = attr_index.getKind(self); + switch (attr_index.toAttribute(self)) { + .zeroext, + .signext, + .inreg, + .@"noalias", + .nocapture, + .nofree, + .nest, + .returned, + .nonnull, + .swiftself, + .swiftasync, + .swifterror, + .immarg, + .noundef, + .allocalign, + .allocptr, + .readnone, + .readonly, + .writeonly, + .alwaysinline, + .builtin, + .cold, + .convergent, + .disable_sanitizer_information, + .fn_ret_thunk_extern, + .hot, + .inlinehint, + .jumptable, + .minsize, + .naked, + .nobuiltin, + .nocallback, + .noduplicate, + .noimplicitfloat, + .@"noinline", + .nomerge, + .nonlazybind, + .noprofile, + .skipprofile, + .noredzone, + .noreturn, + .norecurse, + .willreturn, + .nosync, + .nounwind, + .nosanitize_bounds, + .nosanitize_coverage, + .null_pointer_is_valid, + .optforfuzzing, + .optnone, + .optsize, + .returns_twice, + .safestack, + .sanitize_address, + .sanitize_memory, + .sanitize_thread, + .sanitize_hwaddress, + .sanitize_memtag, + .speculative_load_hardening, + .speculatable, + .ssp, + .sspstrong, + .sspreq, + .strictfp, + .nocf_check, + .shadowcallstack, + .mustprogress, + .no_sanitize_address, + .no_sanitize_hwaddress, + .sanitize_address_dyninit, + => { + try record.ensureUnusedCapacity(self.gpa, 2); + record.appendAssumeCapacity(0); + record.appendAssumeCapacity(@intFromEnum(kind)); + }, + .byval, + .byref, + .preallocated, + .inalloca, + .sret, + .elementtype, + => |ty| { + try record.ensureUnusedCapacity(self.gpa, 3); + record.appendAssumeCapacity(6); + record.appendAssumeCapacity(@intFromEnum(kind)); + record.appendAssumeCapacity(@intFromEnum(ty)); + }, + .@"align", + .alignstack, + => |alignment| { + try record.ensureUnusedCapacity(self.gpa, 3); + record.appendAssumeCapacity(1); + record.appendAssumeCapacity(@intFromEnum(kind)); + record.appendAssumeCapacity(alignment.toByteUnits() orelse 0); + }, + .dereferenceable, + .dereferenceable_or_null, + => |size| { + try record.ensureUnusedCapacity(self.gpa, 3); + record.appendAssumeCapacity(1); + record.appendAssumeCapacity(@intFromEnum(kind)); + record.appendAssumeCapacity(size); + }, + .nofpclass => |fpclass| { + try record.ensureUnusedCapacity(self.gpa, 3); + record.appendAssumeCapacity(1); + record.appendAssumeCapacity(@intFromEnum(kind)); + record.appendAssumeCapacity(@as(u32, @bitCast(fpclass))); + }, + .allockind => |allockind| { + try record.ensureUnusedCapacity(self.gpa, 3); + record.appendAssumeCapacity(1); + record.appendAssumeCapacity(@intFromEnum(kind)); + record.appendAssumeCapacity(@as(u32, @bitCast(allockind))); + }, + + .allocsize => |allocsize| { + try record.ensureUnusedCapacity(self.gpa, 3); + record.appendAssumeCapacity(1); + record.appendAssumeCapacity(@intFromEnum(kind)); + record.appendAssumeCapacity(@bitCast(allocsize.toLlvm())); + }, + .memory => |memory| { + try record.ensureUnusedCapacity(self.gpa, 3); + record.appendAssumeCapacity(1); + record.appendAssumeCapacity(@intFromEnum(kind)); + record.appendAssumeCapacity(@as(u32, @bitCast(memory))); + }, + .uwtable => |uwtable| if (uwtable != .none) { + try record.ensureUnusedCapacity(self.gpa, 3); + record.appendAssumeCapacity(1); + record.appendAssumeCapacity(@intFromEnum(kind)); + record.appendAssumeCapacity(@intFromEnum(uwtable)); + }, + .vscale_range => |vscale_range| { + try record.ensureUnusedCapacity(self.gpa, 3); + record.appendAssumeCapacity(1); + record.appendAssumeCapacity(@intFromEnum(kind)); + record.appendAssumeCapacity(@bitCast(vscale_range.toLlvm())); + }, + .string => |string_attr| { + const string_attr_kind_slice = string_attr.kind.slice(self).?; + const string_attr_value_slice = if (string_attr.value != .none) + string_attr.value.slice(self).? + else + null; + + try record.ensureUnusedCapacity( + self.gpa, + 2 + string_attr_kind_slice.len + if (string_attr_value_slice) |slice| slice.len + 1 else 0, + ); + record.appendAssumeCapacity(if (string_attr.value == .none) 3 else 4); + for (string_attr.kind.slice(self).?) |c| { + record.appendAssumeCapacity(c); + } + record.appendAssumeCapacity(0); + if (string_attr_value_slice) |slice| { + for (slice) |c| { + record.appendAssumeCapacity(c); + } + record.appendAssumeCapacity(0); + } + }, + .none => unreachable, + } + } + + try paramattr_group_block.writeUnabbrev(3, record.items); + } + } + + try paramattr_group_block.end(); + } + + // PARAMATTR_BLOCK + { + const Paramattr = ir.Paramattr; + var paramattr_block = try module_block.enterSubBlock(Paramattr, true); + + for (self.function_attributes_set.keys()) |func_attributes| { + const func_attributes_slice = func_attributes.slice(self); + record.clearRetainingCapacity(); + try record.ensureUnusedCapacity(self.gpa, func_attributes_slice.len); + for (func_attributes_slice, 0..) |attributes, i| { + const attributes_slice = attributes.slice(self); + if (attributes_slice.len == 0) continue; + + const group_index = attributes_set.getIndex(.{ + .attributes = attributes, + .index = @intCast(i), + }).?; + record.appendAssumeCapacity(@intCast(group_index)); + } + + try paramattr_block.writeAbbrev(Paramattr.Entry{ .group_indices = record.items }); + } + + try paramattr_block.end(); + } + + var globals: std.AutoArrayHashMapUnmanaged(Global.Index, void) = .{}; + defer globals.deinit(self.gpa); + try globals.ensureUnusedCapacity( + self.gpa, + self.variables.items.len + + self.functions.items.len + + self.aliases.items.len, + ); + + for (self.variables.items) |variable| { + if (variable.global.getReplacement(self) != .none) continue; + + globals.putAssumeCapacity(variable.global, {}); + } + + for (self.functions.items) |function| { + if (function.global.getReplacement(self) != .none) continue; + + globals.putAssumeCapacity(function.global, {}); + } + + for (self.aliases.items) |alias| { + if (alias.global.getReplacement(self) != .none) continue; + + globals.putAssumeCapacity(alias.global, {}); + } + + const ConstantAdapter = struct { + const ConstantAdapter = @This(); + builder: *const Builder, + globals: *const std.AutoArrayHashMapUnmanaged(Global.Index, void), + + pub fn get(adapter: @This(), param: anytype, comptime field_name: []const u8) @TypeOf(param) { + _ = field_name; + return switch (@TypeOf(param)) { + Constant => @enumFromInt(adapter.getConstantIndex(param)), + else => param, + }; + } + + pub fn getConstantIndex(adapter: ConstantAdapter, constant: Constant) u32 { + return switch (constant.unwrap()) { + .constant => |c| c + adapter.numGlobals(), + .global => |global| @intCast(adapter.globals.getIndex(global.unwrap(adapter.builder)).?), + }; + } + + pub fn numConstants(adapter: ConstantAdapter) u32 { + return @intCast(adapter.globals.count() + adapter.builder.constant_items.len); + } + + pub fn numGlobals(adapter: ConstantAdapter) u32 { + return @intCast(adapter.globals.count()); + } + }; + + const constant_adapter = ConstantAdapter{ + .builder = self, + .globals = &globals, + }; + + // Globals + { + var section_map: std.AutoArrayHashMapUnmanaged(String, void) = .{}; + defer section_map.deinit(self.gpa); + try section_map.ensureUnusedCapacity(self.gpa, globals.count()); + + for (self.variables.items) |variable| { + if (variable.global.getReplacement(self) != .none) continue; + + const section = blk: { + if (variable.section == .none) break :blk 0; + const gop = section_map.getOrPutAssumeCapacity(variable.section); + if (!gop.found_existing) { + try module_block.writeAbbrev(Module.String{ + .code = 5, + .string = variable.section.slice(self).?, + }); + } + break :blk gop.index + 1; + }; + + const initid = if (variable.init == .no_init) + 0 + else + (constant_adapter.getConstantIndex(variable.init) + 1); + + const strtab = variable.global.strtab(self); + + const global = variable.global.ptrConst(self); + try module_block.writeAbbrev(Module.Variable{ + .strtab_offset = strtab.offset, + .strtab_size = strtab.size, + .type_index = global.type, + .is_const = .{ + .is_const = switch (variable.mutability) { + .global => false, + .constant => true, + }, + .addr_space = global.addr_space, + }, + .initid = initid, + .linkage = global.linkage, + .alignment = variable.alignment.toLlvm(), + .section = section, + .visibility = global.visibility, + .thread_local = variable.thread_local, + .unnamed_addr = global.unnamed_addr, + .externally_initialized = global.externally_initialized, + .dllstorageclass = global.dll_storage_class, + .preemption = global.preemption, + }); + } + + for (self.functions.items) |func| { + if (func.global.getReplacement(self) != .none) continue; + + const section = blk: { + if (func.section == .none) break :blk 0; + const gop = section_map.getOrPutAssumeCapacity(func.section); + if (!gop.found_existing) { + try module_block.writeAbbrev(Module.String{ + .code = 5, + .string = func.section.slice(self).?, + }); + } + break :blk gop.index + 1; + }; + + const paramattr_index = if (self.function_attributes_set.getIndex(func.attributes)) |index| + index + 1 + else + 0; + + const strtab = func.global.strtab(self); + + const global = func.global.ptrConst(self); + try module_block.writeAbbrev(Module.Function{ + .strtab_offset = strtab.offset, + .strtab_size = strtab.size, + .type_index = global.type, + .call_conv = func.call_conv, + .is_proto = func.instructions.len == 0, + .linkage = global.linkage, + .paramattr = paramattr_index, + .alignment = func.alignment.toLlvm(), + .section = section, + .visibility = global.visibility, + .unnamed_addr = global.unnamed_addr, + .dllstorageclass = global.dll_storage_class, + .preemption = global.preemption, + .addr_space = global.addr_space, + }); + } + + for (self.aliases.items) |alias| { + if (alias.global.getReplacement(self) != .none) continue; + + const strtab = alias.global.strtab(self); + + const global = alias.global.ptrConst(self); + try module_block.writeAbbrev(Module.Alias{ + .strtab_offset = strtab.offset, + .strtab_size = strtab.size, + .type_index = global.type, + .addr_space = global.addr_space, + .aliasee = constant_adapter.getConstantIndex(alias.aliasee), + .linkage = global.linkage, + .visibility = global.visibility, + .thread_local = alias.thread_local, + .unnamed_addr = global.unnamed_addr, + .dllstorageclass = global.dll_storage_class, + .preemption = global.preemption, + }); + } + } + + // CONSTANTS_BLOCK + { + const Constants = ir.Constants; + var constants_block = try module_block.enterSubBlock(Constants, true); + + var current_type: Type = .none; + const tags = self.constant_items.items(.tag); + const datas = self.constant_items.items(.data); + for (0..self.constant_items.len) |index| { + record.clearRetainingCapacity(); + const constant: Constant = @enumFromInt(index); + const constant_type = constant.typeOf(self); + if (constant_type != current_type) { + try constants_block.writeAbbrev(Constants.SetType{ .type_id = constant_type }); + current_type = constant_type; + } + const data = datas[index]; + switch (tags[index]) { + .null, + .zeroinitializer, + .none, + => try constants_block.writeAbbrev(Constants.Null{}), + .undef => try constants_block.writeAbbrev(Constants.Undef{}), + .poison => try constants_block.writeAbbrev(Constants.Poison{}), + .positive_integer, + .negative_integer, + => |tag| { + const extra: *align(@alignOf(std.math.big.Limb)) Constant.Integer = + @ptrCast(self.constant_limbs.items[data..][0..Constant.Integer.limbs]); + const limbs = self.constant_limbs + .items[data + Constant.Integer.limbs ..][0..extra.limbs_len]; + const bigint: std.math.big.int.Const = .{ + .limbs = limbs, + .positive = tag == .positive_integer, + }; + + const bit_count = extra.type.scalarBits(self); + if (bit_count <= 64) { + const val = bigint.to(i64) catch unreachable; + const emit_val = if (tag == .positive_integer) + @shlWithOverflow(val, 1)[0] + else + (@shlWithOverflow(@addWithOverflow(~val, 1)[0], 1)[0] | 1); + try constants_block.writeAbbrev(Constants.Integer{ .value = @bitCast(emit_val) }); + } else { + const word_count = std.mem.alignForward(u24, bit_count, 64) / 64; + try record.ensureUnusedCapacity(self.gpa, word_count); + const buffer: [*]u8 = @ptrCast(record.items.ptr); + bigint.writeTwosComplement(buffer[0..(word_count * 8)], .little); + + const signed_buffer: [*]i64 = @ptrCast(record.items.ptr); + for (signed_buffer[0..word_count], 0..) |val, i| { + signed_buffer[i] = if (val >= 0) + @shlWithOverflow(val, 1)[0] + else + (@shlWithOverflow(@addWithOverflow(~val, 1)[0], 1)[0] | 1); + } + + try constants_block.writeUnabbrev(5, record.items.ptr[0..word_count]); + } + }, + .half, + .bfloat, + => try constants_block.writeAbbrev(Constants.Half{ .value = @truncate(data) }), + .float => try constants_block.writeAbbrev(Constants.Float{ .value = data }), + .double => { + const extra = self.constantExtraData(Constant.Double, data); + try constants_block.writeAbbrev(Constants.Double{ + .value = (@as(u64, extra.hi) << 32) | extra.lo, + }); + }, + .x86_fp80 => { + const extra = self.constantExtraData(Constant.Fp80, data); + try constants_block.writeAbbrev(Constants.Fp80{ + .hi = @as(u64, extra.hi) << 48 | @as(u64, extra.lo_hi) << 16 | + extra.lo_lo >> 16, + .lo = @truncate(extra.lo_lo), + }); + }, + .fp128, + .ppc_fp128, + => { + const extra = self.constantExtraData(Constant.Fp128, data); + try constants_block.writeAbbrev(Constants.Fp128{ + .lo = @as(u64, extra.lo_hi) << 32 | @as(u64, extra.lo_lo), + .hi = @as(u64, extra.hi_hi) << 32 | @as(u64, extra.hi_lo), + }); + }, + .array, + .vector, + .structure, + .packed_structure, + => { + var extra = self.constantExtraDataTrail(Constant.Aggregate, data); + const len: u32 = @intCast(extra.data.type.aggregateLen(self)); + const values = extra.trail.next(len, Constant, self); + + try constants_block.writeAbbrevAdapted( + Constants.Aggregate{ .values = values }, + constant_adapter, + ); + }, + .splat => { + const ConstantsWriter = @TypeOf(constants_block); + const extra = self.constantExtraData(Constant.Splat, data); + const vector_len = extra.type.vectorLen(self); + const c = constant_adapter.getConstantIndex(extra.value); + + try bitcode.writeBits( + ConstantsWriter.abbrevId(Constants.Aggregate), + ConstantsWriter.abbrev_len, + ); + try bitcode.writeVBR(vector_len, 6); + for (0..vector_len) |_| { + try bitcode.writeBits(c, Constants.Aggregate.ops[1].array_fixed); + } + }, + .string => { + const str: String = @enumFromInt(data); + if (str == .none) { + try constants_block.writeAbbrev(Constants.Null{}); + } else { + const slice = str.slice(self).?; + if (slice.len > 0 and slice[slice.len - 1] == 0) + try constants_block.writeAbbrev(Constants.CString{ .string = slice[0 .. slice.len - 1] }) + else + try constants_block.writeAbbrev(Constants.String{ .string = slice }); + } + }, + .bitcast, + .inttoptr, + .ptrtoint, + .fptosi, + .fptoui, + .sitofp, + .uitofp, + .addrspacecast, + .fptrunc, + .trunc, + .fpext, + .sext, + .zext, + => |tag| { + const extra = self.constantExtraData(Constant.Cast, data); + try constants_block.writeAbbrevAdapted(Constants.Cast{ + .type_index = extra.type, + .val = extra.val, + .opcode = tag.toCastOpcode(), + }, constant_adapter); + }, + .add, + .@"add nsw", + .@"add nuw", + .sub, + .@"sub nsw", + .@"sub nuw", + .mul, + .@"mul nsw", + .@"mul nuw", + .shl, + .lshr, + .ashr, + .@"and", + .@"or", + .xor, + => |tag| { + const extra = self.constantExtraData(Constant.Binary, data); + try constants_block.writeAbbrevAdapted(Constants.Binary{ + .opcode = tag.toBinaryOpcode(), + .lhs = extra.lhs, + .rhs = extra.rhs, + }, constant_adapter); + }, + .icmp, + .fcmp, + => { + const extra = self.constantExtraData(Constant.Compare, data); + try constants_block.writeAbbrevAdapted(Constants.Cmp{ + .ty = extra.lhs.typeOf(self), + .lhs = extra.lhs, + .rhs = extra.rhs, + .pred = extra.cond, + }, constant_adapter); + }, + .extractelement => { + const extra = self.constantExtraData(Constant.ExtractElement, data); + try constants_block.writeAbbrevAdapted(Constants.ExtractElement{ + .val_type = extra.val.typeOf(self), + .val = extra.val, + .index_type = extra.index.typeOf(self), + .index = extra.index, + }, constant_adapter); + }, + .insertelement => { + const extra = self.constantExtraData(Constant.InsertElement, data); + try constants_block.writeAbbrevAdapted(Constants.InsertElement{ + .val = extra.val, + .elem = extra.elem, + .index_type = extra.index.typeOf(self), + .index = extra.index, + }, constant_adapter); + }, + .shufflevector => { + const extra = self.constantExtraData(Constant.ShuffleVector, data); + const ty = constant.typeOf(self); + const lhs_type = extra.lhs.typeOf(self); + // Check if instruction is widening, truncating or not + if (ty == lhs_type) { + try constants_block.writeAbbrevAdapted(Constants.ShuffleVector{ + .lhs = extra.lhs, + .rhs = extra.rhs, + .mask = extra.mask, + }, constant_adapter); + } else { + try constants_block.writeAbbrevAdapted(Constants.ShuffleVectorEx{ + .ty = ty, + .lhs = extra.lhs, + .rhs = extra.rhs, + .mask = extra.mask, + }, constant_adapter); + } + }, + .getelementptr, + .@"getelementptr inbounds", + => |tag| { + var extra = self.constantExtraDataTrail(Constant.GetElementPtr, data); + const indices = extra.trail.next(extra.data.info.indices_len, Constant, self); + try record.ensureUnusedCapacity(self.gpa, 1 + 2 + 2 * indices.len); + + record.appendAssumeCapacity(@intFromEnum(extra.data.type)); + + record.appendAssumeCapacity(@intFromEnum(extra.data.base.typeOf(self))); + record.appendAssumeCapacity(constant_adapter.getConstantIndex(extra.data.base)); + + for (indices) |i| { + record.appendAssumeCapacity(@intFromEnum(i.typeOf(self))); + record.appendAssumeCapacity(constant_adapter.getConstantIndex(i)); + } + + try constants_block.writeUnabbrev(switch (tag) { + .getelementptr => 12, + .@"getelementptr inbounds" => 20, + else => unreachable, + }, record.items); + }, + .@"asm", + .@"asm sideeffect", + .@"asm alignstack", + .@"asm sideeffect alignstack", + .@"asm inteldialect", + .@"asm sideeffect inteldialect", + .@"asm alignstack inteldialect", + .@"asm sideeffect alignstack inteldialect", + .@"asm unwind", + .@"asm sideeffect unwind", + .@"asm alignstack unwind", + .@"asm sideeffect alignstack unwind", + .@"asm inteldialect unwind", + .@"asm sideeffect inteldialect unwind", + .@"asm alignstack inteldialect unwind", + .@"asm sideeffect alignstack inteldialect unwind", + => |tag| { + const extra = self.constantExtraData(Constant.Assembly, data); + + const assembly_slice = extra.assembly.slice(self).?; + const constraints_slice = extra.constraints.slice(self).?; + + try record.ensureUnusedCapacity(self.gpa, 4 + assembly_slice.len + constraints_slice.len); + + record.appendAssumeCapacity(@intFromEnum(extra.type)); + record.appendAssumeCapacity(switch (tag) { + .@"asm" => 0, + .@"asm sideeffect" => 0b0001, + .@"asm sideeffect alignstack" => 0b0011, + .@"asm sideeffect inteldialect" => 0b0101, + .@"asm sideeffect alignstack inteldialect" => 0b0111, + .@"asm sideeffect unwind" => 0b1001, + .@"asm sideeffect alignstack unwind" => 0b1011, + .@"asm sideeffect inteldialect unwind" => 0b1101, + .@"asm sideeffect alignstack inteldialect unwind" => 0b1111, + .@"asm alignstack" => 0b0010, + .@"asm inteldialect" => 0b0100, + .@"asm alignstack inteldialect" => 0b0110, + .@"asm unwind" => 0b1000, + .@"asm alignstack unwind" => 0b1010, + .@"asm inteldialect unwind" => 0b1100, + .@"asm alignstack inteldialect unwind" => 0b1110, + else => unreachable, + }); + + record.appendAssumeCapacity(assembly_slice.len); + for (assembly_slice) |c| record.appendAssumeCapacity(c); + + record.appendAssumeCapacity(constraints_slice.len); + for (constraints_slice) |c| record.appendAssumeCapacity(c); + + try constants_block.writeUnabbrev(30, record.items); + }, + .blockaddress => { + const extra = self.constantExtraData(Constant.BlockAddress, data); + try constants_block.writeAbbrev(Constants.BlockAddress{ + .type_id = extra.function.typeOf(self), + .function = constant_adapter.getConstantIndex(extra.function.toConst(self)), + .block = @intFromEnum(extra.block), + }); + }, + .dso_local_equivalent, + .no_cfi, + => |tag| { + const function: Function.Index = @enumFromInt(data); + try constants_block.writeAbbrev(Constants.DsoLocalEquivalentOrNoCfi{ + .code = switch (tag) { + .dso_local_equivalent => 27, + .no_cfi => 29, + else => unreachable, + }, + .type_id = function.typeOf(self), + .function = constant_adapter.getConstantIndex(function.toConst(self)), + }); + }, + } + } + + try constants_block.end(); + } + + // 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, + }); + } + + try metadata_kind_block.end(); + } + + const MetadataAdapter = struct { + builder: *const Builder, + constant_adapter: ConstantAdapter, + + pub fn init( + builder: *const Builder, + const_adapter: ConstantAdapter, + ) @This() { + return .{ + .builder = builder, + .constant_adapter = const_adapter, + }; + } + + pub fn get(adapter: @This(), value: anytype, comptime field_name: []const u8) @TypeOf(value) { + _ = field_name; + const Ty = @TypeOf(value); + return switch (Ty) { + Metadata => @enumFromInt(adapter.getMetadataIndex(value)), + MetadataString => @enumFromInt(adapter.getMetadataStringIndex(value)), + Constant => @enumFromInt(adapter.constant_adapter.getConstantIndex(value)), + else => value, + }; + } + + pub fn getMetadataIndex(adapter: @This(), metadata: Metadata) u32 { + if (metadata == .none) return 0; + return @intCast(adapter.builder.metadata_string_map.count() + + @intFromEnum(metadata.unwrap(adapter.builder)) - 1); + } + + pub fn getMetadataStringIndex(_: @This(), metadata_string: MetadataString) u32 { + return @intFromEnum(metadata_string); + } + }; + + 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 + { + const strings_offset, const strings_size = blk: { + var strings_offset: u32 = 0; + var strings_size: u32 = 0; + for (1..self.metadata_string_map.count()) |metadata_string_index| { + const metadata_string: MetadataString = @enumFromInt(metadata_string_index); + const slice = metadata_string.slice(self); + strings_offset += bitcode.bitsVBR(@as(u32, @intCast(slice.len)), 6); + strings_size += @intCast(slice.len * 8); + } + break :blk .{ + std.mem.alignForward(u32, strings_offset, 32) / 8, + std.mem.alignForward(u32, strings_size, 32) / 8, + }; + }; + + try bitcode.writeBits( + comptime MetadataBlockWriter.abbrevId(MetadataBlock.Strings), + MetadataBlockWriter.abbrev_len, + ); + + try bitcode.writeVBR(@as(u32, @intCast(self.metadata_string_map.count() - 1)), 6); + try bitcode.writeVBR(strings_offset, 6); + + try bitcode.writeVBR(strings_size + strings_offset, 6); + + try bitcode.alignTo32(); + + for (1..self.metadata_string_map.count()) |metadata_string_index| { + const metadata_string: MetadataString = @enumFromInt(metadata_string_index); + const slice = metadata_string.slice(self); + try bitcode.writeVBR(@as(u32, @intCast(slice.len)), 6); + } + + try bitcode.alignTo32(); + + for (1..self.metadata_string_map.count()) |metadata_string_index| { + const metadata_string: MetadataString = @enumFromInt(metadata_string_index); + const slice = metadata_string.slice(self); + for (slice) |c| { + try bitcode.writeBits(c, 8); + } + } + + try bitcode.alignTo32(); + } + + for ( + self.metadata_items.items(.tag)[1..], + self.metadata_items.items(.data)[1..], + ) |tag, data| { + switch (tag) { + .none => unreachable, + .file => { + const extra = self.metadataExtraData(Metadata.File, data); + + try metadata_block.writeAbbrevAdapted(MetadataBlock.File{ + .filename = extra.filename, + .directory = extra.directory, + }, metadata_adapter); + }, + .compile_unit, + .@"compile_unit optimized", + => |kind| { + const extra = self.metadataExtraData(Metadata.CompileUnit, data); + try metadata_block.writeAbbrevAdapted(MetadataBlock.CompileUnit{ + .file = extra.file, + .producer = extra.producer, + .is_optimized = switch (kind) { + .compile_unit => false, + .@"compile_unit optimized" => true, + else => unreachable, + }, + .enums = extra.enums, + .globals = extra.globals, + }, metadata_adapter); + }, + .subprogram, + .@"subprogram local", + .@"subprogram definition", + .@"subprogram local definition", + .@"subprogram optimized", + .@"subprogram optimized local", + .@"subprogram optimized definition", + .@"subprogram optimized local definition", + => |kind| { + const extra = self.metadataExtraData(Metadata.Subprogram, data); + + try metadata_block.writeAbbrevAdapted(MetadataBlock.Subprogram{ + .scope = extra.file, + .name = extra.name, + .linkage_name = extra.linkage_name, + .file = extra.file, + .line = extra.line, + .ty = extra.ty, + .scope_line = extra.scope_line, + .sp_flags = @bitCast(@as(u32, @as(u3, @intCast( + @intFromEnum(kind) - @intFromEnum(Metadata.Tag.subprogram), + ))) << 2), + .flags = extra.di_flags, + .compile_unit = extra.compile_unit, + }, metadata_adapter); + }, + .lexical_block => { + const extra = self.metadataExtraData(Metadata.LexicalBlock, data); + try metadata_block.writeAbbrevAdapted(MetadataBlock.LexicalBlock{ + .scope = extra.scope, + .file = extra.file, + .line = extra.line, + .column = extra.column, + }, metadata_adapter); + }, + .location => { + const extra = self.metadataExtraData(Metadata.Location, data); + assert(extra.scope != .none); + try metadata_block.writeAbbrev(MetadataBlock.Location{ + .line = extra.line, + .column = extra.column, + .scope = metadata_adapter.getMetadataIndex(extra.scope) - 1, + .inlined_at = @enumFromInt(metadata_adapter.getMetadataIndex(extra.inlined_at)), + }); + }, + .basic_bool_type, + .basic_unsigned_type, + .basic_signed_type, + .basic_float_type, + => |kind| { + const extra = self.metadataExtraData(Metadata.BasicType, data); + try metadata_block.writeAbbrevAdapted(MetadataBlock.BasicType{ + .name = extra.name, + .size_in_bits = extra.bitSize(), + .encoding = switch (kind) { + .basic_bool_type => DW.ATE.boolean, + .basic_unsigned_type => DW.ATE.unsigned, + .basic_signed_type => DW.ATE.signed, + .basic_float_type => DW.ATE.float, + else => unreachable, + }, + }, metadata_adapter); + }, + .composite_struct_type, + .composite_union_type, + .composite_enumeration_type, + .composite_array_type, + .composite_vector_type, + => |kind| { + const extra = self.metadataExtraData(Metadata.CompositeType, data); + + try metadata_block.writeAbbrevAdapted(MetadataBlock.CompositeType{ + .tag = switch (kind) { + .composite_struct_type => DW.TAG.structure_type, + .composite_union_type => DW.TAG.union_type, + .composite_enumeration_type => DW.TAG.enumeration_type, + .composite_array_type, .composite_vector_type => DW.TAG.array_type, + else => unreachable, + }, + .name = extra.name, + .file = extra.file, + .line = extra.line, + .scope = extra.scope, + .underlying_type = extra.underlying_type, + .size_in_bits = extra.bitSize(), + .align_in_bits = extra.bitAlign(), + .flags = if (kind == .composite_vector_type) .{ .Vector = true } else .{}, + .elements = extra.fields_tuple, + }, metadata_adapter); + }, + .derived_pointer_type, + .derived_member_type, + => |kind| { + const extra = self.metadataExtraData(Metadata.DerivedType, data); + try metadata_block.writeAbbrevAdapted(MetadataBlock.DerivedType{ + .tag = switch (kind) { + .derived_pointer_type => DW.TAG.pointer_type, + .derived_member_type => DW.TAG.member, + else => unreachable, + }, + .name = extra.name, + .file = extra.file, + .line = extra.line, + .scope = extra.scope, + .underlying_type = extra.underlying_type, + .size_in_bits = extra.bitSize(), + .align_in_bits = extra.bitAlign(), + .offset_in_bits = extra.bitOffset(), + }, metadata_adapter); + }, + .subroutine_type => { + const extra = self.metadataExtraData(Metadata.SubroutineType, data); + + try metadata_block.writeAbbrevAdapted(MetadataBlock.SubroutineType{ + .types = extra.types_tuple, + }, metadata_adapter); + }, + .enumerator_unsigned, + .enumerator_signed_positive, + .enumerator_signed_negative, + => |kind| { + const positive = switch (kind) { + .enumerator_unsigned, + .enumerator_signed_positive, + => true, + .enumerator_signed_negative => false, + else => unreachable, + }; + + const unsigned = switch (kind) { + .enumerator_unsigned => true, + .enumerator_signed_positive, + .enumerator_signed_negative, + => false, + else => unreachable, + }; + + const extra = self.metadataExtraData(Metadata.Enumerator, data); + + const limbs = self.metadata_limbs.items[extra.limbs_index..][0..extra.limbs_len]; + + const bigint: std.math.big.int.Const = .{ + .limbs = limbs, + .positive = positive, + }; + + if (extra.bit_width <= 64) { + const val = bigint.to(i64) catch unreachable; + const emit_val = if (positive) + @shlWithOverflow(val, 1)[0] + else + (@shlWithOverflow(@addWithOverflow(~val, 1)[0], 1)[0] | 1); + try metadata_block.writeAbbrevAdapted(MetadataBlock.Enumerator{ + .flags = .{ + .unsigned = unsigned, + .bigint = false, + }, + .bit_width = extra.bit_width, + .name = extra.name, + .value = @bitCast(emit_val), + }, metadata_adapter); + } else { + const word_count = std.mem.alignForward(u32, extra.bit_width, 64) / 64; + try record.ensureUnusedCapacity(self.gpa, 3 + word_count); + + const flags: MetadataBlock.Enumerator.Flags = .{ + .unsigned = unsigned, + .bigint = true, + }; + + const FlagsInt = @typeInfo(MetadataBlock.Enumerator.Flags).Struct.backing_integer.?; + + const flags_int: FlagsInt = @bitCast(flags); + + record.appendAssumeCapacity(@intCast(flags_int)); + record.appendAssumeCapacity(@intCast(extra.bit_width)); + record.appendAssumeCapacity(metadata_adapter.getMetadataStringIndex(extra.name)); + + const buffer: [*]u8 = @ptrCast(record.items.ptr); + bigint.writeTwosComplement(buffer[0..(word_count * 8)], .little); + + const signed_buffer: [*]i64 = @ptrCast(record.items.ptr); + for (signed_buffer[0..word_count], 0..) |val, i| { + signed_buffer[i] = if (val >= 0) + @shlWithOverflow(val, 1)[0] + else + (@shlWithOverflow(@addWithOverflow(~val, 1)[0], 1)[0] | 1); + } + + try metadata_block.writeUnabbrev( + MetadataBlock.Enumerator.id, + record.items.ptr[0..(3 + word_count)], + ); + } + }, + .subrange => { + const extra = self.metadataExtraData(Metadata.Subrange, data); + + try metadata_block.writeAbbrevAdapted(MetadataBlock.Subrange{ + .count = extra.count, + .lower_bound = extra.lower_bound, + }, metadata_adapter); + }, + .expression => { + var extra = self.metadataExtraDataTrail(Metadata.Expression, data); + + const elements = extra.trail.next(extra.data.elements_len, u32, self); + + try metadata_block.writeAbbrevAdapted(MetadataBlock.Expression{ + .elements = elements, + }, metadata_adapter); + }, + .tuple => { + var extra = self.metadataExtraDataTrail(Metadata.Tuple, data); + + const elements = extra.trail.next(extra.data.elements_len, Metadata, self); + + try metadata_block.writeAbbrevAdapted(MetadataBlock.Node{ + .elements = elements, + }, metadata_adapter); + }, + .module_flag => { + const extra = self.metadataExtraData(Metadata.ModuleFlag, data); + try metadata_block.writeAbbrev(MetadataBlock.Node{ + .elements = &.{ + @enumFromInt(metadata_adapter.getMetadataIndex(extra.behavior)), + @enumFromInt(metadata_adapter.getMetadataStringIndex(extra.name)), + @enumFromInt(metadata_adapter.getMetadataIndex(extra.constant)), + }, + }); + }, + .local_var => { + const extra = self.metadataExtraData(Metadata.LocalVar, data); + try metadata_block.writeAbbrevAdapted(MetadataBlock.LocalVar{ + .scope = extra.scope, + .name = extra.name, + .file = extra.file, + .line = extra.line, + .ty = extra.ty, + }, metadata_adapter); + }, + .parameter => { + const extra = self.metadataExtraData(Metadata.Parameter, data); + try metadata_block.writeAbbrevAdapted(MetadataBlock.Parameter{ + .scope = extra.scope, + .name = extra.name, + .file = extra.file, + .line = extra.line, + .ty = extra.ty, + .arg = extra.arg_no, + }, metadata_adapter); + }, + .global_var, + .@"global_var local", + => |kind| { + const extra = self.metadataExtraData(Metadata.GlobalVar, data); + try metadata_block.writeAbbrevAdapted(MetadataBlock.GlobalVar{ + .scope = extra.scope, + .name = extra.name, + .linkage_name = extra.linkage_name, + .file = extra.file, + .line = extra.line, + .ty = extra.ty, + .local = kind == .@"global_var local", + }, metadata_adapter); + }, + .global_var_expression => { + const extra = self.metadataExtraData(Metadata.GlobalVarExpression, data); + try metadata_block.writeAbbrevAdapted(MetadataBlock.GlobalVarExpression{ + .variable = extra.variable, + .expression = extra.expression, + }, metadata_adapter); + }, + .constant => { + const constant: Constant = @enumFromInt(data); + try metadata_block.writeAbbrevAdapted(MetadataBlock.Constant{ + .ty = constant.typeOf(self), + .constant = constant, + }, metadata_adapter); + }, + } + record.clearRetainingCapacity(); + } + + // Write named metadata + for (self.metadata_named.keys(), self.metadata_named.values()) |name, operands| { + const slice = name.slice(self); + try metadata_block.writeAbbrev(MetadataBlock.Name{ + .name = slice, + }); + + const elements = self.metadata_extra.items[operands.index..][0..operands.len]; + for (elements) |*e| { + e.* = metadata_adapter.getMetadataIndex(@enumFromInt(e.*)) - 1; + } + + try metadata_block.writeAbbrev(MetadataBlock.NamedNode{ + .elements = @ptrCast(elements), + }); + } + + // Write global attached metadata + { + for (globals.keys()) |global| { + const global_ptr = global.ptrConst(self); + if (global_ptr.dbg == .none) continue; + + switch (global_ptr.kind) { + .function => |f| if (f.ptrConst(self).instructions.len != 0) continue, + else => {}, + } + + try metadata_block.writeAbbrev(MetadataBlock.GlobalDeclAttachment{ + .value = @enumFromInt(constant_adapter.getConstantIndex(global.toConst())), + .kind = ir.MetadataKind.dbg, + .metadata = @enumFromInt(metadata_adapter.getMetadataIndex(global_ptr.dbg) - 1), + }); + } + } + + try metadata_block.end(); + } + + // Block info + { + const BlockInfo = ir.BlockInfo; + var block_info_block = try module_block.enterSubBlock(BlockInfo, true); + + try block_info_block.writeUnabbrev(BlockInfo.set_block_id, &.{ir.FunctionBlock.id}); + inline for (ir.FunctionBlock.abbrevs) |abbrev| { + try block_info_block.defineAbbrev(&abbrev.ops); + } + + try block_info_block.writeUnabbrev(BlockInfo.set_block_id, &.{ir.FunctionValueSymbolTable.id}); + inline for (ir.FunctionValueSymbolTable.abbrevs) |abbrev| { + try block_info_block.defineAbbrev(&abbrev.ops); + } + + try block_info_block.writeUnabbrev(BlockInfo.set_block_id, &.{ir.FunctionMetadataBlock.id}); + inline for (ir.FunctionMetadataBlock.abbrevs) |abbrev| { + try block_info_block.defineAbbrev(&abbrev.ops); + } + + try block_info_block.writeUnabbrev(BlockInfo.set_block_id, &.{ir.MetadataAttachmentBlock.id}); + inline for (ir.MetadataAttachmentBlock.abbrevs) |abbrev| { + try block_info_block.defineAbbrev(&abbrev.ops); + } + + try block_info_block.end(); + } + + // FUNCTION_BLOCKS + { + const FunctionAdapter = struct { + 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, + }; + } + + pub fn get(adapter: @This(), value: anytype, comptime field_name: []const u8) @TypeOf(value) { + _ = field_name; + const Ty = @TypeOf(value); + return switch (Ty) { + Value => @enumFromInt(adapter.getOffsetValueIndex(value)), + Constant => @enumFromInt(adapter.getOffsetConstantIndex(value)), + FunctionAttributes => @enumFromInt(switch (value) { + .none => 0, + else => 1 + adapter.constant_adapter.builder.function_attributes_set.getIndex(value).?, + }), + else => value, + }; + } + + pub fn getValueIndex(adapter: @This(), value: Value) u32 { + return @intCast(switch (value.unwrap()) { + .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; + + return @intCast(@intFromEnum(metadata) - + Metadata.first_local_metadata + + adapter.metadata_adapter.builder.metadata_string_map.count() - 1 + + adapter.metadata_adapter.builder.metadata_map.count() - 1); + }, + }); + } + + pub fn getOffsetValueIndex(adapter: @This(), value: Value) u32 { + return @subWithOverflow(adapter.offset(), adapter.getValueIndex(value))[0]; + } + + pub fn getOffsetValueSignedIndex(adapter: @This(), value: Value) i32 { + const signed_offset: i32 = @intCast(adapter.offset()); + const signed_value: i32 = @intCast(adapter.getValueIndex(value)); + return signed_offset - signed_value; + } + + pub fn getOffsetConstantIndex(adapter: @This(), constant: Constant) u32 { + return adapter.offset() - adapter.constant_adapter.getConstantIndex(constant); + } + + pub fn offset(adapter: @This()) u32 { + return @as( + Function.Instruction.Index, + @enumFromInt(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| { + const FunctionBlock = ir.FunctionBlock; + if (func.global.getReplacement(self) != .none) continue; + + if (func.instructions.len == 0) continue; + + var function_block = try module_block.enterSubBlock(FunctionBlock, false); + + try function_block.writeAbbrev(FunctionBlock.DeclareBlocks{ .num_blocks = func.blocks.len }); + + var adapter = FunctionAdapter.init(constant_adapter, metadata_adapter, &func); + + // Emit function level metadata block + if (!func.strip and func.debug_values.len > 0) { + const MetadataBlock = ir.FunctionMetadataBlock; + var metadata_block = try function_block.enterSubBlock(MetadataBlock, false); + + for (func.debug_values) |value| { + try metadata_block.writeAbbrev(MetadataBlock.Value{ + .ty = value.typeOf(@enumFromInt(func_index), self), + .value = @enumFromInt(adapter.getValueIndex(value.toValue())), + }); + } + + try metadata_block.end(); + } + + const tags = func.instructions.items(.tag); + const datas = func.instructions.items(.data); + + var has_location = false; + + var block_incoming_len: u32 = undefined; + for (0..func.instructions.len) |instr_index| { + const tag = tags[instr_index]; + + record.clearRetainingCapacity(); + + switch (tag) { + .block => block_incoming_len = datas[instr_index], + .arg => {}, + .@"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]); + + const call_conv = extra.data.info.call_conv; + const args = extra.trail.next(extra.data.args_len, Value, &func); + try function_block.writeAbbrevAdapted(FunctionBlock.Call{ + .attributes = extra.data.attributes, + .call_type = switch (kind) { + .call => .{ .call_conv = call_conv }, + .@"tail call" => .{ .tail = true, .call_conv = call_conv }, + .@"musttail call" => .{ .must_tail = true, .call_conv = call_conv }, + .@"notail call" => .{ .no_tail = true, .call_conv = call_conv }, + else => unreachable, + }, + .type_id = extra.data.ty, + .callee = extra.data.callee, + .args = args, + }, adapter); + }, + .@"call fast", + .@"musttail call fast", + .@"notail call fast", + .@"tail call fast", + => |kind| { + var extra = func.extraDataTrail(Function.Instruction.Call, datas[instr_index]); + + const call_conv = extra.data.info.call_conv; + const args = extra.trail.next(extra.data.args_len, Value, &func); + try function_block.writeAbbrevAdapted(FunctionBlock.CallFast{ + .attributes = extra.data.attributes, + .call_type = switch (kind) { + .call => .{ .call_conv = call_conv }, + .@"tail call" => .{ .tail = true, .call_conv = call_conv }, + .@"musttail call" => .{ .must_tail = true, .call_conv = call_conv }, + .@"notail call" => .{ .no_tail = true, .call_conv = call_conv }, + else => unreachable, + }, + .fast_math = .{}, + .type_id = extra.data.ty, + .callee = extra.data.callee, + .args = args, + }, adapter); + }, + .add, + .@"add nsw", + .@"add nuw", + .@"add nuw nsw", + .@"and", + .fadd, + .fdiv, + .fmul, + .mul, + .@"mul nsw", + .@"mul nuw", + .@"mul nuw nsw", + .frem, + .fsub, + .sdiv, + .@"sdiv exact", + .sub, + .@"sub nsw", + .@"sub nuw", + .@"sub nuw nsw", + .udiv, + .@"udiv exact", + .xor, + .shl, + .@"shl nsw", + .@"shl nuw", + .@"shl nuw nsw", + .lshr, + .@"lshr exact", + .@"or", + .urem, + .srem, + .ashr, + .@"ashr exact", + => |kind| { + const extra = func.extraData(Function.Instruction.Binary, datas[instr_index]); + try function_block.writeAbbrev(FunctionBlock.Binary{ + .opcode = kind.toBinaryOpcode(), + .lhs = adapter.getOffsetValueIndex(extra.lhs), + .rhs = adapter.getOffsetValueIndex(extra.rhs), + }); + }, + .@"fadd fast", + .@"fdiv fast", + .@"fmul fast", + .@"frem fast", + .@"fsub fast", + => |kind| { + const extra = func.extraData(Function.Instruction.Binary, datas[instr_index]); + try function_block.writeAbbrev(FunctionBlock.BinaryFast{ + .opcode = kind.toBinaryOpcode(), + .lhs = adapter.getOffsetValueIndex(extra.lhs), + .rhs = adapter.getOffsetValueIndex(extra.rhs), + .fast_math = .{}, + }); + }, + .alloca, + .@"alloca inalloca", + => |kind| { + const extra = func.extraData(Function.Instruction.Alloca, datas[instr_index]); + const alignment = extra.info.alignment.toLlvm(); + try function_block.writeAbbrev(FunctionBlock.Alloca{ + .inst_type = extra.type, + .len_type = extra.len.typeOf(@enumFromInt(func_index), self), + .len_value = adapter.getValueIndex(extra.len), + .flags = .{ + .align_lower = @truncate(alignment), + .inalloca = kind == .@"alloca inalloca", + .explicit_type = true, + .swift_error = false, + .align_upper = @truncate(alignment << 5), + }, + }); + }, + .bitcast, + .inttoptr, + .ptrtoint, + .fptosi, + .fptoui, + .sitofp, + .uitofp, + .addrspacecast, + .fptrunc, + .trunc, + .fpext, + .sext, + .zext, + => |kind| { + const extra = func.extraData(Function.Instruction.Cast, datas[instr_index]); + try function_block.writeAbbrev(FunctionBlock.Cast{ + .val = adapter.getOffsetValueIndex(extra.val), + .type_index = extra.type, + .opcode = kind.toCastOpcode(), + }); + }, + .@"fcmp false", + .@"fcmp oeq", + .@"fcmp oge", + .@"fcmp ogt", + .@"fcmp ole", + .@"fcmp olt", + .@"fcmp one", + .@"fcmp ord", + .@"fcmp true", + .@"fcmp ueq", + .@"fcmp uge", + .@"fcmp ugt", + .@"fcmp ule", + .@"fcmp ult", + .@"fcmp une", + .@"fcmp uno", + .@"icmp eq", + .@"icmp ne", + .@"icmp sge", + .@"icmp sgt", + .@"icmp sle", + .@"icmp slt", + .@"icmp uge", + .@"icmp ugt", + .@"icmp ule", + .@"icmp ult", + => |kind| { + const extra = func.extraData(Function.Instruction.Binary, datas[instr_index]); + try function_block.writeAbbrev(FunctionBlock.Cmp{ + .lhs = adapter.getOffsetValueIndex(extra.lhs), + .rhs = adapter.getOffsetValueIndex(extra.rhs), + .pred = kind.toCmpPredicate(), + }); + }, + .@"fcmp fast false", + .@"fcmp fast oeq", + .@"fcmp fast oge", + .@"fcmp fast ogt", + .@"fcmp fast ole", + .@"fcmp fast olt", + .@"fcmp fast one", + .@"fcmp fast ord", + .@"fcmp fast true", + .@"fcmp fast ueq", + .@"fcmp fast uge", + .@"fcmp fast ugt", + .@"fcmp fast ule", + .@"fcmp fast ult", + .@"fcmp fast une", + .@"fcmp fast uno", + => |kind| { + const extra = func.extraData(Function.Instruction.Binary, datas[instr_index]); + try function_block.writeAbbrev(FunctionBlock.CmpFast{ + .lhs = adapter.getOffsetValueIndex(extra.lhs), + .rhs = adapter.getOffsetValueIndex(extra.rhs), + .pred = kind.toCmpPredicate(), + .fast_math = .{}, + }); + }, + .fneg => try function_block.writeAbbrev(FunctionBlock.FNeg{ + .val = adapter.getOffsetValueIndex(@enumFromInt(datas[instr_index])), + }), + .@"fneg fast" => try function_block.writeAbbrev(FunctionBlock.FNegFast{ + .val = adapter.getOffsetValueIndex(@enumFromInt(datas[instr_index])), + .fast_math = .{}, + }), + .extractvalue => { + var extra = func.extraDataTrail(Function.Instruction.ExtractValue, datas[instr_index]); + const indices = extra.trail.next(extra.data.indices_len, u32, &func); + try function_block.writeAbbrev(FunctionBlock.ExtractValue{ + .val = adapter.getOffsetValueIndex(extra.data.val), + .indices = indices, + }); + }, + .insertvalue => { + var extra = func.extraDataTrail(Function.Instruction.InsertValue, datas[instr_index]); + const indices = extra.trail.next(extra.data.indices_len, u32, &func); + try function_block.writeAbbrev(FunctionBlock.InsertValue{ + .val = adapter.getOffsetValueIndex(extra.data.val), + .elem = adapter.getOffsetValueIndex(extra.data.elem), + .indices = indices, + }); + }, + .extractelement => { + const extra = func.extraData(Function.Instruction.ExtractElement, datas[instr_index]); + 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]); + try function_block.writeAbbrev(FunctionBlock.InsertElement{ + .val = adapter.getOffsetValueIndex(extra.val), + .elem = adapter.getOffsetValueIndex(extra.elem), + .index = adapter.getOffsetValueIndex(extra.index), + }); + }, + .select => { + const extra = func.extraData(Function.Instruction.Select, datas[instr_index]); + try function_block.writeAbbrev(FunctionBlock.Select{ + .lhs = adapter.getOffsetValueIndex(extra.lhs), + .rhs = adapter.getOffsetValueIndex(extra.rhs), + .cond = adapter.getOffsetValueIndex(extra.cond), + }); + }, + .@"select fast" => { + const extra = func.extraData(Function.Instruction.Select, datas[instr_index]); + try function_block.writeAbbrev(FunctionBlock.SelectFast{ + .lhs = adapter.getOffsetValueIndex(extra.lhs), + .rhs = adapter.getOffsetValueIndex(extra.rhs), + .cond = adapter.getOffsetValueIndex(extra.cond), + .fast_math = .{}, + }); + }, + .shufflevector => { + const extra = func.extraData(Function.Instruction.ShuffleVector, datas[instr_index]); + try function_block.writeAbbrev(FunctionBlock.ShuffleVector{ + .lhs = adapter.getOffsetValueIndex(extra.lhs), + .rhs = adapter.getOffsetValueIndex(extra.rhs), + .mask = adapter.getOffsetValueIndex(extra.mask), + }); + }, + .getelementptr, + .@"getelementptr inbounds", + => |kind| { + var extra = func.extraDataTrail(Function.Instruction.GetElementPtr, datas[instr_index]); + const indices = extra.trail.next(extra.data.indices_len, Value, &func); + try function_block.writeAbbrevAdapted( + FunctionBlock.GetElementPtr{ + .is_inbounds = kind == .@"getelementptr inbounds", + .type_index = extra.data.type, + .base = extra.data.base, + .indices = indices, + }, + adapter, + ); + }, + .load => { + const extra = func.extraData(Function.Instruction.Load, datas[instr_index]); + try function_block.writeAbbrev(FunctionBlock.Load{ + .ptr = adapter.getOffsetValueIndex(extra.ptr), + .ty = extra.type, + .alignment = extra.info.alignment.toLlvm(), + .is_volatile = extra.info.access_kind == .@"volatile", + }); + }, + .@"load atomic" => { + const extra = func.extraData(Function.Instruction.Load, datas[instr_index]); + try function_block.writeAbbrev(FunctionBlock.LoadAtomic{ + .ptr = adapter.getOffsetValueIndex(extra.ptr), + .ty = extra.type, + .alignment = extra.info.alignment.toLlvm(), + .is_volatile = extra.info.access_kind == .@"volatile", + .success_ordering = extra.info.success_ordering, + .sync_scope = extra.info.sync_scope, + }); + }, + .store => { + const extra = func.extraData(Function.Instruction.Store, datas[instr_index]); + try function_block.writeAbbrev(FunctionBlock.Store{ + .ptr = adapter.getOffsetValueIndex(extra.ptr), + .val = adapter.getOffsetValueIndex(extra.val), + .alignment = extra.info.alignment.toLlvm(), + .is_volatile = extra.info.access_kind == .@"volatile", + }); + }, + .@"store atomic" => { + const extra = func.extraData(Function.Instruction.Store, datas[instr_index]); + try function_block.writeAbbrev(FunctionBlock.StoreAtomic{ + .ptr = adapter.getOffsetValueIndex(extra.ptr), + .val = adapter.getOffsetValueIndex(extra.val), + .alignment = extra.info.alignment.toLlvm(), + .is_volatile = extra.info.access_kind == .@"volatile", + .success_ordering = extra.info.success_ordering, + .sync_scope = extra.info.sync_scope, + }); + }, + .br => { + try function_block.writeAbbrev(FunctionBlock.BrUnconditional{ + .block = datas[instr_index], + }); + }, + .br_cond => { + const extra = func.extraData(Function.Instruction.BrCond, datas[instr_index]); + try function_block.writeAbbrev(FunctionBlock.BrConditional{ + .then_block = @intFromEnum(extra.then), + .else_block = @intFromEnum(extra.@"else"), + .condition = adapter.getOffsetValueIndex(extra.cond), + }); + }, + .@"switch" => { + var extra = func.extraDataTrail(Function.Instruction.Switch, datas[instr_index]); + + try record.ensureUnusedCapacity(self.gpa, 3 + extra.data.cases_len * 2); + + // Conditional type + record.appendAssumeCapacity(@intFromEnum(extra.data.val.typeOf(@enumFromInt(func_index), self))); + + // Conditional + record.appendAssumeCapacity(adapter.getOffsetValueIndex(extra.data.val)); + + // Default block + record.appendAssumeCapacity(@intFromEnum(extra.data.default)); + + const vals = extra.trail.next(extra.data.cases_len, Constant, &func); + const blocks = extra.trail.next(extra.data.cases_len, Function.Block.Index, &func); + for (vals, blocks) |val, block| { + record.appendAssumeCapacity(adapter.constant_adapter.getConstantIndex(val)); + record.appendAssumeCapacity(@intFromEnum(block)); + } + + try function_block.writeUnabbrev(12, record.items); + }, + .va_arg => { + const extra = func.extraData(Function.Instruction.VaArg, datas[instr_index]); + try function_block.writeAbbrev(FunctionBlock.VaArg{ + .list_type = extra.list.typeOf(@enumFromInt(func_index), self), + .list = adapter.getOffsetValueIndex(extra.list), + .type = extra.type, + }); + }, + .phi, + .@"phi fast", + => |kind| { + var extra = func.extraDataTrail(Function.Instruction.Phi, datas[instr_index]); + const vals = extra.trail.next(block_incoming_len, Value, &func); + const blocks = extra.trail.next(block_incoming_len, Function.Block.Index, &func); + + try record.ensureUnusedCapacity( + self.gpa, + 1 + block_incoming_len * 2 + @intFromBool(kind == .@"phi fast"), + ); + + record.appendAssumeCapacity(@intFromEnum(extra.data.type)); + + for (vals, blocks) |val, block| { + const offset_value = adapter.getOffsetValueSignedIndex(val); + const abs_value: u32 = @intCast(@abs(offset_value)); + const signed_vbr = if (offset_value > 0) abs_value << 1 else ((abs_value << 1) | 1); + record.appendAssumeCapacity(signed_vbr); + record.appendAssumeCapacity(@intFromEnum(block)); + } + + if (kind == .@"phi fast") record.appendAssumeCapacity(@as(u8, @bitCast(FastMath{}))); + + try function_block.writeUnabbrev(16, record.items); + }, + .ret => try function_block.writeAbbrev(FunctionBlock.Ret{ + .val = adapter.getOffsetValueIndex(@enumFromInt(datas[instr_index])), + }), + .@"ret void" => try function_block.writeAbbrev(FunctionBlock.RetVoid{}), + .atomicrmw => { + const extra = func.extraData(Function.Instruction.AtomicRmw, datas[instr_index]); + try function_block.writeAbbrev(FunctionBlock.AtomicRmw{ + .ptr = adapter.getOffsetValueIndex(extra.ptr), + .val = adapter.getOffsetValueIndex(extra.val), + .operation = extra.info.atomic_rmw_operation, + .is_volatile = extra.info.access_kind == .@"volatile", + .success_ordering = extra.info.success_ordering, + .sync_scope = extra.info.sync_scope, + .alignment = extra.info.alignment.toLlvm(), + }); + }, + .cmpxchg, + .@"cmpxchg weak", + => |kind| { + const extra = func.extraData(Function.Instruction.CmpXchg, datas[instr_index]); + + try function_block.writeAbbrev(FunctionBlock.CmpXchg{ + .ptr = adapter.getOffsetValueIndex(extra.ptr), + .cmp = adapter.getOffsetValueIndex(extra.cmp), + .new = adapter.getOffsetValueIndex(extra.new), + .is_volatile = extra.info.access_kind == .@"volatile", + .success_ordering = extra.info.success_ordering, + .sync_scope = extra.info.sync_scope, + .failure_ordering = extra.info.failure_ordering, + .is_weak = kind == .@"cmpxchg weak", + .alignment = extra.info.alignment.toLlvm(), + }); + }, + .fence => { + const info: MemoryAccessInfo = @bitCast(datas[instr_index]); + try function_block.writeAbbrev(FunctionBlock.Fence{ + .ordering = info.success_ordering, + .sync_scope = info.sync_scope, + }); + }, + } + + if (!func.strip) { + if (func.debug_locations.get(@enumFromInt(instr_index))) |debug_location| { + switch (debug_location) { + .no_location => has_location = false, + .location => |location| { + try function_block.writeAbbrev(FunctionBlock.DebugLoc{ + .line = location.line, + .column = location.column, + .scope = @enumFromInt(metadata_adapter.getMetadataIndex(location.scope)), + .inlined_at = @enumFromInt(metadata_adapter.getMetadataIndex(location.inlined_at)), + }); + has_location = true; + }, + } + } else if (has_location) { + try function_block.writeAbbrev(FunctionBlock.DebugLocAgain{}); + } + } + + adapter.next(); + } + + // VALUE_SYMTAB + if (!func.strip) { + const ValueSymbolTable = ir.FunctionValueSymbolTable; + + var value_symtab_block = try function_block.enterSubBlock(ValueSymbolTable, false); + + for (func.blocks, 0..) |block, block_index| { + const name = block.instruction.name(&func); + + if (name == .none or name == .empty) continue; + + try value_symtab_block.writeAbbrev(ValueSymbolTable.BlockEntry{ + .value_id = @intCast(block_index), + .string = name.slice(self).?, + }); + } + + // TODO: Emit non block entries if the builder ever starts assigning names to non blocks + + try value_symtab_block.end(); + } + + // 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), + }); + + try metadata_attach_block.end(); + } + + try function_block.end(); + } + } + + try module_block.end(); + } + + // STRTAB_BLOCK + { + const Strtab = ir.Strtab; + var strtab_block = try bitcode.enterTopBlock(Strtab); + + try strtab_block.writeAbbrev(Strtab.Blob{ .blob = self.string_bytes.items }); + + try strtab_block.end(); + } + + return bitcode.toOwnedSlice(); +} + +const Allocator = std.mem.Allocator; const assert = std.debug.assert; +const bitcode_writer = @import("bitcode_writer.zig"); const build_options = @import("build_options"); +const Builder = @This(); const builtin = @import("builtin"); -const llvm = if (build_options.have_llvm) - @import("bindings.zig") -else - @compileError("LLVM unavailable"); +const DW = std.dwarf; +const ir = @import("ir.zig"); const log = std.log.scoped(.llvm); const std = @import("std"); - -const Allocator = std.mem.Allocator; -const Builder = @This(); diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig index d7144b36ce..affadef882 100644 --- a/src/codegen/llvm/bindings.zig +++ b/src/codegen/llvm/bindings.zig @@ -15,7 +15,14 @@ pub const Bool = enum(c_int) { return b != .False; } }; -pub const AttributeIndex = c_uint; + +pub const MemoryBuffer = opaque { + pub const createMemoryBufferWithMemoryRange = LLVMCreateMemoryBufferWithMemoryRange; + pub const dispose = LLVMDisposeMemoryBuffer; + + extern fn LLVMCreateMemoryBufferWithMemoryRange(InputData: [*]const u8, InputDataLength: usize, BufferName: ?[*:0]const u8, RequiresNullTerminator: Bool) *MemoryBuffer; + extern fn LLVMDisposeMemoryBuffer(MemBuf: *MemoryBuffer) void; +}; /// Make sure to use the *InContext functions instead of the global ones. pub const Context = opaque { @@ -25,382 +32,17 @@ pub const Context = opaque { pub const dispose = LLVMContextDispose; extern fn LLVMContextDispose(C: *Context) void; - pub const createEnumAttribute = LLVMCreateEnumAttribute; - extern fn LLVMCreateEnumAttribute(C: *Context, KindID: c_uint, Val: u64) *Attribute; - - pub const createTypeAttribute = LLVMCreateTypeAttribute; - extern fn LLVMCreateTypeAttribute(C: *Context, KindID: c_uint, Type: *Type) *Attribute; - - pub const createStringAttribute = LLVMCreateStringAttribute; - extern fn LLVMCreateStringAttribute(C: *Context, Key: [*]const u8, Key_Len: c_uint, Value: [*]const u8, Value_Len: c_uint) *Attribute; - - pub const pointerType = LLVMPointerTypeInContext; - extern fn LLVMPointerTypeInContext(C: *Context, AddressSpace: c_uint) *Type; - - pub const intType = LLVMIntTypeInContext; - extern fn LLVMIntTypeInContext(C: *Context, NumBits: c_uint) *Type; - - pub const halfType = LLVMHalfTypeInContext; - extern fn LLVMHalfTypeInContext(C: *Context) *Type; - - pub const bfloatType = LLVMBFloatTypeInContext; - extern fn LLVMBFloatTypeInContext(C: *Context) *Type; - - pub const floatType = LLVMFloatTypeInContext; - extern fn LLVMFloatTypeInContext(C: *Context) *Type; - - pub const doubleType = LLVMDoubleTypeInContext; - extern fn LLVMDoubleTypeInContext(C: *Context) *Type; - - pub const fp128Type = LLVMFP128TypeInContext; - extern fn LLVMFP128TypeInContext(C: *Context) *Type; - - pub const x86_fp80Type = LLVMX86FP80TypeInContext; - extern fn LLVMX86FP80TypeInContext(C: *Context) *Type; - - pub const ppc_fp128Type = LLVMPPCFP128TypeInContext; - extern fn LLVMPPCFP128TypeInContext(C: *Context) *Type; - - pub const x86_amxType = LLVMX86AMXTypeInContext; - extern fn LLVMX86AMXTypeInContext(C: *Context) *Type; - - pub const x86_mmxType = LLVMX86MMXTypeInContext; - extern fn LLVMX86MMXTypeInContext(C: *Context) *Type; - - pub const voidType = LLVMVoidTypeInContext; - extern fn LLVMVoidTypeInContext(C: *Context) *Type; - - pub const labelType = LLVMLabelTypeInContext; - extern fn LLVMLabelTypeInContext(C: *Context) *Type; - - pub const tokenType = LLVMTokenTypeInContext; - extern fn LLVMTokenTypeInContext(C: *Context) *Type; - - pub const metadataType = LLVMMetadataTypeInContext; - extern fn LLVMMetadataTypeInContext(C: *Context) *Type; - - pub const structType = LLVMStructTypeInContext; - extern fn LLVMStructTypeInContext( - C: *Context, - ElementTypes: [*]const *Type, - ElementCount: c_uint, - Packed: Bool, - ) *Type; - - pub const structCreateNamed = LLVMStructCreateNamed; - extern fn LLVMStructCreateNamed(C: *Context, Name: [*:0]const u8) *Type; - - pub const constString = LLVMConstStringInContext; - extern fn LLVMConstStringInContext(C: *Context, Str: [*]const u8, Length: c_uint, DontNullTerminate: Bool) *Value; - - pub const appendBasicBlock = LLVMAppendBasicBlockInContext; - extern fn LLVMAppendBasicBlockInContext(C: *Context, Fn: *Value, Name: [*:0]const u8) *BasicBlock; - - pub const createBuilder = LLVMCreateBuilderInContext; - extern fn LLVMCreateBuilderInContext(C: *Context) *Builder; + pub const parseBitcodeInContext2 = LLVMParseBitcodeInContext2; + extern fn LLVMParseBitcodeInContext2(C: *Context, MemBuf: *MemoryBuffer, OutModule: **Module) Bool; pub const setOptBisectLimit = ZigLLVMSetOptBisectLimit; extern fn ZigLLVMSetOptBisectLimit(C: *Context, limit: c_int) void; }; -pub const Value = opaque { - pub const addAttributeAtIndex = LLVMAddAttributeAtIndex; - extern fn LLVMAddAttributeAtIndex(F: *Value, Idx: AttributeIndex, A: *Attribute) void; - - pub const removeEnumAttributeAtIndex = LLVMRemoveEnumAttributeAtIndex; - extern fn LLVMRemoveEnumAttributeAtIndex(F: *Value, Idx: AttributeIndex, KindID: c_uint) void; - - pub const removeStringAttributeAtIndex = LLVMRemoveStringAttributeAtIndex; - extern fn LLVMRemoveStringAttributeAtIndex(F: *Value, Idx: AttributeIndex, K: [*]const u8, KLen: c_uint) void; - - pub const getFirstBasicBlock = LLVMGetFirstBasicBlock; - extern fn LLVMGetFirstBasicBlock(Fn: *Value) ?*BasicBlock; - - pub const addIncoming = LLVMAddIncoming; - extern fn LLVMAddIncoming( - PhiNode: *Value, - IncomingValues: [*]const *Value, - IncomingBlocks: [*]const *BasicBlock, - Count: c_uint, - ) void; - - pub const setGlobalConstant = LLVMSetGlobalConstant; - extern fn LLVMSetGlobalConstant(GlobalVar: *Value, IsConstant: Bool) void; - - pub const setLinkage = LLVMSetLinkage; - extern fn LLVMSetLinkage(Global: *Value, Linkage: Linkage) void; - - pub const setVisibility = LLVMSetVisibility; - extern fn LLVMSetVisibility(Global: *Value, Linkage: Visibility) void; - - pub const setUnnamedAddr = LLVMSetUnnamedAddr; - extern fn LLVMSetUnnamedAddr(Global: *Value, HasUnnamedAddr: Bool) void; - - pub const setThreadLocalMode = LLVMSetThreadLocalMode; - extern fn LLVMSetThreadLocalMode(Global: *Value, Mode: ThreadLocalMode) void; - - pub const setSection = LLVMSetSection; - extern fn LLVMSetSection(Global: *Value, Section: [*:0]const u8) void; - - pub const removeGlobalValue = ZigLLVMRemoveGlobalValue; - extern fn ZigLLVMRemoveGlobalValue(GlobalVal: *Value) void; - - pub const eraseGlobalValue = ZigLLVMEraseGlobalValue; - extern fn ZigLLVMEraseGlobalValue(GlobalVal: *Value) void; - - pub const deleteGlobalValue = ZigLLVMDeleteGlobalValue; - extern fn ZigLLVMDeleteGlobalValue(GlobalVal: *Value) void; - - pub const setAliasee = LLVMAliasSetAliasee; - extern fn LLVMAliasSetAliasee(Alias: *Value, Aliasee: *Value) void; - - pub const constAdd = LLVMConstAdd; - extern fn LLVMConstAdd(LHSConstant: *Value, RHSConstant: *Value) *Value; - - pub const constNSWAdd = LLVMConstNSWAdd; - extern fn LLVMConstNSWAdd(LHSConstant: *Value, RHSConstant: *Value) *Value; - - pub const constNUWAdd = LLVMConstNUWAdd; - extern fn LLVMConstNUWAdd(LHSConstant: *Value, RHSConstant: *Value) *Value; - - pub const constSub = LLVMConstSub; - extern fn LLVMConstSub(LHSConstant: *Value, RHSConstant: *Value) *Value; - - pub const constNSWSub = LLVMConstNSWSub; - extern fn LLVMConstNSWSub(LHSConstant: *Value, RHSConstant: *Value) *Value; - - pub const constNUWSub = LLVMConstNUWSub; - extern fn LLVMConstNUWSub(LHSConstant: *Value, RHSConstant: *Value) *Value; - - pub const constMul = LLVMConstMul; - extern fn LLVMConstMul(LHSConstant: *Value, RHSConstant: *Value) *Value; - - pub const constNSWMul = LLVMConstNSWMul; - extern fn LLVMConstNSWMul(LHSConstant: *Value, RHSConstant: *Value) *Value; - - pub const constNUWMul = LLVMConstNUWMul; - extern fn LLVMConstNUWMul(LHSConstant: *Value, RHSConstant: *Value) *Value; - - pub const constAnd = LLVMConstAnd; - extern fn LLVMConstAnd(LHSConstant: *Value, RHSConstant: *Value) *Value; - - pub const constOr = LLVMConstOr; - extern fn LLVMConstOr(LHSConstant: *Value, RHSConstant: *Value) *Value; - - pub const constXor = LLVMConstXor; - extern fn LLVMConstXor(LHSConstant: *Value, RHSConstant: *Value) *Value; - - pub const constShl = LLVMConstShl; - extern fn LLVMConstShl(LHSConstant: *Value, RHSConstant: *Value) *Value; - - pub const constLShr = LLVMConstLShr; - extern fn LLVMConstLShr(LHSConstant: *Value, RHSConstant: *Value) *Value; - - pub const constAShr = LLVMConstAShr; - extern fn LLVMConstAShr(LHSConstant: *Value, RHSConstant: *Value) *Value; - - pub const constTrunc = LLVMConstTrunc; - extern fn LLVMConstTrunc(ConstantVal: *Value, ToType: *Type) *Value; - - pub const constSExt = LLVMConstSExt; - extern fn LLVMConstSExt(ConstantVal: *Value, ToType: *Type) *Value; - - pub const constZExt = LLVMConstZExt; - extern fn LLVMConstZExt(ConstantVal: *Value, ToType: *Type) *Value; - - pub const constFPTrunc = LLVMConstFPTrunc; - extern fn LLVMConstFPTrunc(ConstantVal: *Value, ToType: *Type) *Value; - - pub const constFPExt = LLVMConstFPExt; - extern fn LLVMConstFPExt(ConstantVal: *Value, ToType: *Type) *Value; - - pub const constUIToFP = LLVMConstUIToFP; - extern fn LLVMConstUIToFP(ConstantVal: *Value, ToType: *Type) *Value; - - pub const constSIToFP = LLVMConstSIToFP; - extern fn LLVMConstSIToFP(ConstantVal: *Value, ToType: *Type) *Value; - - pub const constFPToUI = LLVMConstFPToUI; - extern fn LLVMConstFPToUI(ConstantVal: *Value, ToType: *Type) *Value; - - pub const constFPToSI = LLVMConstFPToSI; - extern fn LLVMConstFPToSI(ConstantVal: *Value, ToType: *Type) *Value; - - pub const constPtrToInt = LLVMConstPtrToInt; - extern fn LLVMConstPtrToInt(ConstantVal: *Value, ToType: *Type) *Value; - - pub const constIntToPtr = LLVMConstIntToPtr; - extern fn LLVMConstIntToPtr(ConstantVal: *Value, ToType: *Type) *Value; - - pub const constBitCast = LLVMConstBitCast; - extern fn LLVMConstBitCast(ConstantVal: *Value, ToType: *Type) *Value; - - pub const constAddrSpaceCast = LLVMConstAddrSpaceCast; - extern fn LLVMConstAddrSpaceCast(ConstantVal: *Value, ToType: *Type) *Value; - - pub const constExtractElement = LLVMConstExtractElement; - extern fn LLVMConstExtractElement(VectorConstant: *Value, IndexConstant: *Value) *Value; - - pub const constInsertElement = LLVMConstInsertElement; - extern fn LLVMConstInsertElement( - VectorConstant: *Value, - ElementValueConstant: *Value, - IndexConstant: *Value, - ) *Value; - - pub const constShuffleVector = LLVMConstShuffleVector; - extern fn LLVMConstShuffleVector( - VectorAConstant: *Value, - VectorBConstant: *Value, - MaskConstant: *Value, - ) *Value; - - pub const isConstant = LLVMIsConstant; - extern fn LLVMIsConstant(Val: *Value) Bool; - - pub const blockAddress = LLVMBlockAddress; - extern fn LLVMBlockAddress(F: *Value, BB: *BasicBlock) *Value; - - pub const setWeak = LLVMSetWeak; - extern fn LLVMSetWeak(CmpXchgInst: *Value, IsWeak: Bool) void; - - pub const setOrdering = LLVMSetOrdering; - extern fn LLVMSetOrdering(MemoryAccessInst: *Value, Ordering: AtomicOrdering) void; - - pub const setVolatile = LLVMSetVolatile; - extern fn LLVMSetVolatile(MemoryAccessInst: *Value, IsVolatile: Bool) void; - - pub const setAlignment = LLVMSetAlignment; - extern fn LLVMSetAlignment(V: *Value, Bytes: c_uint) void; - - pub const getAlignment = LLVMGetAlignment; - extern fn LLVMGetAlignment(V: *Value) c_uint; - - pub const setFunctionCallConv = LLVMSetFunctionCallConv; - extern fn LLVMSetFunctionCallConv(Fn: *Value, CC: CallConv) void; - - pub const setInstructionCallConv = LLVMSetInstructionCallConv; - extern fn LLVMSetInstructionCallConv(Instr: *Value, CC: CallConv) void; - - pub const setTailCallKind = ZigLLVMSetTailCallKind; - extern fn ZigLLVMSetTailCallKind(CallInst: *Value, TailCallKind: TailCallKind) void; - - pub const addCallSiteAttribute = LLVMAddCallSiteAttribute; - extern fn LLVMAddCallSiteAttribute(C: *Value, Idx: AttributeIndex, A: *Attribute) void; - - pub const fnSetSubprogram = ZigLLVMFnSetSubprogram; - extern fn ZigLLVMFnSetSubprogram(f: *Value, subprogram: *DISubprogram) void; - - pub const setValueName = LLVMSetValueName2; - extern fn LLVMSetValueName2(Val: *Value, Name: [*]const u8, NameLen: usize) void; - - pub const takeName = ZigLLVMTakeName; - extern fn ZigLLVMTakeName(new_owner: *Value, victim: *Value) void; - - pub const getParam = LLVMGetParam; - extern fn LLVMGetParam(Fn: *Value, Index: c_uint) *Value; - - pub const setInitializer = ZigLLVMSetInitializer; - extern fn ZigLLVMSetInitializer(GlobalVar: *Value, ConstantVal: ?*Value) void; - - pub const setDLLStorageClass = LLVMSetDLLStorageClass; - extern fn LLVMSetDLLStorageClass(Global: *Value, Class: DLLStorageClass) void; - - pub const addCase = LLVMAddCase; - extern fn LLVMAddCase(Switch: *Value, OnVal: *Value, Dest: *BasicBlock) void; - - pub const replaceAllUsesWith = LLVMReplaceAllUsesWith; - extern fn LLVMReplaceAllUsesWith(OldVal: *Value, NewVal: *Value) void; - - pub const attachMetaData = ZigLLVMAttachMetaData; - extern fn ZigLLVMAttachMetaData(GlobalVar: *Value, DIG: *DIGlobalVariableExpression) void; - - pub const dump = LLVMDumpValue; - extern fn LLVMDumpValue(Val: *Value) void; -}; - -pub const Type = opaque { - pub const constNull = LLVMConstNull; - extern fn LLVMConstNull(Ty: *Type) *Value; - - pub const constInt = LLVMConstInt; - extern fn LLVMConstInt(IntTy: *Type, N: c_ulonglong, SignExtend: Bool) *Value; - - pub const constIntOfArbitraryPrecision = LLVMConstIntOfArbitraryPrecision; - extern fn LLVMConstIntOfArbitraryPrecision(IntTy: *Type, NumWords: c_uint, Words: [*]const u64) *Value; - - pub const constReal = LLVMConstReal; - extern fn LLVMConstReal(RealTy: *Type, N: f64) *Value; - - pub const constArray2 = LLVMConstArray2; - extern fn LLVMConstArray2(ElementTy: *Type, ConstantVals: [*]const *Value, Length: u64) *Value; - - pub const constNamedStruct = LLVMConstNamedStruct; - extern fn LLVMConstNamedStruct( - StructTy: *Type, - ConstantVals: [*]const *Value, - Count: c_uint, - ) *Value; - - pub const getUndef = LLVMGetUndef; - extern fn LLVMGetUndef(Ty: *Type) *Value; - - pub const getPoison = LLVMGetPoison; - extern fn LLVMGetPoison(Ty: *Type) *Value; - - pub const arrayType2 = LLVMArrayType2; - extern fn LLVMArrayType2(ElementType: *Type, ElementCount: u64) *Type; - - pub const vectorType = LLVMVectorType; - extern fn LLVMVectorType(ElementType: *Type, ElementCount: c_uint) *Type; - - pub const scalableVectorType = LLVMScalableVectorType; - extern fn LLVMScalableVectorType(ElementType: *Type, ElementCount: c_uint) *Type; - - pub const structSetBody = LLVMStructSetBody; - extern fn LLVMStructSetBody( - StructTy: *Type, - ElementTypes: [*]*Type, - ElementCount: c_uint, - Packed: Bool, - ) void; - - pub const isSized = LLVMTypeIsSized; - extern fn LLVMTypeIsSized(Ty: *Type) Bool; - - pub const constGEP = LLVMConstGEP2; - extern fn LLVMConstGEP2( - Ty: *Type, - ConstantVal: *Value, - ConstantIndices: [*]const *Value, - NumIndices: c_uint, - ) *Value; - - pub const constInBoundsGEP = LLVMConstInBoundsGEP2; - extern fn LLVMConstInBoundsGEP2( - Ty: *Type, - ConstantVal: *Value, - ConstantIndices: [*]const *Value, - NumIndices: c_uint, - ) *Value; - - pub const dump = LLVMDumpType; - extern fn LLVMDumpType(Ty: *Type) void; -}; - pub const Module = opaque { - pub const createWithName = LLVMModuleCreateWithNameInContext; - extern fn LLVMModuleCreateWithNameInContext(ModuleID: [*:0]const u8, C: *Context) *Module; - pub const dispose = LLVMDisposeModule; extern fn LLVMDisposeModule(*Module) void; - pub const verify = LLVMVerifyModule; - extern fn LLVMVerifyModule(*Module, Action: VerifierFailureAction, OutMessage: *[*:0]const u8) Bool; - - pub const setModuleDataLayout = LLVMSetModuleDataLayout; - extern fn LLVMSetModuleDataLayout(*Module, *TargetData) void; - pub const setModulePICLevel = ZigLLVMSetModulePICLevel; extern fn ZigLLVMSetModulePICLevel(module: *Module) void; @@ -409,508 +51,11 @@ pub const Module = opaque { pub const setModuleCodeModel = ZigLLVMSetModuleCodeModel; extern fn ZigLLVMSetModuleCodeModel(module: *Module, code_model: CodeModel) void; - - pub const addFunctionInAddressSpace = ZigLLVMAddFunctionInAddressSpace; - extern fn ZigLLVMAddFunctionInAddressSpace(*Module, Name: [*:0]const u8, FunctionTy: *Type, AddressSpace: c_uint) *Value; - - pub const printToString = LLVMPrintModuleToString; - extern fn LLVMPrintModuleToString(*Module) [*:0]const u8; - - pub const addGlobalInAddressSpace = LLVMAddGlobalInAddressSpace; - extern fn LLVMAddGlobalInAddressSpace(M: *Module, Ty: *Type, Name: [*:0]const u8, AddressSpace: c_uint) *Value; - - pub const dump = LLVMDumpModule; - extern fn LLVMDumpModule(M: *Module) void; - - pub const addAlias = LLVMAddAlias2; - extern fn LLVMAddAlias2( - M: *Module, - Ty: *Type, - AddrSpace: c_uint, - Aliasee: *Value, - Name: [*:0]const u8, - ) *Value; - - pub const setTarget = LLVMSetTarget; - extern fn LLVMSetTarget(M: *Module, Triple: [*:0]const u8) void; - - pub const addModuleDebugInfoFlag = ZigLLVMAddModuleDebugInfoFlag; - extern fn ZigLLVMAddModuleDebugInfoFlag(module: *Module, dwarf64: bool) void; - - pub const addModuleCodeViewFlag = ZigLLVMAddModuleCodeViewFlag; - extern fn ZigLLVMAddModuleCodeViewFlag(module: *Module) void; - - pub const createDIBuilder = ZigLLVMCreateDIBuilder; - extern fn ZigLLVMCreateDIBuilder(module: *Module, allow_unresolved: bool) *DIBuilder; - - pub const setModuleInlineAsm = LLVMSetModuleInlineAsm2; - extern fn LLVMSetModuleInlineAsm2(M: *Module, Asm: [*]const u8, Len: usize) void; - - pub const printModuleToFile = LLVMPrintModuleToFile; - extern fn LLVMPrintModuleToFile(M: *Module, Filename: [*:0]const u8, ErrorMessage: *[*:0]const u8) Bool; - - pub const writeBitcodeToFile = LLVMWriteBitcodeToFile; - extern fn LLVMWriteBitcodeToFile(M: *Module, Path: [*:0]const u8) c_int; }; pub const disposeMessage = LLVMDisposeMessage; extern fn LLVMDisposeMessage(Message: [*:0]const u8) void; -pub const VerifierFailureAction = enum(c_int) { - AbortProcess, - PrintMessage, - ReturnStatus, -}; - -pub const constVector = LLVMConstVector; -extern fn LLVMConstVector( - ScalarConstantVals: [*]*Value, - Size: c_uint, -) *Value; - -pub const constICmp = LLVMConstICmp; -extern fn LLVMConstICmp(Predicate: IntPredicate, LHSConstant: *Value, RHSConstant: *Value) *Value; - -pub const constFCmp = LLVMConstFCmp; -extern fn LLVMConstFCmp(Predicate: RealPredicate, LHSConstant: *Value, RHSConstant: *Value) *Value; - -pub const getEnumAttributeKindForName = LLVMGetEnumAttributeKindForName; -extern fn LLVMGetEnumAttributeKindForName(Name: [*]const u8, SLen: usize) c_uint; - -pub const getInlineAsm = LLVMGetInlineAsm; -extern fn LLVMGetInlineAsm( - Ty: *Type, - AsmString: [*]const u8, - AsmStringSize: usize, - Constraints: [*]const u8, - ConstraintsSize: usize, - HasSideEffects: Bool, - IsAlignStack: Bool, - Dialect: InlineAsmDialect, - CanThrow: Bool, -) *Value; - -pub const functionType = LLVMFunctionType; -extern fn LLVMFunctionType( - ReturnType: *Type, - ParamTypes: [*]const *Type, - ParamCount: c_uint, - IsVarArg: Bool, -) *Type; - -pub const InlineAsmDialect = enum(c_uint) { ATT, Intel }; - -pub const Attribute = opaque {}; - -pub const Builder = opaque { - pub const dispose = LLVMDisposeBuilder; - extern fn LLVMDisposeBuilder(Builder: *Builder) void; - - pub const positionBuilder = LLVMPositionBuilder; - extern fn LLVMPositionBuilder( - Builder: *Builder, - Block: *BasicBlock, - Instr: ?*Value, - ) void; - - pub const buildZExt = LLVMBuildZExt; - extern fn LLVMBuildZExt( - *Builder, - Value: *Value, - DestTy: *Type, - Name: [*:0]const u8, - ) *Value; - - pub const buildSExt = LLVMBuildSExt; - extern fn LLVMBuildSExt( - *Builder, - Val: *Value, - DestTy: *Type, - Name: [*:0]const u8, - ) *Value; - - pub const buildCall = LLVMBuildCall2; - extern fn LLVMBuildCall2( - *Builder, - *Type, - Fn: *Value, - Args: [*]const *Value, - NumArgs: c_uint, - Name: [*:0]const u8, - ) *Value; - - pub const buildRetVoid = LLVMBuildRetVoid; - extern fn LLVMBuildRetVoid(*Builder) *Value; - - pub const buildRet = LLVMBuildRet; - extern fn LLVMBuildRet(*Builder, V: *Value) *Value; - - pub const buildUnreachable = LLVMBuildUnreachable; - extern fn LLVMBuildUnreachable(*Builder) *Value; - - pub const buildAlloca = LLVMBuildAlloca; - extern fn LLVMBuildAlloca(*Builder, Ty: *Type, Name: [*:0]const u8) *Value; - - pub const buildStore = LLVMBuildStore; - extern fn LLVMBuildStore(*Builder, Val: *Value, Ptr: *Value) *Value; - - pub const buildLoad = LLVMBuildLoad2; - extern fn LLVMBuildLoad2(*Builder, Ty: *Type, PointerVal: *Value, Name: [*:0]const u8) *Value; - - pub const buildFAdd = LLVMBuildFAdd; - extern fn LLVMBuildFAdd(*Builder, LHS: *Value, RHS: *Value, Name: [*:0]const u8) *Value; - - pub const buildAdd = LLVMBuildAdd; - extern fn LLVMBuildAdd(*Builder, LHS: *Value, RHS: *Value, Name: [*:0]const u8) *Value; - - pub const buildNSWAdd = LLVMBuildNSWAdd; - extern fn LLVMBuildNSWAdd(*Builder, LHS: *Value, RHS: *Value, Name: [*:0]const u8) *Value; - - pub const buildNUWAdd = LLVMBuildNUWAdd; - extern fn LLVMBuildNUWAdd(*Builder, LHS: *Value, RHS: *Value, Name: [*:0]const u8) *Value; - - pub const buildFSub = LLVMBuildFSub; - extern fn LLVMBuildFSub(*Builder, LHS: *Value, RHS: *Value, Name: [*:0]const u8) *Value; - - pub const buildFNeg = LLVMBuildFNeg; - extern fn LLVMBuildFNeg(*Builder, V: *Value, Name: [*:0]const u8) *Value; - - pub const buildSub = LLVMBuildSub; - extern fn LLVMBuildSub(*Builder, LHS: *Value, RHS: *Value, Name: [*:0]const u8) *Value; - - pub const buildNSWSub = LLVMBuildNSWSub; - extern fn LLVMBuildNSWSub(*Builder, LHS: *Value, RHS: *Value, Name: [*:0]const u8) *Value; - - pub const buildNUWSub = LLVMBuildNUWSub; - extern fn LLVMBuildNUWSub(*Builder, LHS: *Value, RHS: *Value, Name: [*:0]const u8) *Value; - - pub const buildFMul = LLVMBuildFMul; - extern fn LLVMBuildFMul(*Builder, LHS: *Value, RHS: *Value, Name: [*:0]const u8) *Value; - - pub const buildMul = LLVMBuildMul; - extern fn LLVMBuildMul(*Builder, LHS: *Value, RHS: *Value, Name: [*:0]const u8) *Value; - - pub const buildNSWMul = LLVMBuildNSWMul; - extern fn LLVMBuildNSWMul(*Builder, LHS: *Value, RHS: *Value, Name: [*:0]const u8) *Value; - - pub const buildNUWMul = LLVMBuildNUWMul; - extern fn LLVMBuildNUWMul(*Builder, LHS: *Value, RHS: *Value, Name: [*:0]const u8) *Value; - - pub const buildUDiv = LLVMBuildUDiv; - extern fn LLVMBuildUDiv(*Builder, LHS: *Value, RHS: *Value, Name: [*:0]const u8) *Value; - - pub const buildSDiv = LLVMBuildSDiv; - extern fn LLVMBuildSDiv(*Builder, LHS: *Value, RHS: *Value, Name: [*:0]const u8) *Value; - - pub const buildFDiv = LLVMBuildFDiv; - extern fn LLVMBuildFDiv(*Builder, LHS: *Value, RHS: *Value, Name: [*:0]const u8) *Value; - - pub const buildURem = LLVMBuildURem; - extern fn LLVMBuildURem(*Builder, LHS: *Value, RHS: *Value, Name: [*:0]const u8) *Value; - - pub const buildSRem = LLVMBuildSRem; - extern fn LLVMBuildSRem(*Builder, LHS: *Value, RHS: *Value, Name: [*:0]const u8) *Value; - - pub const buildFRem = LLVMBuildFRem; - extern fn LLVMBuildFRem(*Builder, LHS: *Value, RHS: *Value, Name: [*:0]const u8) *Value; - - pub const buildAnd = LLVMBuildAnd; - extern fn LLVMBuildAnd(*Builder, LHS: *Value, RHS: *Value, Name: [*:0]const u8) *Value; - - pub const buildLShr = LLVMBuildLShr; - extern fn LLVMBuildLShr(*Builder, LHS: *Value, RHS: *Value, Name: [*:0]const u8) *Value; - - pub const buildAShr = LLVMBuildAShr; - extern fn LLVMBuildAShr(*Builder, LHS: *Value, RHS: *Value, Name: [*:0]const u8) *Value; - - pub const buildLShrExact = ZigLLVMBuildLShrExact; - extern fn ZigLLVMBuildLShrExact(*Builder, LHS: *Value, RHS: *Value, Name: [*:0]const u8) *Value; - - pub const buildAShrExact = ZigLLVMBuildAShrExact; - extern fn ZigLLVMBuildAShrExact(*Builder, LHS: *Value, RHS: *Value, Name: [*:0]const u8) *Value; - - pub const buildShl = LLVMBuildShl; - extern fn LLVMBuildShl(*Builder, LHS: *Value, RHS: *Value, Name: [*:0]const u8) *Value; - - pub const buildNUWShl = ZigLLVMBuildNUWShl; - extern fn ZigLLVMBuildNUWShl(*Builder, LHS: *Value, RHS: *Value, Name: [*:0]const u8) *Value; - - pub const buildNSWShl = ZigLLVMBuildNSWShl; - extern fn ZigLLVMBuildNSWShl(*Builder, LHS: *Value, RHS: *Value, Name: [*:0]const u8) *Value; - - pub const buildOr = LLVMBuildOr; - extern fn LLVMBuildOr(*Builder, LHS: *Value, RHS: *Value, Name: [*:0]const u8) *Value; - - pub const buildXor = LLVMBuildXor; - extern fn LLVMBuildXor(*Builder, LHS: *Value, RHS: *Value, Name: [*:0]const u8) *Value; - - pub const buildBitCast = LLVMBuildBitCast; - extern fn LLVMBuildBitCast(*Builder, Val: *Value, DestTy: *Type, Name: [*:0]const u8) *Value; - - pub const buildGEP = LLVMBuildGEP2; - extern fn LLVMBuildGEP2( - B: *Builder, - Ty: *Type, - Pointer: *Value, - Indices: [*]const *Value, - NumIndices: c_uint, - Name: [*:0]const u8, - ) *Value; - - pub const buildInBoundsGEP = LLVMBuildInBoundsGEP2; - extern fn LLVMBuildInBoundsGEP2( - B: *Builder, - Ty: *Type, - Pointer: *Value, - Indices: [*]const *Value, - NumIndices: c_uint, - Name: [*:0]const u8, - ) *Value; - - pub const buildICmp = LLVMBuildICmp; - extern fn LLVMBuildICmp(*Builder, Op: IntPredicate, LHS: *Value, RHS: *Value, Name: [*:0]const u8) *Value; - - pub const buildFCmp = LLVMBuildFCmp; - extern fn LLVMBuildFCmp(*Builder, Op: RealPredicate, LHS: *Value, RHS: *Value, Name: [*:0]const u8) *Value; - - pub const buildBr = LLVMBuildBr; - extern fn LLVMBuildBr(*Builder, Dest: *BasicBlock) *Value; - - pub const buildCondBr = LLVMBuildCondBr; - extern fn LLVMBuildCondBr(*Builder, If: *Value, Then: *BasicBlock, Else: *BasicBlock) *Value; - - pub const buildSwitch = LLVMBuildSwitch; - extern fn LLVMBuildSwitch(*Builder, V: *Value, Else: *BasicBlock, NumCases: c_uint) *Value; - - pub const buildPhi = LLVMBuildPhi; - extern fn LLVMBuildPhi(*Builder, Ty: *Type, Name: [*:0]const u8) *Value; - - pub const buildExtractValue = LLVMBuildExtractValue; - extern fn LLVMBuildExtractValue( - *Builder, - AggVal: *Value, - Index: c_uint, - Name: [*:0]const u8, - ) *Value; - - pub const buildExtractElement = LLVMBuildExtractElement; - extern fn LLVMBuildExtractElement( - *Builder, - VecVal: *Value, - Index: *Value, - Name: [*:0]const u8, - ) *Value; - - pub const buildInsertElement = LLVMBuildInsertElement; - extern fn LLVMBuildInsertElement( - *Builder, - VecVal: *Value, - EltVal: *Value, - Index: *Value, - Name: [*:0]const u8, - ) *Value; - - pub const buildPtrToInt = LLVMBuildPtrToInt; - extern fn LLVMBuildPtrToInt( - *Builder, - Val: *Value, - DestTy: *Type, - Name: [*:0]const u8, - ) *Value; - - pub const buildIntToPtr = LLVMBuildIntToPtr; - extern fn LLVMBuildIntToPtr( - *Builder, - Val: *Value, - DestTy: *Type, - Name: [*:0]const u8, - ) *Value; - - pub const buildTrunc = LLVMBuildTrunc; - extern fn LLVMBuildTrunc( - *Builder, - Val: *Value, - DestTy: *Type, - Name: [*:0]const u8, - ) *Value; - - pub const buildInsertValue = LLVMBuildInsertValue; - extern fn LLVMBuildInsertValue( - *Builder, - AggVal: *Value, - EltVal: *Value, - Index: c_uint, - Name: [*:0]const u8, - ) *Value; - - pub const buildAtomicCmpXchg = LLVMBuildAtomicCmpXchg; - extern fn LLVMBuildAtomicCmpXchg( - builder: *Builder, - ptr: *Value, - cmp: *Value, - new_val: *Value, - success_ordering: AtomicOrdering, - failure_ordering: AtomicOrdering, - is_single_threaded: Bool, - ) *Value; - - pub const buildSelect = LLVMBuildSelect; - extern fn LLVMBuildSelect( - *Builder, - If: *Value, - Then: *Value, - Else: *Value, - Name: [*:0]const u8, - ) *Value; - - pub const buildFence = LLVMBuildFence; - extern fn LLVMBuildFence( - B: *Builder, - ordering: AtomicOrdering, - singleThread: Bool, - Name: [*:0]const u8, - ) *Value; - - pub const buildAtomicRmw = LLVMBuildAtomicRMW; - extern fn LLVMBuildAtomicRMW( - B: *Builder, - op: AtomicRMWBinOp, - PTR: *Value, - Val: *Value, - ordering: AtomicOrdering, - singleThread: Bool, - ) *Value; - - pub const buildFPToUI = LLVMBuildFPToUI; - extern fn LLVMBuildFPToUI( - *Builder, - Val: *Value, - DestTy: *Type, - Name: [*:0]const u8, - ) *Value; - - pub const buildFPToSI = LLVMBuildFPToSI; - extern fn LLVMBuildFPToSI( - *Builder, - Val: *Value, - DestTy: *Type, - Name: [*:0]const u8, - ) *Value; - - pub const buildUIToFP = LLVMBuildUIToFP; - extern fn LLVMBuildUIToFP( - *Builder, - Val: *Value, - DestTy: *Type, - Name: [*:0]const u8, - ) *Value; - - pub const buildSIToFP = LLVMBuildSIToFP; - extern fn LLVMBuildSIToFP( - *Builder, - Val: *Value, - DestTy: *Type, - Name: [*:0]const u8, - ) *Value; - - pub const buildFPTrunc = LLVMBuildFPTrunc; - extern fn LLVMBuildFPTrunc( - *Builder, - Val: *Value, - DestTy: *Type, - Name: [*:0]const u8, - ) *Value; - - pub const buildFPExt = LLVMBuildFPExt; - extern fn LLVMBuildFPExt( - *Builder, - Val: *Value, - DestTy: *Type, - Name: [*:0]const u8, - ) *Value; - - pub const buildExactUDiv = LLVMBuildExactUDiv; - extern fn LLVMBuildExactUDiv(*Builder, LHS: *Value, RHS: *Value, Name: [*:0]const u8) *Value; - - pub const buildExactSDiv = LLVMBuildExactSDiv; - extern fn LLVMBuildExactSDiv(*Builder, LHS: *Value, RHS: *Value, Name: [*:0]const u8) *Value; - - pub const setCurrentDebugLocation = ZigLLVMSetCurrentDebugLocation2; - extern fn ZigLLVMSetCurrentDebugLocation2(builder: *Builder, line: c_uint, column: c_uint, scope: *DIScope, inlined_at: ?*DILocation) void; - - pub const clearCurrentDebugLocation = ZigLLVMClearCurrentDebugLocation; - extern fn ZigLLVMClearCurrentDebugLocation(builder: *Builder) void; - - pub const getCurrentDebugLocation2 = LLVMGetCurrentDebugLocation2; - extern fn LLVMGetCurrentDebugLocation2(Builder: *Builder) *Metadata; - - pub const setCurrentDebugLocation2 = LLVMSetCurrentDebugLocation2; - extern fn LLVMSetCurrentDebugLocation2(Builder: *Builder, Loc: *Metadata) void; - - pub const buildShuffleVector = LLVMBuildShuffleVector; - extern fn LLVMBuildShuffleVector(*Builder, V1: *Value, V2: *Value, Mask: *Value, Name: [*:0]const u8) *Value; - - pub const setFastMath = ZigLLVMSetFastMath; - extern fn ZigLLVMSetFastMath(B: *Builder, on_state: bool) void; - - pub const buildAddrSpaceCast = LLVMBuildAddrSpaceCast; - extern fn LLVMBuildAddrSpaceCast(B: *Builder, Val: *Value, DestTy: *Type, Name: [*:0]const u8) *Value; - - pub const buildAllocaInAddressSpace = ZigLLVMBuildAllocaInAddressSpace; - extern fn ZigLLVMBuildAllocaInAddressSpace(B: *Builder, Ty: *Type, AddressSpace: c_uint, Name: [*:0]const u8) *Value; - - pub const buildVAArg = LLVMBuildVAArg; - extern fn LLVMBuildVAArg(*Builder, List: *Value, Ty: *Type, Name: [*:0]const u8) *Value; -}; - -pub const MDString = opaque { - pub const get = LLVMMDStringInContext2; - extern fn LLVMMDStringInContext2(C: *Context, Str: [*]const u8, SLen: usize) *MDString; -}; - -pub const DIScope = opaque { - pub const toNode = ZigLLVMScopeToNode; - extern fn ZigLLVMScopeToNode(scope: *DIScope) *DINode; -}; - -pub const DINode = opaque {}; -pub const Metadata = opaque {}; - -pub const IntPredicate = enum(c_uint) { - EQ = 32, - NE = 33, - UGT = 34, - UGE = 35, - ULT = 36, - ULE = 37, - SGT = 38, - SGE = 39, - SLT = 40, - SLE = 41, -}; - -pub const RealPredicate = enum(c_uint) { - OEQ = 1, - OGT = 2, - OGE = 3, - OLT = 4, - OLE = 5, - ONE = 6, - ORD = 7, - UNO = 8, - UEQ = 9, - UGT = 10, - UGE = 11, - ULT = 12, - ULE = 13, - UNE = 14, -}; - -pub const BasicBlock = opaque { - pub const deleteBasicBlock = LLVMDeleteBasicBlock; - extern fn LLVMDeleteBasicBlock(BB: *BasicBlock) void; -}; - pub const TargetMachine = opaque { pub const create = ZigLLVMCreateTargetMachine; extern fn ZigLLVMCreateTargetMachine( @@ -945,23 +90,11 @@ pub const TargetMachine = opaque { llvm_ir_filename: ?[*:0]const u8, bitcode_filename: ?[*:0]const u8, ) bool; - - pub const createTargetDataLayout = LLVMCreateTargetDataLayout; - extern fn LLVMCreateTargetDataLayout(*TargetMachine) *TargetData; }; pub const TargetData = opaque { pub const dispose = LLVMDisposeTargetData; extern fn LLVMDisposeTargetData(*TargetData) void; - - pub const abiAlignmentOfType = LLVMABIAlignmentOfType; - extern fn LLVMABIAlignmentOfType(TD: *TargetData, Ty: *Type) c_uint; - - pub const abiSizeOfType = LLVMABISizeOfType; - extern fn LLVMABISizeOfType(TD: *TargetData, Ty: *Type) c_ulonglong; - - pub const stringRep = LLVMCopyStringRepOfTargetData; - extern fn LLVMCopyStringRepOfTargetData(TD: *TargetData) [*:0]const u8; }; pub const CodeModel = enum(c_int) { @@ -991,11 +124,6 @@ pub const RelocMode = enum(c_int) { ROPI_RWPI, }; -pub const CodeGenFileType = enum(c_int) { - AssemblyFile, - ObjectFile, -}; - pub const ABIType = enum(c_int) { /// Target-specific (either soft or hard depending on triple, etc). Default, @@ -1266,576 +394,3 @@ extern fn ZigLLVMWriteImportLibrary( output_lib_path: [*:0]const u8, kill_at: bool, ) bool; - -pub const Linkage = enum(c_uint) { - External, - AvailableExternally, - LinkOnceAny, - LinkOnceODR, - LinkOnceODRAutoHide, - WeakAny, - WeakODR, - Appending, - Internal, - Private, - DLLImport, - DLLExport, - ExternalWeak, - Ghost, - Common, - LinkerPrivate, - LinkerPrivateWeak, -}; - -pub const Visibility = enum(c_uint) { - Default, - Hidden, - Protected, -}; - -pub const ThreadLocalMode = enum(c_uint) { - NotThreadLocal, - GeneralDynamicTLSModel, - LocalDynamicTLSModel, - InitialExecTLSModel, - LocalExecTLSModel, -}; - -pub const AtomicOrdering = enum(c_uint) { - NotAtomic = 0, - Unordered = 1, - Monotonic = 2, - Acquire = 4, - Release = 5, - AcquireRelease = 6, - SequentiallyConsistent = 7, -}; - -pub const AtomicRMWBinOp = enum(c_int) { - Xchg, - Add, - Sub, - And, - Nand, - Or, - Xor, - Max, - Min, - UMax, - UMin, - FAdd, - FSub, - FMax, - FMin, -}; - -pub const CallConv = enum(c_uint) { - C = 0, - Fast = 8, - Cold = 9, - GHC = 10, - HiPE = 11, - WebKit_JS = 12, - AnyReg = 13, - PreserveMost = 14, - PreserveAll = 15, - Swift = 16, - CXX_FAST_TLS = 17, - - X86_StdCall = 64, - X86_FastCall = 65, - ARM_APCS = 66, - ARM_AAPCS = 67, - ARM_AAPCS_VFP = 68, - MSP430_INTR = 69, - X86_ThisCall = 70, - PTX_Kernel = 71, - PTX_Device = 72, - SPIR_FUNC = 75, - SPIR_KERNEL = 76, - Intel_OCL_BI = 77, - X86_64_SysV = 78, - Win64 = 79, - X86_VectorCall = 80, - HHVM = 81, - HHVM_C = 82, - X86_INTR = 83, - AVR_INTR = 84, - AVR_SIGNAL = 85, - AVR_BUILTIN = 86, - AMDGPU_VS = 87, - AMDGPU_GS = 88, - AMDGPU_PS = 89, - AMDGPU_CS = 90, - AMDGPU_KERNEL = 91, - X86_RegCall = 92, - AMDGPU_HS = 93, - MSP430_BUILTIN = 94, - AMDGPU_LS = 95, - AMDGPU_ES = 96, - AArch64_VectorCall = 97, -}; - -pub const CallAttr = enum(c_int) { - Auto, - NeverTail, - NeverInline, - AlwaysTail, - AlwaysInline, -}; - -pub const TailCallKind = enum(c_uint) { - None, - Tail, - MustTail, - NoTail, -}; - -pub const DLLStorageClass = enum(c_uint) { - Default, - DLLImport, - DLLExport, -}; - -pub const address_space = struct { - pub const default: c_uint = 0; - - // See llvm/lib/Target/X86/X86.h - pub const x86_64 = x86; - pub const x86 = struct { - pub const gs: c_uint = 256; - pub const fs: c_uint = 257; - pub const ss: c_uint = 258; - - pub const ptr32_sptr: c_uint = 270; - pub const ptr32_uptr: c_uint = 271; - pub const ptr64: c_uint = 272; - }; - - // See llvm/lib/Target/AVR/AVR.h - pub const avr = struct { - pub const flash: c_uint = 1; - pub const flash1: c_uint = 2; - pub const flash2: c_uint = 3; - pub const flash3: c_uint = 4; - pub const flash4: c_uint = 5; - pub const flash5: c_uint = 6; - }; - - // See llvm/lib/Target/NVPTX/NVPTX.h - pub const nvptx = struct { - pub const generic: c_uint = 0; - pub const global: c_uint = 1; - pub const constant: c_uint = 2; - pub const shared: c_uint = 3; - pub const param: c_uint = 4; - pub const local: c_uint = 5; - }; - - // See llvm/lib/Target/AMDGPU/AMDGPU.h - pub const amdgpu = struct { - pub const flat: c_uint = 0; - pub const global: c_uint = 1; - pub const region: c_uint = 2; - pub const local: c_uint = 3; - pub const constant: c_uint = 4; - pub const private: c_uint = 5; - pub const constant_32bit: c_uint = 6; - pub const buffer_fat_pointer: c_uint = 7; - pub const param_d: c_uint = 6; - pub const param_i: c_uint = 7; - pub const constant_buffer_0: c_uint = 8; - pub const constant_buffer_1: c_uint = 9; - pub const constant_buffer_2: c_uint = 10; - pub const constant_buffer_3: c_uint = 11; - pub const constant_buffer_4: c_uint = 12; - pub const constant_buffer_5: c_uint = 13; - pub const constant_buffer_6: c_uint = 14; - pub const constant_buffer_7: c_uint = 15; - pub const constant_buffer_8: c_uint = 16; - pub const constant_buffer_9: c_uint = 17; - pub const constant_buffer_10: c_uint = 18; - pub const constant_buffer_11: c_uint = 19; - pub const constant_buffer_12: c_uint = 20; - pub const constant_buffer_13: c_uint = 21; - pub const constant_buffer_14: c_uint = 22; - pub const constant_buffer_15: c_uint = 23; - }; - - // See llvm/lib/Target/WebAssembly/Utils/WebAssemblyTypetilities.h - pub const wasm = struct { - pub const variable: c_uint = 1; - pub const externref: c_uint = 10; - pub const funcref: c_uint = 20; - }; -}; - -pub const DIEnumerator = opaque {}; -pub const DILocalVariable = opaque {}; -pub const DILocation = opaque {}; -pub const DIGlobalExpression = opaque {}; - -pub const DIGlobalVariable = opaque { - pub const toNode = ZigLLVMGlobalVariableToNode; - extern fn ZigLLVMGlobalVariableToNode(global_variable: *DIGlobalVariable) *DINode; - - pub const replaceLinkageName = ZigLLVMGlobalVariableReplaceLinkageName; - extern fn ZigLLVMGlobalVariableReplaceLinkageName(global_variable: *DIGlobalVariable, linkage_name: *MDString) void; -}; -pub const DIGlobalVariableExpression = opaque { - pub const getVariable = ZigLLVMGlobalGetVariable; - extern fn ZigLLVMGlobalGetVariable(global_variable: *DIGlobalVariableExpression) *DIGlobalVariable; -}; -pub const DIType = opaque { - pub const toScope = ZigLLVMTypeToScope; - extern fn ZigLLVMTypeToScope(ty: *DIType) *DIScope; - - pub const toNode = ZigLLVMTypeToNode; - extern fn ZigLLVMTypeToNode(ty: *DIType) *DINode; -}; -pub const DIFile = opaque { - pub const toScope = ZigLLVMFileToScope; - extern fn ZigLLVMFileToScope(difile: *DIFile) *DIScope; - - pub const toNode = ZigLLVMFileToNode; - extern fn ZigLLVMFileToNode(difile: *DIFile) *DINode; -}; -pub const DILexicalBlock = opaque { - pub const toScope = ZigLLVMLexicalBlockToScope; - extern fn ZigLLVMLexicalBlockToScope(lexical_block: *DILexicalBlock) *DIScope; - - pub const toNode = ZigLLVMLexicalBlockToNode; - extern fn ZigLLVMLexicalBlockToNode(lexical_block: *DILexicalBlock) *DINode; -}; -pub const DICompileUnit = opaque { - pub const toScope = ZigLLVMCompileUnitToScope; - extern fn ZigLLVMCompileUnitToScope(compile_unit: *DICompileUnit) *DIScope; - - pub const toNode = ZigLLVMCompileUnitToNode; - extern fn ZigLLVMCompileUnitToNode(compile_unit: *DICompileUnit) *DINode; -}; -pub const DISubprogram = opaque { - pub const toScope = ZigLLVMSubprogramToScope; - extern fn ZigLLVMSubprogramToScope(subprogram: *DISubprogram) *DIScope; - - pub const toNode = ZigLLVMSubprogramToNode; - extern fn ZigLLVMSubprogramToNode(subprogram: *DISubprogram) *DINode; - - pub const replaceLinkageName = ZigLLVMSubprogramReplaceLinkageName; - extern fn ZigLLVMSubprogramReplaceLinkageName(subprogram: *DISubprogram, linkage_name: *MDString) void; -}; - -pub const getDebugLoc = ZigLLVMGetDebugLoc2; -extern fn ZigLLVMGetDebugLoc2(line: c_uint, col: c_uint, scope: *DIScope, inlined_at: ?*DILocation) *DILocation; - -pub const DIBuilder = opaque { - pub const dispose = ZigLLVMDisposeDIBuilder; - extern fn ZigLLVMDisposeDIBuilder(dib: *DIBuilder) void; - - pub const finalize = ZigLLVMDIBuilderFinalize; - extern fn ZigLLVMDIBuilderFinalize(dib: *DIBuilder) void; - - pub const createPointerType = ZigLLVMCreateDebugPointerType; - extern fn ZigLLVMCreateDebugPointerType( - dib: *DIBuilder, - pointee_type: *DIType, - size_in_bits: u64, - align_in_bits: u64, - name: [*:0]const u8, - ) *DIType; - - pub const createBasicType = ZigLLVMCreateDebugBasicType; - extern fn ZigLLVMCreateDebugBasicType( - dib: *DIBuilder, - name: [*:0]const u8, - size_in_bits: u64, - encoding: c_uint, - ) *DIType; - - pub const createArrayType = ZigLLVMCreateDebugArrayType; - extern fn ZigLLVMCreateDebugArrayType( - dib: *DIBuilder, - size_in_bits: u64, - align_in_bits: u64, - elem_type: *DIType, - elem_count: i64, - ) *DIType; - - pub const createEnumerator = ZigLLVMCreateDebugEnumerator; - extern fn ZigLLVMCreateDebugEnumerator( - dib: *DIBuilder, - name: [*:0]const u8, - val: u64, - is_unsigned: bool, - ) *DIEnumerator; - - pub const createEnumerator2 = ZigLLVMCreateDebugEnumeratorOfArbitraryPrecision; - extern fn ZigLLVMCreateDebugEnumeratorOfArbitraryPrecision( - dib: *DIBuilder, - name: [*:0]const u8, - num_words: c_uint, - words: [*]const u64, - bits: c_uint, - is_unsigned: bool, - ) *DIEnumerator; - - pub const createEnumerationType = ZigLLVMCreateDebugEnumerationType; - extern fn ZigLLVMCreateDebugEnumerationType( - dib: *DIBuilder, - scope: *DIScope, - name: [*:0]const u8, - file: *DIFile, - line_number: c_uint, - size_in_bits: u64, - align_in_bits: u64, - enumerator_array: [*]const *DIEnumerator, - enumerator_array_len: c_int, - underlying_type: *DIType, - unique_id: [*:0]const u8, - ) *DIType; - - pub const createStructType = ZigLLVMCreateDebugStructType; - extern fn ZigLLVMCreateDebugStructType( - dib: *DIBuilder, - scope: *DIScope, - name: [*:0]const u8, - file: ?*DIFile, - line_number: c_uint, - size_in_bits: u64, - align_in_bits: u64, - flags: c_uint, - derived_from: ?*DIType, - types_array: [*]const *DIType, - types_array_len: c_int, - run_time_lang: c_uint, - vtable_holder: ?*DIType, - unique_id: [*:0]const u8, - ) *DIType; - - pub const createUnionType = ZigLLVMCreateDebugUnionType; - extern fn ZigLLVMCreateDebugUnionType( - dib: *DIBuilder, - scope: *DIScope, - name: [*:0]const u8, - file: ?*DIFile, - line_number: c_uint, - size_in_bits: u64, - align_in_bits: u64, - flags: c_uint, - types_array: [*]const *DIType, - types_array_len: c_int, - run_time_lang: c_uint, - unique_id: [*:0]const u8, - ) *DIType; - - pub const createMemberType = ZigLLVMCreateDebugMemberType; - extern fn ZigLLVMCreateDebugMemberType( - dib: *DIBuilder, - scope: *DIScope, - name: [*:0]const u8, - file: ?*DIFile, - line: c_uint, - size_in_bits: u64, - align_in_bits: u64, - offset_in_bits: u64, - flags: c_uint, - ty: *DIType, - ) *DIType; - - pub const createReplaceableCompositeType = ZigLLVMCreateReplaceableCompositeType; - extern fn ZigLLVMCreateReplaceableCompositeType( - dib: *DIBuilder, - tag: c_uint, - name: [*:0]const u8, - scope: *DIScope, - file: ?*DIFile, - line: c_uint, - ) *DIType; - - pub const createForwardDeclType = ZigLLVMCreateDebugForwardDeclType; - extern fn ZigLLVMCreateDebugForwardDeclType( - dib: *DIBuilder, - tag: c_uint, - name: [*:0]const u8, - scope: ?*DIScope, - file: ?*DIFile, - line: c_uint, - ) *DIType; - - pub const replaceTemporary = ZigLLVMReplaceTemporary; - extern fn ZigLLVMReplaceTemporary(dib: *DIBuilder, ty: *DIType, replacement: *DIType) void; - - pub const replaceDebugArrays = ZigLLVMReplaceDebugArrays; - extern fn ZigLLVMReplaceDebugArrays( - dib: *DIBuilder, - ty: *DIType, - types_array: [*]const *DIType, - types_array_len: c_int, - ) void; - - pub const createSubroutineType = ZigLLVMCreateSubroutineType; - extern fn ZigLLVMCreateSubroutineType( - dib: *DIBuilder, - types_array: [*]const *DIType, - types_array_len: c_int, - flags: c_uint, - ) *DIType; - - pub const createAutoVariable = ZigLLVMCreateAutoVariable; - extern fn ZigLLVMCreateAutoVariable( - dib: *DIBuilder, - scope: *DIScope, - name: [*:0]const u8, - file: *DIFile, - line_no: c_uint, - ty: *DIType, - always_preserve: bool, - flags: c_uint, - ) *DILocalVariable; - - pub const createGlobalVariableExpression = ZigLLVMCreateGlobalVariableExpression; - extern fn ZigLLVMCreateGlobalVariableExpression( - dib: *DIBuilder, - scope: *DIScope, - name: [*:0]const u8, - linkage_name: [*:0]const u8, - file: *DIFile, - line_no: c_uint, - di_type: *DIType, - is_local_to_unit: bool, - ) *DIGlobalVariableExpression; - - pub const createParameterVariable = ZigLLVMCreateParameterVariable; - extern fn ZigLLVMCreateParameterVariable( - dib: *DIBuilder, - scope: *DIScope, - name: [*:0]const u8, - file: *DIFile, - line_no: c_uint, - ty: *DIType, - always_preserve: bool, - flags: c_uint, - arg_no: c_uint, - ) *DILocalVariable; - - pub const createLexicalBlock = ZigLLVMCreateLexicalBlock; - extern fn ZigLLVMCreateLexicalBlock( - dib: *DIBuilder, - scope: *DIScope, - file: *DIFile, - line: c_uint, - col: c_uint, - ) *DILexicalBlock; - - pub const createCompileUnit = ZigLLVMCreateCompileUnit; - extern fn ZigLLVMCreateCompileUnit( - dib: *DIBuilder, - lang: c_uint, - difile: *DIFile, - producer: [*:0]const u8, - is_optimized: bool, - flags: [*:0]const u8, - runtime_version: c_uint, - split_name: [*:0]const u8, - dwo_id: u64, - emit_debug_info: bool, - ) *DICompileUnit; - - pub const createFile = ZigLLVMCreateFile; - extern fn ZigLLVMCreateFile( - dib: *DIBuilder, - filename: [*:0]const u8, - directory: [*:0]const u8, - ) *DIFile; - - pub const createFunction = ZigLLVMCreateFunction; - extern fn ZigLLVMCreateFunction( - dib: *DIBuilder, - scope: *DIScope, - name: [*:0]const u8, - linkage_name: [*:0]const u8, - file: *DIFile, - lineno: c_uint, - fn_di_type: *DIType, - is_local_to_unit: bool, - is_definition: bool, - scope_line: c_uint, - flags: c_uint, - is_optimized: bool, - decl_subprogram: ?*DISubprogram, - ) *DISubprogram; - - pub const createVectorType = ZigLLVMDIBuilderCreateVectorType; - extern fn ZigLLVMDIBuilderCreateVectorType( - dib: *DIBuilder, - SizeInBits: u64, - AlignInBits: u32, - Ty: *DIType, - elem_count: u32, - ) *DIType; - - pub const insertDeclareAtEnd = ZigLLVMInsertDeclareAtEnd; - extern fn ZigLLVMInsertDeclareAtEnd( - dib: *DIBuilder, - storage: *Value, - var_info: *DILocalVariable, - debug_loc: *DILocation, - basic_block_ref: *BasicBlock, - ) *Value; - - pub const insertDeclare = ZigLLVMInsertDeclare; - extern fn ZigLLVMInsertDeclare( - dib: *DIBuilder, - storage: *Value, - var_info: *DILocalVariable, - debug_loc: *DILocation, - insert_before_instr: *Value, - ) *Value; - - pub const insertDbgValueIntrinsicAtEnd = ZigLLVMInsertDbgValueIntrinsicAtEnd; - extern fn ZigLLVMInsertDbgValueIntrinsicAtEnd( - dib: *DIBuilder, - val: *Value, - var_info: *DILocalVariable, - debug_loc: *DILocation, - basic_block_ref: *BasicBlock, - ) *Value; -}; - -pub const DIFlags = opaque { - pub const Zero = 0; - pub const Private = 1; - pub const Protected = 2; - pub const Public = 3; - - pub const FwdDecl = 1 << 2; - pub const AppleBlock = 1 << 3; - pub const BlockByrefStruct = 1 << 4; - pub const Virtual = 1 << 5; - pub const Artificial = 1 << 6; - pub const Explicit = 1 << 7; - pub const Prototyped = 1 << 8; - pub const ObjcClassComplete = 1 << 9; - pub const ObjectPointer = 1 << 10; - pub const Vector = 1 << 11; - pub const StaticMember = 1 << 12; - pub const LValueReference = 1 << 13; - pub const RValueReference = 1 << 14; - pub const Reserved = 1 << 15; - - pub const SingleInheritance = 1 << 16; - pub const MultipleInheritance = 2 << 16; - pub const VirtualInheritance = 3 << 16; - - pub const IntroducedVirtual = 1 << 18; - pub const BitField = 1 << 19; - pub const NoReturn = 1 << 20; - pub const TypePassByValue = 1 << 22; - pub const TypePassByReference = 1 << 23; - pub const EnumClass = 1 << 24; - pub const Thunk = 1 << 25; - pub const NonTrivial = 1 << 26; - pub const BigEndian = 1 << 27; - pub const LittleEndian = 1 << 28; - pub const AllCallsDescribed = 1 << 29; -}; diff --git a/src/codegen/llvm/bitcode_writer.zig b/src/codegen/llvm/bitcode_writer.zig new file mode 100644 index 0000000000..7140c87929 --- /dev/null +++ b/src/codegen/llvm/bitcode_writer.zig @@ -0,0 +1,426 @@ +const std = @import("std"); + +pub const AbbrevOp = union(enum) { + literal: u32, // 0 + fixed: u16, // 1 + fixed_runtime: type, // 1 + vbr: u16, // 2 + char6: void, // 4 + blob: void, // 5 + array_fixed: u16, // 3, 1 + array_fixed_runtime: type, // 3, 1 + array_vbr: u16, // 3, 2 + array_char6: void, // 3, 4 +}; + +pub const Error = error{OutOfMemory}; + +pub fn BitcodeWriter(comptime types: []const type) type { + return struct { + const BcWriter = @This(); + + buffer: std.ArrayList(u32), + bit_buffer: u32 = 0, + bit_count: u5 = 0, + + widths: [types.len]u16, + + pub fn getTypeWidth(self: BcWriter, comptime Type: type) u16 { + return self.widths[comptime std.mem.indexOfScalar(type, types, Type).?]; + } + + pub fn init(allocator: std.mem.Allocator, widths: [types.len]u16) BcWriter { + return .{ + .buffer = std.ArrayList(u32).init(allocator), + .widths = widths, + }; + } + + pub fn deinit(self: BcWriter) void { + self.buffer.deinit(); + } + + pub fn toOwnedSlice(self: *BcWriter) Error![]const u32 { + std.debug.assert(self.bit_count == 0); + return self.buffer.toOwnedSlice(); + } + + pub fn length(self: BcWriter) usize { + std.debug.assert(self.bit_count == 0); + return self.buffer.items.len; + } + + pub fn writeBits(self: *BcWriter, value: anytype, bits: u16) Error!void { + if (bits == 0) return; + + var in_buffer = bufValue(value, 32); + var in_bits = bits; + + // Store input bits in buffer if they fit otherwise store as many as possible and flush + if (self.bit_count > 0) { + const bits_remaining = 31 - self.bit_count + 1; + const n: u5 = @intCast(@min(bits_remaining, in_bits)); + const v = @as(u32, @truncate(in_buffer)) << self.bit_count; + self.bit_buffer |= v; + in_buffer >>= n; + + self.bit_count +%= n; + in_bits -= n; + + if (self.bit_count != 0) return; + try self.buffer.append(self.bit_buffer); + self.bit_buffer = 0; + } + + // Write 32-bit chunks of input bits + while (in_bits >= 32) { + try self.buffer.append(@truncate(in_buffer)); + + in_buffer >>= 31; + in_buffer >>= 1; + in_bits -= 32; + } + + // Store remaining input bits in buffer + if (in_bits > 0) { + self.bit_count = @intCast(in_bits); + self.bit_buffer = @truncate(in_buffer); + } + } + + pub fn writeVBR(self: *BcWriter, value: anytype, comptime vbr_bits: usize) Error!void { + comptime { + std.debug.assert(vbr_bits > 1); + if (@bitSizeOf(@TypeOf(value)) > 64) @compileError("Unsupported VBR block type: " ++ @typeName(@TypeOf(value))); + } + + var in_buffer = bufValue(value, vbr_bits); + + const continue_bit = @as(@TypeOf(in_buffer), 1) << @intCast(vbr_bits - 1); + const mask = continue_bit - 1; + + // If input is larger than one VBR block can store + // then store vbr_bits - 1 bits and a continue bit + while (in_buffer > mask) { + try self.writeBits(in_buffer & mask | continue_bit, vbr_bits); + in_buffer >>= @intCast(vbr_bits - 1); + } + + // Store remaining bits + try self.writeBits(in_buffer, vbr_bits); + } + + pub fn bitsVBR(_: *const BcWriter, value: anytype, comptime vbr_bits: usize) u16 { + comptime { + std.debug.assert(vbr_bits > 1); + if (@bitSizeOf(@TypeOf(value)) > 64) @compileError("Unsupported VBR block type: " ++ @typeName(@TypeOf(value))); + } + + var bits: u16 = 0; + + var in_buffer = bufValue(value, vbr_bits); + + const continue_bit = @as(@TypeOf(in_buffer), 1) << @intCast(vbr_bits - 1); + const mask = continue_bit - 1; + + // If input is larger than one VBR block can store + // then store vbr_bits - 1 bits and a continue bit + while (in_buffer > mask) { + bits += @intCast(vbr_bits); + in_buffer >>= @intCast(vbr_bits - 1); + } + + // Store remaining bits + bits += @intCast(vbr_bits); + return bits; + } + + pub fn write6BitChar(self: *BcWriter, c: u8) Error!void { + try self.writeBits(charTo6Bit(c), 6); + } + + pub fn alignTo32(self: *BcWriter) Error!void { + if (self.bit_count == 0) return; + + try self.buffer.append(self.bit_buffer); + self.bit_buffer = 0; + self.bit_count = 0; + } + + pub fn enterTopBlock(self: *BcWriter, comptime SubBlock: type) Error!BlockWriter(SubBlock) { + return BlockWriter(SubBlock).init(self, 2, true); + } + + fn BlockWriter(comptime Block: type) type { + return struct { + const Self = @This(); + + // The minimum abbrev id length based on the number of abbrevs present in the block + pub const abbrev_len = std.math.log2_int_ceil( + u6, + 4 + (if (@hasDecl(Block, "abbrevs")) Block.abbrevs.len else 0), + ); + + start: usize, + bitcode: *BcWriter, + + pub fn init(bitcode: *BcWriter, comptime parent_abbrev_len: u6, comptime define_abbrevs: bool) Error!Self { + try bitcode.writeBits(1, parent_abbrev_len); + try bitcode.writeVBR(Block.id, 8); + try bitcode.writeVBR(abbrev_len, 4); + try bitcode.alignTo32(); + + // We store the index of the block size and store a dummy value as the number of words in the block + const start = bitcode.length(); + try bitcode.writeBits(0, 32); + + var self = Self{ + .start = start, + .bitcode = bitcode, + }; + + // Predefine all block abbrevs + if (define_abbrevs) { + inline for (Block.abbrevs) |Abbrev| { + try self.defineAbbrev(&Abbrev.ops); + } + } + + return self; + } + + pub fn enterSubBlock(self: Self, comptime SubBlock: type, comptime define_abbrevs: bool) Error!BlockWriter(SubBlock) { + return BlockWriter(SubBlock).init(self.bitcode, abbrev_len, define_abbrevs); + } + + pub fn end(self: *Self) Error!void { + try self.bitcode.writeBits(0, abbrev_len); + try self.bitcode.alignTo32(); + + // Set the number of words in the block at the start of the block + self.bitcode.buffer.items[self.start] = @truncate(self.bitcode.length() - self.start - 1); + } + + pub fn writeUnabbrev(self: *Self, code: u32, values: []const u64) Error!void { + try self.bitcode.writeBits(3, abbrev_len); + try self.bitcode.writeVBR(code, 6); + try self.bitcode.writeVBR(values.len, 6); + for (values) |val| { + try self.bitcode.writeVBR(val, 6); + } + } + + pub fn writeAbbrev(self: *Self, params: anytype) Error!void { + return self.writeAbbrevAdapted(params, struct { + pub fn get(_: @This(), param: anytype, comptime _: []const u8) @TypeOf(param) { + return param; + } + }{}); + } + + pub fn abbrevId(comptime Abbrev: type) u32 { + inline for (Block.abbrevs, 0..) |abbrev, i| { + if (Abbrev == abbrev) return i + 4; + } + + @compileError("Unknown abbrev: " ++ @typeName(Abbrev)); + } + + pub fn writeAbbrevAdapted( + self: *Self, + params: anytype, + adapter: anytype, + ) Error!void { + const Abbrev = @TypeOf(params); + + try self.bitcode.writeBits(comptime abbrevId(Abbrev), abbrev_len); + + const fields = std.meta.fields(Abbrev); + + // This abbreviation might only contain literals + if (fields.len == 0) return; + + comptime var field_index: usize = 0; + inline for (Abbrev.ops) |ty| { + const field_name = fields[field_index].name; + const param = @field(params, field_name); + + switch (ty) { + .literal => continue, + .fixed => |len| try self.bitcode.writeBits(adapter.get(param, field_name), len), + .fixed_runtime => |width_ty| try self.bitcode.writeBits( + adapter.get(param, field_name), + self.bitcode.getTypeWidth(width_ty), + ), + .vbr => |len| try self.bitcode.writeVBR(adapter.get(param, field_name), len), + .char6 => try self.bitcode.write6BitChar(adapter.get(param, field_name)), + .blob => { + try self.bitcode.writeVBR(param.len, 6); + try self.bitcode.alignTo32(); + for (param) |x| { + try self.bitcode.writeBits(x, 8); + } + try self.bitcode.alignTo32(); + }, + .array_fixed => |len| { + try self.bitcode.writeVBR(param.len, 6); + for (param) |x| { + try self.bitcode.writeBits(adapter.get(x, field_name), len); + } + }, + .array_fixed_runtime => |width_ty| { + try self.bitcode.writeVBR(param.len, 6); + for (param) |x| { + try self.bitcode.writeBits( + adapter.get(x, field_name), + self.bitcode.getTypeWidth(width_ty), + ); + } + }, + .array_vbr => |len| { + try self.bitcode.writeVBR(param.len, 6); + for (param) |x| { + try self.bitcode.writeVBR(adapter.get(x, field_name), len); + } + }, + .array_char6 => { + try self.bitcode.writeVBR(param.len, 6); + for (param) |x| { + try self.bitcode.write6BitChar(adapter.get(x, field_name)); + } + }, + } + field_index += 1; + if (field_index == fields.len) break; + } + } + + pub fn defineAbbrev(self: *Self, comptime ops: []const AbbrevOp) Error!void { + const bitcode = self.bitcode; + try bitcode.writeBits(2, abbrev_len); + + // ops.len is not accurate because arrays are actually two ops + try bitcode.writeVBR(blk: { + var count: usize = 0; + inline for (ops) |op| { + count += switch (op) { + .literal, .fixed, .fixed_runtime, .vbr, .char6, .blob => 1, + .array_fixed, .array_fixed_runtime, .array_vbr, .array_char6 => 2, + }; + } + break :blk count; + }, 5); + + inline for (ops) |op| { + switch (op) { + .literal => |value| { + try bitcode.writeBits(1, 1); + try bitcode.writeVBR(value, 8); + }, + .fixed => |width| { + try bitcode.writeBits(0, 1); + try bitcode.writeBits(1, 3); + try bitcode.writeVBR(width, 5); + }, + .fixed_runtime => |width_ty| { + try bitcode.writeBits(0, 1); + try bitcode.writeBits(1, 3); + try bitcode.writeVBR(bitcode.getTypeWidth(width_ty), 5); + }, + .vbr => |width| { + try bitcode.writeBits(0, 1); + try bitcode.writeBits(2, 3); + try bitcode.writeVBR(width, 5); + }, + .char6 => { + try bitcode.writeBits(0, 1); + try bitcode.writeBits(4, 3); + }, + .blob => { + try bitcode.writeBits(0, 1); + try bitcode.writeBits(5, 3); + }, + .array_fixed => |width| { + // Array op + try bitcode.writeBits(0, 1); + try bitcode.writeBits(3, 3); + + // Fixed or VBR op + try bitcode.writeBits(0, 1); + try bitcode.writeBits(1, 3); + try bitcode.writeVBR(width, 5); + }, + .array_fixed_runtime => |width_ty| { + // Array op + try bitcode.writeBits(0, 1); + try bitcode.writeBits(3, 3); + + // Fixed or VBR op + try bitcode.writeBits(0, 1); + try bitcode.writeBits(1, 3); + try bitcode.writeVBR(bitcode.getTypeWidth(width_ty), 5); + }, + .array_vbr => |width| { + // Array op + try bitcode.writeBits(0, 1); + try bitcode.writeBits(3, 3); + + // Fixed or VBR op + try bitcode.writeBits(0, 1); + try bitcode.writeBits(2, 3); + try bitcode.writeVBR(width, 5); + }, + .array_char6 => { + // Array op + try bitcode.writeBits(0, 1); + try bitcode.writeBits(3, 3); + + // Char6 op + try bitcode.writeBits(0, 1); + try bitcode.writeBits(4, 3); + }, + } + } + } + }; + } + }; +} + +fn charTo6Bit(c: u8) u8 { + return switch (c) { + 'a'...'z' => c - 'a', + 'A'...'Z' => c - 'A' + 26, + '0'...'9' => c - '0' + 52, + '.' => 62, + '_' => 63, + else => @panic("Failed to encode byte as 6-bit char"), + }; +} + +fn BufType(comptime T: type, comptime min_len: usize) type { + return std.meta.Int(.unsigned, @max(min_len, @bitSizeOf(switch (@typeInfo(T)) { + .ComptimeInt => u32, + .Int => |info| if (info.signedness == .unsigned) + T + else + @compileError("Unsupported type: " ++ @typeName(T)), + .Enum => |info| info.tag_type, + .Bool => u1, + .Struct => |info| switch (info.layout) { + .Auto, .Extern => @compileError("Unsupported type: " ++ @typeName(T)), + .Packed => std.meta.Int(.unsigned, @bitSizeOf(T)), + }, + else => @compileError("Unsupported type: " ++ @typeName(T)), + }))); +} + +fn bufValue(value: anytype, comptime min_len: usize) BufType(@TypeOf(value), min_len) { + return switch (@typeInfo(@TypeOf(value))) { + .ComptimeInt, .Int => @intCast(value), + .Enum => @intFromEnum(value), + .Bool => @intFromBool(value), + .Struct => @intCast(@as(std.meta.Int(.unsigned, @bitSizeOf(@TypeOf(value))), @bitCast(value))), + else => unreachable, + }; +} diff --git a/src/codegen/llvm/ir.zig b/src/codegen/llvm/ir.zig new file mode 100644 index 0000000000..f5c0e535c2 --- /dev/null +++ b/src/codegen/llvm/ir.zig @@ -0,0 +1,1643 @@ +const std = @import("std"); +const Builder = @import("Builder.zig"); +const bitcode_writer = @import("bitcode_writer.zig"); + +const AbbrevOp = bitcode_writer.AbbrevOp; + +pub const MAGIC: u32 = 0xdec04342; + +const ValueAbbrev = AbbrevOp{ .vbr = 6 }; +const ValueArrayAbbrev = AbbrevOp{ .array_vbr = 6 }; + +const ConstantAbbrev = AbbrevOp{ .vbr = 6 }; +const ConstantArrayAbbrev = AbbrevOp{ .array_vbr = 6 }; + +const MetadataAbbrev = AbbrevOp{ .vbr = 16 }; +const MetadataArrayAbbrev = AbbrevOp{ .array_vbr = 16 }; + +const LineAbbrev = AbbrevOp{ .vbr = 8 }; +const ColumnAbbrev = AbbrevOp{ .vbr = 8 }; + +const BlockAbbrev = AbbrevOp{ .vbr = 6 }; + +pub const MetadataKind = enum(u1) { + dbg = 0, +}; + +pub const Identification = struct { + pub const id = 13; + + pub const abbrevs = [_]type{ + Version, + Epoch, + }; + + pub const Version = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 1 }, + .{ .array_fixed = 8 }, + }; + string: []const u8, + }; + + pub const Epoch = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 2 }, + .{ .vbr = 6 }, + }; + epoch: u32, + }; +}; + +pub const Module = struct { + pub const id = 8; + + pub const abbrevs = [_]type{ + Version, + String, + Variable, + Function, + Alias, + }; + + pub const Version = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 1 }, + .{ .literal = 2 }, + }; + }; + + pub const String = struct { + pub const ops = [_]AbbrevOp{ + .{ .vbr = 4 }, + .{ .array_fixed = 8 }, + }; + code: u16, + string: []const u8, + }; + + pub const Variable = struct { + const AddrSpaceAndIsConst = packed struct { + is_const: bool, + one: u1 = 1, + addr_space: Builder.AddrSpace, + }; + + pub const ops = [_]AbbrevOp{ + .{ .literal = 7 }, // Code + .{ .vbr = 16 }, // strtab_offset + .{ .vbr = 16 }, // strtab_size + .{ .fixed_runtime = Builder.Type }, + .{ .fixed = @bitSizeOf(AddrSpaceAndIsConst) }, // isconst + ConstantAbbrev, // initid + .{ .fixed = @bitSizeOf(Builder.Linkage) }, + .{ .fixed = @bitSizeOf(Builder.Alignment) }, + .{ .vbr = 16 }, // section + .{ .fixed = @bitSizeOf(Builder.Visibility) }, + .{ .fixed = @bitSizeOf(Builder.ThreadLocal) }, // threadlocal + .{ .fixed = @bitSizeOf(Builder.UnnamedAddr) }, + .{ .fixed = @bitSizeOf(Builder.ExternallyInitialized) }, + .{ .fixed = @bitSizeOf(Builder.DllStorageClass) }, + .{ .literal = 0 }, // comdat + .{ .literal = 0 }, // attributes + .{ .fixed = @bitSizeOf(Builder.Preemption) }, + }; + strtab_offset: usize, + strtab_size: usize, + type_index: Builder.Type, + is_const: AddrSpaceAndIsConst, + initid: u32, + linkage: Builder.Linkage, + alignment: std.meta.Int(.unsigned, @bitSizeOf(Builder.Alignment)), + section: usize, + visibility: Builder.Visibility, + thread_local: Builder.ThreadLocal, + unnamed_addr: Builder.UnnamedAddr, + externally_initialized: Builder.ExternallyInitialized, + dllstorageclass: Builder.DllStorageClass, + preemption: Builder.Preemption, + }; + + pub const Function = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 8 }, // Code + .{ .vbr = 16 }, // strtab_offset + .{ .vbr = 16 }, // strtab_size + .{ .fixed_runtime = Builder.Type }, + .{ .fixed = @bitSizeOf(Builder.CallConv) }, + .{ .fixed = 1 }, // isproto + .{ .fixed = @bitSizeOf(Builder.Linkage) }, + .{ .vbr = 16 }, // paramattr + .{ .fixed = @bitSizeOf(Builder.Alignment) }, + .{ .vbr = 16 }, // section + .{ .fixed = @bitSizeOf(Builder.Visibility) }, + .{ .literal = 0 }, // gc + .{ .fixed = @bitSizeOf(Builder.UnnamedAddr) }, + .{ .literal = 0 }, // prologuedata + .{ .fixed = @bitSizeOf(Builder.DllStorageClass) }, + .{ .literal = 0 }, // comdat + .{ .literal = 0 }, // prefixdata + .{ .literal = 0 }, // personalityfn + .{ .fixed = @bitSizeOf(Builder.Preemption) }, + .{ .fixed = @bitSizeOf(Builder.AddrSpace) }, + }; + strtab_offset: usize, + strtab_size: usize, + type_index: Builder.Type, + call_conv: Builder.CallConv, + is_proto: bool, + linkage: Builder.Linkage, + paramattr: usize, + alignment: std.meta.Int(.unsigned, @bitSizeOf(Builder.Alignment)), + section: usize, + visibility: Builder.Visibility, + unnamed_addr: Builder.UnnamedAddr, + dllstorageclass: Builder.DllStorageClass, + preemption: Builder.Preemption, + addr_space: Builder.AddrSpace, + }; + + pub const Alias = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 14 }, // Code + .{ .vbr = 16 }, // strtab_offset + .{ .vbr = 16 }, // strtab_size + .{ .fixed_runtime = Builder.Type }, + .{ .fixed = @bitSizeOf(Builder.AddrSpace) }, + ConstantAbbrev, // aliasee val + .{ .fixed = @bitSizeOf(Builder.Linkage) }, + .{ .fixed = @bitSizeOf(Builder.Visibility) }, + .{ .fixed = @bitSizeOf(Builder.DllStorageClass) }, + .{ .fixed = @bitSizeOf(Builder.ThreadLocal) }, + .{ .fixed = @bitSizeOf(Builder.UnnamedAddr) }, + .{ .fixed = @bitSizeOf(Builder.Preemption) }, + }; + strtab_offset: usize, + strtab_size: usize, + type_index: Builder.Type, + addr_space: Builder.AddrSpace, + aliasee: u32, + linkage: Builder.Linkage, + visibility: Builder.Visibility, + dllstorageclass: Builder.DllStorageClass, + thread_local: Builder.ThreadLocal, + unnamed_addr: Builder.UnnamedAddr, + preemption: Builder.Preemption, + }; +}; + +pub const BlockInfo = struct { + pub const id = 0; + + pub const set_block_id = 1; + + pub const abbrevs = [_]type{}; +}; + +pub const Type = struct { + pub const id = 17; + + pub const abbrevs = [_]type{ + NumEntry, + Simple, + Opaque, + Integer, + StructAnon, + StructNamed, + StructName, + Array, + Vector, + Pointer, + Target, + Function, + }; + + pub const NumEntry = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 1 }, + .{ .fixed = 32 }, + }; + num: u32, + }; + + pub const Simple = struct { + pub const ops = [_]AbbrevOp{ + .{ .vbr = 4 }, + }; + code: u5, + }; + + pub const Opaque = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 6 }, + .{ .literal = 0 }, + }; + }; + + pub const Integer = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 7 }, + .{ .fixed = 28 }, + }; + width: u28, + }; + + pub const StructAnon = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 18 }, + .{ .fixed = 1 }, + .{ .array_fixed_runtime = Builder.Type }, + }; + is_packed: bool, + types: []const Builder.Type, + }; + + pub const StructNamed = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 20 }, + .{ .fixed = 1 }, + .{ .array_fixed_runtime = Builder.Type }, + }; + is_packed: bool, + types: []const Builder.Type, + }; + + pub const StructName = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 19 }, + .{ .array_fixed = 8 }, + }; + string: []const u8, + }; + + pub const Array = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 11 }, + .{ .vbr = 16 }, + .{ .fixed_runtime = Builder.Type }, + }; + len: u64, + child: Builder.Type, + }; + + pub const Vector = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 12 }, + .{ .vbr = 16 }, + .{ .fixed_runtime = Builder.Type }, + }; + len: u64, + child: Builder.Type, + }; + + pub const Pointer = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 25 }, + .{ .vbr = 4 }, + }; + addr_space: Builder.AddrSpace, + }; + + pub const Target = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 26 }, + .{ .vbr = 4 }, + .{ .array_fixed_runtime = Builder.Type }, + .{ .array_fixed = 32 }, + }; + num_types: u32, + types: []const Builder.Type, + ints: []const u32, + }; + + pub const Function = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 21 }, + .{ .fixed = 1 }, + .{ .fixed_runtime = Builder.Type }, + .{ .array_fixed_runtime = Builder.Type }, + }; + is_vararg: bool, + return_type: Builder.Type, + param_types: []const Builder.Type, + }; +}; + +pub const Paramattr = struct { + pub const id = 9; + + pub const abbrevs = [_]type{ + Entry, + }; + + pub const Entry = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 2 }, + .{ .array_vbr = 8 }, + }; + group_indices: []const u64, + }; +}; + +pub const ParamattrGroup = struct { + pub const id = 10; + + pub const abbrevs = [_]type{}; +}; + +pub const Constants = struct { + pub const id = 11; + + pub const abbrevs = [_]type{ + SetType, + Null, + Undef, + Poison, + Integer, + Half, + Float, + Double, + Fp80, + Fp128, + Aggregate, + String, + CString, + Cast, + Binary, + Cmp, + ExtractElement, + InsertElement, + ShuffleVector, + ShuffleVectorEx, + BlockAddress, + DsoLocalEquivalentOrNoCfi, + }; + + pub const SetType = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 1 }, + .{ .fixed_runtime = Builder.Type }, + }; + type_id: Builder.Type, + }; + + pub const Null = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 2 }, + }; + }; + + pub const Undef = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 3 }, + }; + }; + + pub const Poison = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 26 }, + }; + }; + + pub const Integer = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 4 }, + .{ .vbr = 16 }, + }; + value: u64, + }; + + pub const Half = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 6 }, + .{ .fixed = 16 }, + }; + value: u16, + }; + + pub const Float = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 6 }, + .{ .fixed = 32 }, + }; + value: u32, + }; + + pub const Double = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 6 }, + .{ .vbr = 6 }, + }; + value: u64, + }; + + pub const Fp80 = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 6 }, + .{ .vbr = 6 }, + .{ .vbr = 6 }, + }; + hi: u64, + lo: u16, + }; + + pub const Fp128 = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 6 }, + .{ .vbr = 6 }, + .{ .vbr = 6 }, + }; + lo: u64, + hi: u64, + }; + + pub const Aggregate = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 7 }, + .{ .array_fixed = 32 }, + }; + values: []const Builder.Constant, + }; + + pub const String = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 8 }, + .{ .array_fixed = 8 }, + }; + string: []const u8, + }; + + pub const CString = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 9 }, + .{ .array_fixed = 8 }, + }; + string: []const u8, + }; + + pub const Cast = struct { + const CastOpcode = Builder.CastOpcode; + pub const ops = [_]AbbrevOp{ + .{ .literal = 11 }, + .{ .fixed = @bitSizeOf(CastOpcode) }, + .{ .fixed_runtime = Builder.Type }, + ConstantAbbrev, + }; + + opcode: CastOpcode, + type_index: Builder.Type, + val: Builder.Constant, + }; + + pub const Binary = struct { + const BinaryOpcode = Builder.BinaryOpcode; + pub const ops = [_]AbbrevOp{ + .{ .literal = 10 }, + .{ .fixed = @bitSizeOf(BinaryOpcode) }, + ConstantAbbrev, + ConstantAbbrev, + }; + + opcode: BinaryOpcode, + lhs: Builder.Constant, + rhs: Builder.Constant, + }; + + pub const Cmp = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 17 }, + .{ .fixed_runtime = Builder.Type }, + ConstantAbbrev, + ConstantAbbrev, + .{ .vbr = 6 }, + }; + + ty: Builder.Type, + lhs: Builder.Constant, + rhs: Builder.Constant, + pred: u32, + }; + + pub const ExtractElement = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 14 }, + .{ .fixed_runtime = Builder.Type }, + ConstantAbbrev, + .{ .fixed_runtime = Builder.Type }, + ConstantAbbrev, + }; + + val_type: Builder.Type, + val: Builder.Constant, + index_type: Builder.Type, + index: Builder.Constant, + }; + + pub const InsertElement = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 15 }, + ConstantAbbrev, + ConstantAbbrev, + .{ .fixed_runtime = Builder.Type }, + ConstantAbbrev, + }; + + val: Builder.Constant, + elem: Builder.Constant, + index_type: Builder.Type, + index: Builder.Constant, + }; + + pub const ShuffleVector = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 16 }, + ValueAbbrev, + ValueAbbrev, + ValueAbbrev, + }; + + lhs: Builder.Constant, + rhs: Builder.Constant, + mask: Builder.Constant, + }; + + pub const ShuffleVectorEx = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 19 }, + .{ .fixed_runtime = Builder.Type }, + ValueAbbrev, + ValueAbbrev, + ValueAbbrev, + }; + + ty: Builder.Type, + lhs: Builder.Constant, + rhs: Builder.Constant, + mask: Builder.Constant, + }; + + pub const BlockAddress = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 21 }, + .{ .fixed_runtime = Builder.Type }, + ConstantAbbrev, + BlockAbbrev, + }; + type_id: Builder.Type, + function: u32, + block: u32, + }; + + pub const DsoLocalEquivalentOrNoCfi = struct { + pub const ops = [_]AbbrevOp{ + .{ .fixed = 5 }, + .{ .fixed_runtime = Builder.Type }, + ConstantAbbrev, + }; + code: u5, + type_id: Builder.Type, + function: u32, + }; +}; + +pub const MetadataKindBlock = struct { + pub const id = 22; + + pub const abbrevs = [_]type{ + Kind, + }; + + pub const Kind = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 6 }, + .{ .vbr = 4 }, + .{ .array_fixed = 8 }, + }; + id: u32, + name: []const u8, + }; +}; + +pub const MetadataAttachmentBlock = struct { + pub const id = 16; + + pub const abbrevs = [_]type{ + AttachmentSingle, + }; + + pub const AttachmentSingle = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 11 }, + .{ .fixed = 1 }, + MetadataAbbrev, + }; + kind: MetadataKind, + metadata: Builder.Metadata, + }; +}; + +pub const MetadataBlock = struct { + pub const id = 15; + + pub const abbrevs = [_]type{ + Strings, + File, + CompileUnit, + Subprogram, + LexicalBlock, + Location, + BasicType, + CompositeType, + DerivedType, + SubroutineType, + Enumerator, + Subrange, + Expression, + Node, + LocalVar, + Parameter, + GlobalVar, + GlobalVarExpression, + Constant, + Name, + NamedNode, + GlobalDeclAttachment, + }; + + pub const Strings = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 35 }, + .{ .vbr = 6 }, + .{ .vbr = 6 }, + .blob, + }; + num_strings: u32, + strings_offset: u32, + blob: []const u8, + }; + + pub const File = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 16 }, + .{ .literal = 0 }, // is distinct + MetadataAbbrev, // filename + MetadataAbbrev, // directory + .{ .literal = 0 }, // checksum + .{ .literal = 0 }, // checksum + }; + + filename: Builder.MetadataString, + directory: Builder.MetadataString, + }; + + pub const CompileUnit = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 20 }, + .{ .literal = 1 }, // is distinct + .{ .literal = std.dwarf.LANG.C99 }, // source language + MetadataAbbrev, // file + MetadataAbbrev, // producer + .{ .fixed = 1 }, // isOptimized + .{ .literal = 0 }, // raw flags + .{ .literal = 0 }, // runtime version + .{ .literal = 0 }, // split debug file name + .{ .literal = 1 }, // emission kind + MetadataAbbrev, // enums + .{ .literal = 0 }, // retained types + .{ .literal = 0 }, // subprograms + MetadataAbbrev, // globals + .{ .literal = 0 }, // imported entities + .{ .literal = 0 }, // DWO ID + .{ .literal = 0 }, // macros + .{ .literal = 0 }, // split debug inlining + .{ .literal = 0 }, // debug info profiling + .{ .literal = 0 }, // name table kind + .{ .literal = 0 }, // ranges base address + .{ .literal = 0 }, // raw sysroot + .{ .literal = 0 }, // raw SDK + }; + + file: Builder.Metadata, + producer: Builder.MetadataString, + is_optimized: bool, + enums: Builder.Metadata, + globals: Builder.Metadata, + }; + + pub const Subprogram = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 21 }, + .{ .literal = 0b111 }, // is distinct | has sp flags | has flags + MetadataAbbrev, // scope + MetadataAbbrev, // name + MetadataAbbrev, // linkage name + MetadataAbbrev, // file + LineAbbrev, // line + MetadataAbbrev, // type + LineAbbrev, // scope line + .{ .literal = 0 }, // containing type + .{ .fixed = 32 }, // sp flags + .{ .literal = 0 }, // virtual index + .{ .fixed = 32 }, // flags + MetadataAbbrev, // compile unit + .{ .literal = 0 }, // template params + .{ .literal = 0 }, // declaration + .{ .literal = 0 }, // retained nodes + .{ .literal = 0 }, // this adjustment + .{ .literal = 0 }, // thrown types + .{ .literal = 0 }, // annotations + .{ .literal = 0 }, // target function name + }; + + scope: Builder.Metadata, + name: Builder.MetadataString, + linkage_name: Builder.MetadataString, + file: Builder.Metadata, + line: u32, + ty: Builder.Metadata, + scope_line: u32, + sp_flags: Builder.Metadata.Subprogram.DISPFlags, + flags: Builder.Metadata.DIFlags, + compile_unit: Builder.Metadata, + }; + + pub const LexicalBlock = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 22 }, + .{ .literal = 0 }, // is distinct + MetadataAbbrev, // scope + MetadataAbbrev, // file + LineAbbrev, // line + ColumnAbbrev, // column + }; + + scope: Builder.Metadata, + file: Builder.Metadata, + line: u32, + column: u32, + }; + + pub const Location = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 7 }, + .{ .literal = 0 }, // is distinct + LineAbbrev, // line + ColumnAbbrev, // column + MetadataAbbrev, // scope + MetadataAbbrev, // inlined at + .{ .literal = 0 }, // is implicit code + }; + + line: u32, + column: u32, + scope: u32, + inlined_at: Builder.Metadata, + }; + + pub const BasicType = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 15 }, + .{ .literal = 0 }, // is distinct + .{ .literal = std.dwarf.TAG.base_type }, // tag + MetadataAbbrev, // name + .{ .vbr = 6 }, // size in bits + .{ .literal = 0 }, // align in bits + .{ .vbr = 8 }, // encoding + .{ .literal = 0 }, // flags + }; + + name: Builder.MetadataString, + size_in_bits: u64, + encoding: u32, + }; + + pub const CompositeType = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 18 }, + .{ .literal = 0 | 0x2 }, // is distinct | is not used in old type ref + .{ .fixed = 32 }, // tag + MetadataAbbrev, // name + MetadataAbbrev, // file + LineAbbrev, // line + MetadataAbbrev, // scope + MetadataAbbrev, // underlying type + .{ .vbr = 6 }, // size in bits + .{ .vbr = 6 }, // align in bits + .{ .literal = 0 }, // offset in bits + .{ .fixed = 32 }, // flags + MetadataAbbrev, // elements + .{ .literal = 0 }, // runtime lang + .{ .literal = 0 }, // vtable holder + .{ .literal = 0 }, // template params + .{ .literal = 0 }, // raw id + .{ .literal = 0 }, // discriminator + .{ .literal = 0 }, // data location + .{ .literal = 0 }, // associated + .{ .literal = 0 }, // allocated + .{ .literal = 0 }, // rank + .{ .literal = 0 }, // annotations + }; + + tag: u32, + name: Builder.MetadataString, + file: Builder.Metadata, + line: u32, + scope: Builder.Metadata, + underlying_type: Builder.Metadata, + size_in_bits: u64, + align_in_bits: u64, + flags: Builder.Metadata.DIFlags, + elements: Builder.Metadata, + }; + + pub const DerivedType = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 17 }, + .{ .literal = 0 }, // is distinct + .{ .fixed = 32 }, // tag + MetadataAbbrev, // name + MetadataAbbrev, // file + LineAbbrev, // line + MetadataAbbrev, // scope + MetadataAbbrev, // underlying type + .{ .vbr = 6 }, // size in bits + .{ .vbr = 6 }, // align in bits + .{ .vbr = 6 }, // offset in bits + .{ .literal = 0 }, // flags + .{ .literal = 0 }, // extra data + }; + + tag: u32, + name: Builder.MetadataString, + file: Builder.Metadata, + line: u32, + scope: Builder.Metadata, + underlying_type: Builder.Metadata, + size_in_bits: u64, + align_in_bits: u64, + offset_in_bits: u64, + }; + + pub const SubroutineType = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 19 }, + .{ .literal = 0 | 0x2 }, // is distinct | has no old type refs + .{ .literal = 0 }, // flags + MetadataAbbrev, // types + .{ .literal = 0 }, // cc + }; + + types: Builder.Metadata, + }; + + pub const Enumerator = struct { + pub const id = 14; + + pub const Flags = packed struct(u3) { + distinct: bool = false, + unsigned: bool, + bigint: bool, + }; + + pub const ops = [_]AbbrevOp{ + .{ .literal = Enumerator.id }, + .{ .fixed = @bitSizeOf(Flags) }, // flags + .{ .vbr = 6 }, // bit width + MetadataAbbrev, // name + .{ .vbr = 16 }, // integer value + }; + + flags: Flags, + bit_width: u32, + name: Builder.MetadataString, + value: u64, + }; + + pub const Subrange = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 13 }, + .{ .literal = 0b10 }, // is distinct | version + MetadataAbbrev, // count + MetadataAbbrev, // lower bound + .{ .literal = 0 }, // upper bound + .{ .literal = 0 }, // stride + }; + + count: Builder.Metadata, + lower_bound: Builder.Metadata, + }; + + pub const Expression = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 29 }, + .{ .literal = 0 | (3 << 1) }, // is distinct | version + MetadataArrayAbbrev, // elements + }; + + elements: []const u32, + }; + + pub const Node = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 3 }, + MetadataArrayAbbrev, // elements + }; + + elements: []const Builder.Metadata, + }; + + pub const LocalVar = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 28 }, + .{ .literal = 0b10 }, // is distinct | has alignment + MetadataAbbrev, // scope + MetadataAbbrev, // name + MetadataAbbrev, // file + LineAbbrev, // line + MetadataAbbrev, // type + .{ .literal = 0 }, // arg + .{ .literal = 0 }, // flags + .{ .literal = 0 }, // align bits + .{ .literal = 0 }, // annotations + }; + + scope: Builder.Metadata, + name: Builder.MetadataString, + file: Builder.Metadata, + line: u32, + ty: Builder.Metadata, + }; + + pub const Parameter = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 28 }, + .{ .literal = 0b10 }, // is distinct | has alignment + MetadataAbbrev, // scope + MetadataAbbrev, // name + MetadataAbbrev, // file + LineAbbrev, // line + MetadataAbbrev, // type + .{ .vbr = 4 }, // arg + .{ .literal = 0 }, // flags + .{ .literal = 0 }, // align bits + .{ .literal = 0 }, // annotations + }; + + scope: Builder.Metadata, + name: Builder.MetadataString, + file: Builder.Metadata, + line: u32, + ty: Builder.Metadata, + arg: u32, + }; + + pub const GlobalVar = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 27 }, + .{ .literal = 0b101 }, // is distinct | version + MetadataAbbrev, // scope + MetadataAbbrev, // name + MetadataAbbrev, // linkage name + MetadataAbbrev, // file + LineAbbrev, // line + MetadataAbbrev, // type + .{ .fixed = 1 }, // local + .{ .literal = 1 }, // defined + .{ .literal = 0 }, // static data members declaration + .{ .literal = 0 }, // template params + .{ .literal = 0 }, // align in bits + .{ .literal = 0 }, // annotations + }; + + scope: Builder.Metadata, + name: Builder.MetadataString, + linkage_name: Builder.MetadataString, + file: Builder.Metadata, + line: u32, + ty: Builder.Metadata, + local: bool, + }; + + pub const GlobalVarExpression = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 37 }, + .{ .literal = 0 }, // is distinct + MetadataAbbrev, // variable + MetadataAbbrev, // expression + }; + + variable: Builder.Metadata, + expression: Builder.Metadata, + }; + + pub const Constant = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 2 }, + MetadataAbbrev, // type + MetadataAbbrev, // value + }; + + ty: Builder.Type, + constant: Builder.Constant, + }; + + pub const Name = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 4 }, + .{ .array_fixed = 8 }, // name + }; + + name: []const u8, + }; + + pub const NamedNode = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 10 }, + MetadataArrayAbbrev, // elements + }; + + elements: []const Builder.Metadata, + }; + + pub const GlobalDeclAttachment = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 36 }, + ValueAbbrev, // value id + .{ .fixed = 1 }, // kind + MetadataAbbrev, // elements + }; + + value: Builder.Constant, + kind: MetadataKind, + metadata: Builder.Metadata, + }; +}; + +pub const FunctionMetadataBlock = struct { + pub const id = 15; + + pub const abbrevs = [_]type{ + Value, + }; + + pub const Value = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 2 }, + .{ .fixed = 32 }, // variable + .{ .fixed = 32 }, // expression + }; + + ty: Builder.Type, + value: Builder.Value, + }; +}; + +pub const FunctionBlock = struct { + pub const id = 12; + + pub const abbrevs = [_]type{ + DeclareBlocks, + Call, + CallFast, + FNeg, + FNegFast, + Binary, + BinaryFast, + Cmp, + CmpFast, + Select, + SelectFast, + Cast, + Alloca, + GetElementPtr, + ExtractValue, + InsertValue, + ExtractElement, + InsertElement, + ShuffleVector, + RetVoid, + Ret, + Unreachable, + Load, + LoadAtomic, + Store, + StoreAtomic, + BrUnconditional, + BrConditional, + VaArg, + AtomicRmw, + CmpXchg, + Fence, + DebugLoc, + DebugLocAgain, + }; + + pub const DeclareBlocks = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 1 }, + .{ .vbr = 8 }, + }; + num_blocks: usize, + }; + + pub const Call = struct { + pub const CallType = packed struct(u17) { + tail: bool = false, + call_conv: Builder.CallConv, + reserved: u3 = 0, + must_tail: bool = false, + // We always use the explicit type version as that is what LLVM does + explicit_type: bool = true, + no_tail: bool = false, + }; + pub const ops = [_]AbbrevOp{ + .{ .literal = 34 }, + .{ .fixed_runtime = Builder.FunctionAttributes }, + .{ .fixed = @bitSizeOf(CallType) }, + .{ .fixed_runtime = Builder.Type }, + ValueAbbrev, // Callee + ValueArrayAbbrev, // Args + }; + + attributes: Builder.FunctionAttributes, + call_type: CallType, + type_id: Builder.Type, + callee: Builder.Value, + args: []const Builder.Value, + }; + + pub const CallFast = struct { + const CallType = packed struct(u18) { + tail: bool = false, + call_conv: Builder.CallConv, + reserved: u3 = 0, + must_tail: bool = false, + // We always use the explicit type version as that is what LLVM does + explicit_type: bool = true, + no_tail: bool = false, + fast: bool = true, + }; + + pub const ops = [_]AbbrevOp{ + .{ .literal = 34 }, + .{ .fixed_runtime = Builder.FunctionAttributes }, + .{ .fixed = @bitSizeOf(CallType) }, + .{ .fixed = @bitSizeOf(Builder.FastMath) }, + .{ .fixed_runtime = Builder.Type }, + ValueAbbrev, // Callee + ValueArrayAbbrev, // Args + }; + + attributes: Builder.FunctionAttributes, + call_type: CallType, + fast_math: Builder.FastMath, + type_id: Builder.Type, + callee: Builder.Value, + args: []const Builder.Value, + }; + + pub const FNeg = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 56 }, + ValueAbbrev, + .{ .literal = 0 }, + }; + + val: u32, + }; + + pub const FNegFast = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 56 }, + ValueAbbrev, + .{ .literal = 0 }, + .{ .fixed = @bitSizeOf(Builder.FastMath) }, + }; + + val: u32, + fast_math: Builder.FastMath, + }; + + pub const Binary = struct { + const BinaryOpcode = Builder.BinaryOpcode; + pub const ops = [_]AbbrevOp{ + .{ .literal = 2 }, + ValueAbbrev, + ValueAbbrev, + .{ .fixed = @bitSizeOf(BinaryOpcode) }, + }; + + lhs: u32, + rhs: u32, + opcode: BinaryOpcode, + }; + + pub const BinaryFast = struct { + const BinaryOpcode = Builder.BinaryOpcode; + pub const ops = [_]AbbrevOp{ + .{ .literal = 2 }, + ValueAbbrev, + ValueAbbrev, + .{ .fixed = @bitSizeOf(BinaryOpcode) }, + .{ .fixed = @bitSizeOf(Builder.FastMath) }, + }; + + lhs: u32, + rhs: u32, + opcode: BinaryOpcode, + fast_math: Builder.FastMath, + }; + + pub const Cmp = struct { + const CmpPredicate = Builder.CmpPredicate; + pub const ops = [_]AbbrevOp{ + .{ .literal = 28 }, + ValueAbbrev, + ValueAbbrev, + .{ .fixed = @bitSizeOf(CmpPredicate) }, + }; + + lhs: u32, + rhs: u32, + pred: CmpPredicate, + }; + + pub const CmpFast = struct { + const CmpPredicate = Builder.CmpPredicate; + pub const ops = [_]AbbrevOp{ + .{ .literal = 28 }, + ValueAbbrev, + ValueAbbrev, + .{ .fixed = @bitSizeOf(CmpPredicate) }, + .{ .fixed = @bitSizeOf(Builder.FastMath) }, + }; + + lhs: u32, + rhs: u32, + pred: CmpPredicate, + fast_math: Builder.FastMath, + }; + + pub const Select = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 29 }, + ValueAbbrev, + ValueAbbrev, + ValueAbbrev, + }; + + lhs: u32, + rhs: u32, + cond: u32, + }; + + pub const SelectFast = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 29 }, + ValueAbbrev, + ValueAbbrev, + ValueAbbrev, + .{ .fixed = @bitSizeOf(Builder.FastMath) }, + }; + + lhs: u32, + rhs: u32, + cond: u32, + fast_math: Builder.FastMath, + }; + + pub const Cast = struct { + const CastOpcode = Builder.CastOpcode; + pub const ops = [_]AbbrevOp{ + .{ .literal = 3 }, + ValueAbbrev, + .{ .fixed_runtime = Builder.Type }, + .{ .fixed = @bitSizeOf(CastOpcode) }, + }; + + val: u32, + type_index: Builder.Type, + opcode: CastOpcode, + }; + + pub const Alloca = struct { + pub const Flags = packed struct(u11) { + align_lower: u5, + inalloca: bool, + explicit_type: bool, + swift_error: bool, + align_upper: u3, + }; + pub const ops = [_]AbbrevOp{ + .{ .literal = 19 }, + .{ .fixed_runtime = Builder.Type }, + .{ .fixed_runtime = Builder.Type }, + ValueAbbrev, + .{ .fixed = @bitSizeOf(Flags) }, + }; + + inst_type: Builder.Type, + len_type: Builder.Type, + len_value: u32, + flags: Flags, + }; + + pub const RetVoid = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 10 }, + }; + }; + + pub const Ret = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 10 }, + ValueAbbrev, + }; + val: u32, + }; + + pub const GetElementPtr = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 43 }, + .{ .fixed = 1 }, + .{ .fixed_runtime = Builder.Type }, + ValueAbbrev, + ValueArrayAbbrev, + }; + + is_inbounds: bool, + type_index: Builder.Type, + base: Builder.Value, + indices: []const Builder.Value, + }; + + pub const ExtractValue = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 26 }, + ValueAbbrev, + ValueArrayAbbrev, + }; + + val: u32, + indices: []const u32, + }; + + pub const InsertValue = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 27 }, + ValueAbbrev, + ValueAbbrev, + ValueArrayAbbrev, + }; + + val: u32, + elem: u32, + indices: []const u32, + }; + + pub const ExtractElement = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 6 }, + ValueAbbrev, + ValueAbbrev, + }; + + val: u32, + index: u32, + }; + + pub const InsertElement = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 7 }, + ValueAbbrev, + ValueAbbrev, + ValueAbbrev, + }; + + val: u32, + elem: u32, + index: u32, + }; + + pub const ShuffleVector = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 8 }, + ValueAbbrev, + ValueAbbrev, + ValueAbbrev, + }; + + lhs: u32, + rhs: u32, + mask: u32, + }; + + pub const Unreachable = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 15 }, + }; + }; + + pub const Load = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 20 }, + ValueAbbrev, + .{ .fixed_runtime = Builder.Type }, + .{ .fixed = @bitSizeOf(Builder.Alignment) }, + .{ .fixed = 1 }, + }; + ptr: u32, + ty: Builder.Type, + alignment: std.meta.Int(.unsigned, @bitSizeOf(Builder.Alignment)), + is_volatile: bool, + }; + + pub const LoadAtomic = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 41 }, + ValueAbbrev, + .{ .fixed_runtime = Builder.Type }, + .{ .fixed = @bitSizeOf(Builder.Alignment) }, + .{ .fixed = 1 }, + .{ .fixed = @bitSizeOf(Builder.AtomicOrdering) }, + .{ .fixed = @bitSizeOf(Builder.SyncScope) }, + }; + ptr: u32, + ty: Builder.Type, + alignment: std.meta.Int(.unsigned, @bitSizeOf(Builder.Alignment)), + is_volatile: bool, + success_ordering: Builder.AtomicOrdering, + sync_scope: Builder.SyncScope, + }; + + pub const Store = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 44 }, + ValueAbbrev, + ValueAbbrev, + .{ .fixed = @bitSizeOf(Builder.Alignment) }, + .{ .fixed = 1 }, + }; + ptr: u32, + val: u32, + alignment: std.meta.Int(.unsigned, @bitSizeOf(Builder.Alignment)), + is_volatile: bool, + }; + + pub const StoreAtomic = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 45 }, + ValueAbbrev, + ValueAbbrev, + .{ .fixed = @bitSizeOf(Builder.Alignment) }, + .{ .fixed = 1 }, + .{ .fixed = @bitSizeOf(Builder.AtomicOrdering) }, + .{ .fixed = @bitSizeOf(Builder.SyncScope) }, + }; + ptr: u32, + val: u32, + alignment: std.meta.Int(.unsigned, @bitSizeOf(Builder.Alignment)), + is_volatile: bool, + success_ordering: Builder.AtomicOrdering, + sync_scope: Builder.SyncScope, + }; + + pub const BrUnconditional = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 11 }, + BlockAbbrev, + }; + block: u32, + }; + + pub const BrConditional = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 11 }, + BlockAbbrev, + BlockAbbrev, + BlockAbbrev, + }; + then_block: u32, + else_block: u32, + condition: u32, + }; + + pub const VaArg = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 23 }, + .{ .fixed_runtime = Builder.Type }, + ValueAbbrev, + .{ .fixed_runtime = Builder.Type }, + }; + list_type: Builder.Type, + list: u32, + type: Builder.Type, + }; + + pub const AtomicRmw = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 59 }, + ValueAbbrev, + ValueAbbrev, + .{ .fixed = @bitSizeOf(Builder.Function.Instruction.AtomicRmw.Operation) }, + .{ .fixed = 1 }, + .{ .fixed = @bitSizeOf(Builder.AtomicOrdering) }, + .{ .fixed = @bitSizeOf(Builder.SyncScope) }, + .{ .fixed = @bitSizeOf(Builder.Alignment) }, + }; + ptr: u32, + val: u32, + operation: Builder.Function.Instruction.AtomicRmw.Operation, + is_volatile: bool, + success_ordering: Builder.AtomicOrdering, + sync_scope: Builder.SyncScope, + alignment: std.meta.Int(.unsigned, @bitSizeOf(Builder.Alignment)), + }; + + pub const CmpXchg = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 46 }, + ValueAbbrev, + ValueAbbrev, + ValueAbbrev, + .{ .fixed = 1 }, + .{ .fixed = @bitSizeOf(Builder.AtomicOrdering) }, + .{ .fixed = @bitSizeOf(Builder.SyncScope) }, + .{ .fixed = @bitSizeOf(Builder.AtomicOrdering) }, + .{ .fixed = 1 }, + .{ .fixed = @bitSizeOf(Builder.Alignment) }, + }; + ptr: u32, + cmp: u32, + new: u32, + is_volatile: bool, + success_ordering: Builder.AtomicOrdering, + sync_scope: Builder.SyncScope, + failure_ordering: Builder.AtomicOrdering, + is_weak: bool, + alignment: std.meta.Int(.unsigned, @bitSizeOf(Builder.Alignment)), + }; + + pub const Fence = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 36 }, + .{ .fixed = @bitSizeOf(Builder.AtomicOrdering) }, + .{ .fixed = @bitSizeOf(Builder.SyncScope) }, + }; + ordering: Builder.AtomicOrdering, + sync_scope: Builder.SyncScope, + }; + + pub const DebugLoc = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 35 }, + LineAbbrev, + ColumnAbbrev, + MetadataAbbrev, + MetadataAbbrev, + .{ .literal = 0 }, + }; + line: u32, + column: u32, + scope: Builder.Metadata, + inlined_at: Builder.Metadata, + }; + + pub const DebugLocAgain = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 33 }, + }; + }; +}; + +pub const FunctionValueSymbolTable = struct { + pub const id = 14; + + pub const abbrevs = [_]type{ + BlockEntry, + }; + + pub const BlockEntry = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 2 }, + ValueAbbrev, + .{ .array_fixed = 8 }, + }; + value_id: u32, + string: []const u8, + }; +}; + +pub const Strtab = struct { + pub const id = 23; + + pub const abbrevs = [_]type{Blob}; + + pub const Blob = struct { + pub const ops = [_]AbbrevOp{ + .{ .literal = 1 }, + .blob, + }; + blob: []const u8, + }; +}; diff --git a/src/codegen/spirv.zig b/src/codegen/spirv.zig index e1558e7f79..dc3b646ab7 100644 --- a/src/codegen/spirv.zig +++ b/src/codegen/spirv.zig @@ -629,6 +629,20 @@ const DeclGen = struct { return self.backingIntBits(ty) == null; } + /// Checks whether the type can be directly translated to SPIR-V vectors + fn isVector(self: *DeclGen, ty: Type) bool { + const mod = self.module; + const target = self.getTarget(); + if (ty.zigTypeTag(mod) != .Vector) return false; + const elem_ty = ty.childType(mod); + + const len = ty.vectorLen(mod); + const is_scalar = elem_ty.isNumeric(mod) or elem_ty.toIntern() == .bool_type; + const spirv_len = len > 1 and len <= 4; + const opencl_len = if (target.os.tag == .opencl) (len == 8 or len == 16) else false; + return is_scalar and (spirv_len or opencl_len); + } + fn arithmeticTypeInfo(self: *DeclGen, ty: Type) ArithmeticTypeInfo { const mod = self.module; const target = self.getTarget(); @@ -694,6 +708,24 @@ const DeclGen = struct { /// This function, unlike SpvModule.constInt, takes care to bitcast /// the value to an unsigned int first for Kernels. fn constInt(self: *DeclGen, ty_ref: CacheRef, value: anytype) !IdRef { + switch (self.spv.cache.lookup(ty_ref)) { + .vector_type => |vec_type| { + const elem_ids = try self.gpa.alloc(IdRef, vec_type.component_count); + defer self.gpa.free(elem_ids); + const int_value = try self.constInt(vec_type.component_type, value); + @memset(elem_ids, int_value); + + const constituents_id = self.spv.allocId(); + try self.func.body.emit(self.spv.gpa, .OpCompositeConstruct, .{ + .id_result_type = self.typeId(ty_ref), + .id_result = constituents_id, + .constituents = elem_ids, + }); + return constituents_id; + }, + else => {}, + } + if (value < 0) { const ty = self.spv.cache.lookup(ty_ref).int_type; // Manually truncate the value so that the resulting value @@ -711,6 +743,24 @@ const DeclGen = struct { /// Emits a float constant fn constFloat(self: *DeclGen, ty_ref: CacheRef, value: f128) !IdRef { + switch (self.spv.cache.lookup(ty_ref)) { + .vector_type => |vec_type| { + const elem_ids = try self.gpa.alloc(IdRef, vec_type.component_count); + defer self.gpa.free(elem_ids); + const int_value = try self.constFloat(vec_type.component_type, value); + @memset(elem_ids, int_value); + + const constituents_id = self.spv.allocId(); + try self.func.body.emit(self.spv.gpa, .OpCompositeConstruct, .{ + .id_result_type = self.typeId(ty_ref), + .id_result = constituents_id, + .constituents = elem_ids, + }); + return constituents_id; + }, + else => {}, + } + const ty = self.spv.cache.lookup(ty_ref).float_type; return switch (ty.bits) { 16 => try self.spv.resolveId(.{ .float = .{ .ty = ty_ref, .value = .{ .float16 = @floatCast(value) } } }), @@ -721,75 +771,18 @@ const DeclGen = struct { }; } - /// Construct a struct at runtime. - /// ty must be a struct type. - /// Constituents should be in `indirect` representation (as the elements of a struct should be). - /// Result is in `direct` representation. - fn constructStruct(self: *DeclGen, ty: Type, types: []const Type, constituents: []const IdRef) !IdRef { - assert(types.len == constituents.len); - // The Khronos LLVM-SPIRV translator crashes because it cannot construct structs which' - // operands are not constant. - // See https://github.com/KhronosGroup/SPIRV-LLVM-Translator/issues/1349 - // For now, just initialize the struct by setting the fields manually... - // TODO: Make this OpCompositeConstruct when we can - const ptr_composite_id = try self.alloc(ty, .{ .storage_class = .Function }); - for (constituents, types, 0..) |constitent_id, member_ty, index| { - const ptr_member_ty_ref = try self.ptrType(member_ty, .Function); - const ptr_id = try self.accessChain(ptr_member_ty_ref, ptr_composite_id, &.{@as(u32, @intCast(index))}); - try self.func.body.emit(self.spv.gpa, .OpStore, .{ - .pointer = ptr_id, - .object = constitent_id, - }); - } - return try self.load(ty, ptr_composite_id, .{}); - } - - /// Construct a vector at runtime. - /// ty must be an vector type. - /// Constituents should be in `indirect` representation (as the elements of an vector should be). - /// Result is in `direct` representation. - fn constructVector(self: *DeclGen, ty: Type, constituents: []const IdRef) !IdRef { - // The Khronos LLVM-SPIRV translator crashes because it cannot construct structs which' - // operands are not constant. - // See https://github.com/KhronosGroup/SPIRV-LLVM-Translator/issues/1349 - // For now, just initialize the struct by setting the fields manually... - // TODO: Make this OpCompositeConstruct when we can - const mod = self.module; - const ptr_composite_id = try self.alloc(ty, .{ .storage_class = .Function }); - const ptr_elem_ty_ref = try self.ptrType(ty.elemType2(mod), .Function); - for (constituents, 0..) |constitent_id, index| { - const ptr_id = try self.accessChain(ptr_elem_ty_ref, ptr_composite_id, &.{@as(u32, @intCast(index))}); - try self.func.body.emit(self.spv.gpa, .OpStore, .{ - .pointer = ptr_id, - .object = constitent_id, - }); - } - - return try self.load(ty, ptr_composite_id, .{}); - } - - /// Construct an array at runtime. - /// ty must be an array type. - /// Constituents should be in `indirect` representation (as the elements of an array should be). - /// Result is in `direct` representation. - fn constructArray(self: *DeclGen, ty: Type, constituents: []const IdRef) !IdRef { - // The Khronos LLVM-SPIRV translator crashes because it cannot construct structs which' - // operands are not constant. - // See https://github.com/KhronosGroup/SPIRV-LLVM-Translator/issues/1349 - // For now, just initialize the struct by setting the fields manually... - // TODO: Make this OpCompositeConstruct when we can - const mod = self.module; - const ptr_composite_id = try self.alloc(ty, .{ .storage_class = .Function }); - const ptr_elem_ty_ref = try self.ptrType(ty.elemType2(mod), .Function); - for (constituents, 0..) |constitent_id, index| { - const ptr_id = try self.accessChain(ptr_elem_ty_ref, ptr_composite_id, &.{@as(u32, @intCast(index))}); - try self.func.body.emit(self.spv.gpa, .OpStore, .{ - .pointer = ptr_id, - .object = constitent_id, - }); - } - - return try self.load(ty, ptr_composite_id, .{}); + /// Construct a composite value at runtime. If the parameters are in direct + /// representation, then the result is also in direct representation. Otherwise, + /// if the parameters are in indirect representation, then the result is too. + fn constructComposite(self: *DeclGen, ty: Type, constituents: []const IdRef) !IdRef { + const constituents_id = self.spv.allocId(); + const type_id = try self.resolveType(ty, .direct); + try self.func.body.emit(self.spv.gpa, .OpCompositeConstruct, .{ + .id_result_type = self.typeId(type_id), + .id_result = constituents_id, + .constituents = constituents, + }); + return constituents_id; } /// This function generates a load for a constant in direct (ie, non-memory) representation. @@ -897,18 +890,15 @@ const DeclGen = struct { }); var constituents: [2]IdRef = undefined; - var types: [2]Type = undefined; if (eu_layout.error_first) { constituents[0] = try self.constant(err_ty, err_val, .indirect); constituents[1] = try self.constant(payload_ty, payload_val, .indirect); - types = .{ err_ty, payload_ty }; } else { constituents[0] = try self.constant(payload_ty, payload_val, .indirect); constituents[1] = try self.constant(err_ty, err_val, .indirect); - types = .{ payload_ty, err_ty }; } - return try self.constructStruct(ty, &types, &constituents); + return try self.constructComposite(ty, &constituents); }, .enum_tag => { const int_val = try val.intFromEnum(ty, mod); @@ -920,11 +910,7 @@ const DeclGen = struct { const ptr_ty = ty.slicePtrFieldType(mod); const ptr_id = try self.constantPtr(ptr_ty, Value.fromInterned(slice.ptr)); const len_id = try self.constant(Type.usize, Value.fromInterned(slice.len), .indirect); - return self.constructStruct( - ty, - &.{ ptr_ty, Type.usize }, - &.{ ptr_id, len_id }, - ); + return self.constructComposite(ty, &.{ ptr_id, len_id }); }, .opt => { const payload_ty = ty.optionalChild(mod); @@ -951,11 +937,7 @@ const DeclGen = struct { else try self.spv.constUndef(try self.resolveType(payload_ty, .indirect)); - return try self.constructStruct( - ty, - &.{ payload_ty, Type.bool }, - &.{ payload_id, has_pl_id }, - ); + return try self.constructComposite(ty, &.{ payload_id, has_pl_id }); }, .aggregate => |aggregate| switch (ip.indexToKey(ty.ip_index)) { inline .array_type, .vector_type => |array_type, tag| { @@ -992,9 +974,9 @@ const DeclGen = struct { const sentinel = Value.fromInterned(array_type.sentinel); constituents[constituents.len - 1] = try self.constant(elem_ty, sentinel, .indirect); } - return self.constructArray(ty, constituents); + return self.constructComposite(ty, constituents); }, - inline .vector_type => return self.constructVector(ty, constituents), + inline .vector_type => return self.constructComposite(ty, constituents), else => unreachable, } }, @@ -1004,9 +986,6 @@ const DeclGen = struct { return self.todo("packed struct constants", .{}); } - var types = std.ArrayList(Type).init(self.gpa); - defer types.deinit(); - var constituents = std.ArrayList(IdRef).init(self.gpa); defer constituents.deinit(); @@ -1022,11 +1001,10 @@ const DeclGen = struct { const field_val = try val.fieldValue(mod, field_index); const field_id = try self.constant(field_ty, field_val, .indirect); - try types.append(field_ty); try constituents.append(field_id); } - return try self.constructStruct(ty, types.items, constituents.items); + return try self.constructComposite(ty, constituents.items); }, .anon_struct_type => unreachable, // TODO else => unreachable, @@ -1520,12 +1498,11 @@ const DeclGen = struct { const elem_ty = ty.childType(mod); const elem_ty_ref = try self.resolveType(elem_ty, .indirect); const len = ty.vectorLen(mod); - const is_scalar = elem_ty.isNumeric(mod) or elem_ty.toIntern() == .bool_type; - const ty_ref = if (is_scalar and len > 1 and len <= 4) - try self.spv.vectorType(ty.vectorLen(mod), elem_ty_ref) + const ty_ref = if (self.isVector(ty)) + try self.spv.vectorType(len, elem_ty_ref) else - try self.spv.arrayType(ty.vectorLen(mod), elem_ty_ref); + try self.spv.arrayType(len, elem_ty_ref); try self.type_map.put(self.gpa, ty.toIntern(), .{ .ty_ref = ty_ref }); return ty_ref; @@ -1824,18 +1801,16 @@ const DeclGen = struct { } /// This structure is used as helper for element-wise operations. It is intended - /// to be used with both vectors and single elements. + /// to be used with vectors, fake vectors (arrays) and single elements. const WipElementWise = struct { dg: *DeclGen, result_ty: Type, + ty: Type, /// Always in direct representation. - result_ty_ref: CacheRef, - scalar_ty: Type, - /// Always in direct representation. - scalar_ty_ref: CacheRef, - scalar_ty_id: IdRef, - /// True if the input is actually a vector type. - is_vector: bool, + ty_ref: CacheRef, + ty_id: IdRef, + /// True if the input is an array type. + is_array: bool, /// The element-wise operation should fill these results before calling finalize(). /// These should all be in **direct** representation! `finalize()` will convert /// them to indirect if required. @@ -1846,31 +1821,30 @@ const DeclGen = struct { } /// Utility function to extract the element at a particular index in an - /// input vector. This type is expected to be a vector if `wip.is_vector`, and - /// a scalar otherwise. + /// input array. This type is expected to be a fake vector (array) if `wip.is_array`, and + /// a vector or scalar otherwise. fn elementAt(wip: WipElementWise, ty: Type, value: IdRef, index: usize) !IdRef { const mod = wip.dg.module; - if (wip.is_vector) { + if (wip.is_array) { assert(ty.isVector(mod)); return try wip.dg.extractField(ty.childType(mod), value, @intCast(index)); } else { - assert(!ty.isVector(mod)); assert(index == 0); return value; } } - /// Turns the results of this WipElementWise into a result. This can either - /// be a vector or single element, depending on `result_ty`. + /// Turns the results of this WipElementWise into a result. This can be + /// vectors, fake vectors (arrays) and single elements, depending on `result_ty`. /// After calling this function, this WIP is no longer usable. /// Results is in `direct` representation. fn finalize(wip: *WipElementWise) !IdRef { - if (wip.is_vector) { + if (wip.is_array) { // Convert all the constituents to indirect, as required for the array. for (wip.results) |*result| { - result.* = try wip.dg.convertToIndirect(wip.scalar_ty, result.*); + result.* = try wip.dg.convertToIndirect(wip.ty, result.*); } - return try wip.dg.constructArray(wip.result_ty, wip.results); + return try wip.dg.constructComposite(wip.result_ty, wip.results); } else { return wip.results[0]; } @@ -1878,33 +1852,30 @@ const DeclGen = struct { /// Allocate a result id at a particular index, and return it. fn allocId(wip: *WipElementWise, index: usize) IdRef { - assert(wip.is_vector or index == 0); + assert(wip.is_array or index == 0); wip.results[index] = wip.dg.spv.allocId(); return wip.results[index]; } }; /// Create a new element-wise operation. - fn elementWise(self: *DeclGen, result_ty: Type) !WipElementWise { + fn elementWise(self: *DeclGen, result_ty: Type, force_element_wise: bool) !WipElementWise { const mod = self.module; - // For now, this operation also reasons in terms of `.direct` representation. - const result_ty_ref = try self.resolveType(result_ty, .direct); - const is_vector = result_ty.isVector(mod); - const num_results = if (is_vector) result_ty.vectorLen(mod) else 1; + const is_array = result_ty.isVector(mod) and (!self.isVector(result_ty) or force_element_wise); + const num_results = if (is_array) result_ty.vectorLen(mod) else 1; const results = try self.gpa.alloc(IdRef, num_results); - for (results) |*result| result.* = undefined; + @memset(results, undefined); - const scalar_ty = result_ty.scalarType(mod); - const scalar_ty_ref = try self.resolveType(scalar_ty, .direct); + const ty = if (is_array) result_ty.scalarType(mod) else result_ty; + const ty_ref = try self.resolveType(ty, .direct); return .{ .dg = self, .result_ty = result_ty, - .result_ty_ref = result_ty_ref, - .scalar_ty = scalar_ty, - .scalar_ty_ref = scalar_ty_ref, - .scalar_ty_id = self.typeId(scalar_ty_ref), - .is_vector = is_vector, + .ty = ty, + .ty_ref = ty_ref, + .ty_id = self.typeId(ty_ref), + .is_array = is_array, .results = results, }; } @@ -2001,7 +1972,10 @@ const DeclGen = struct { try self.func.prologue.emit(self.spv.gpa, .OpFunction, .{ .id_result_type = self.typeId(return_ty_ref), .id_result = decl_id, - .function_control = .{}, // TODO: We can set inline here if the type requires it. + .function_control = switch (fn_info.cc) { + .Inline => .{ .Inline = true }, + else => .{}, + }, .function_type = prototype_id, }); @@ -2045,7 +2019,7 @@ const DeclGen = struct { // Append the actual code into the functions section. try self.spv.addFunction(spv_decl_index, self.func); - const fqn = ip.stringToSlice(try decl.getFullyQualifiedName(self.module)); + const fqn = ip.stringToSlice(try decl.fullyQualifiedName(self.module)); try self.spv.debugName(decl_id, fqn); // Temporarily generate a test kernel declaration if this is a test function. @@ -2081,7 +2055,7 @@ const DeclGen = struct { .id_result = decl_id, .storage_class = actual_storage_class, }); - const fqn = ip.stringToSlice(try decl.getFullyQualifiedName(self.module)); + const fqn = ip.stringToSlice(try decl.fullyQualifiedName(self.module)); try self.spv.debugName(decl_id, fqn); if (opt_init_val) |init_val| { @@ -2232,7 +2206,6 @@ const DeclGen = struct { fn genInst(self: *DeclGen, inst: Air.Inst.Index) !void { const mod = self.module; const ip = &mod.intern_pool; - // TODO: remove now-redundant isUnused calls from AIR handler functions if (self.liveness.isUnused(inst) and !self.air.mustLower(inst, ip)) return; @@ -2384,11 +2357,11 @@ const DeclGen = struct { } fn binOpSimple(self: *DeclGen, ty: Type, lhs_id: IdRef, rhs_id: IdRef, comptime opcode: Opcode) !IdRef { - var wip = try self.elementWise(ty); + var wip = try self.elementWise(ty, false); defer wip.deinit(); for (0..wip.results.len) |i| { try self.func.body.emit(self.spv.gpa, opcode, .{ - .id_result_type = wip.scalar_ty_id, + .id_result_type = wip.ty_id, .id_result = wip.allocId(i), .operand_1 = try wip.elementAt(ty, lhs_id, i), .operand_2 = try wip.elementAt(ty, rhs_id, i), @@ -2398,8 +2371,6 @@ const DeclGen = struct { } fn airBinOpSimple(self: *DeclGen, inst: Air.Inst.Index, comptime opcode: Opcode) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; const lhs_id = try self.resolve(bin_op.lhs); const rhs_id = try self.resolve(bin_op.rhs); @@ -2409,7 +2380,6 @@ const DeclGen = struct { } fn airShift(self: *DeclGen, inst: Air.Inst.Index, comptime unsigned: Opcode, comptime signed: Opcode) !?IdRef { - if (self.liveness.isUnused(inst)) return null; const mod = self.module; const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; const lhs_id = try self.resolve(bin_op.lhs); @@ -2417,7 +2387,7 @@ const DeclGen = struct { const result_ty = self.typeOfIndex(inst); const shift_ty = self.typeOf(bin_op.rhs); - const scalar_shift_ty_ref = try self.resolveType(shift_ty.scalarType(mod), .direct); + const shift_ty_ref = try self.resolveType(shift_ty, .direct); const info = self.arithmeticTypeInfo(result_ty); switch (info.class) { @@ -2426,7 +2396,7 @@ const DeclGen = struct { .float, .bool => unreachable, } - var wip = try self.elementWise(result_ty); + var wip = try self.elementWise(result_ty, false); defer wip.deinit(); for (wip.results, 0..) |*result_id, i| { const lhs_elem_id = try wip.elementAt(result_ty, lhs_id, i); @@ -2434,10 +2404,10 @@ const DeclGen = struct { // Sometimes Zig doesn't make both of the arguments the same types here. SPIR-V expects that, // so just manually upcast it if required. - const shift_id = if (scalar_shift_ty_ref != wip.scalar_ty_ref) blk: { + const shift_id = if (shift_ty_ref != wip.ty_ref) blk: { const shift_id = self.spv.allocId(); try self.func.body.emit(self.spv.gpa, .OpUConvert, .{ - .id_result_type = wip.scalar_ty_id, + .id_result_type = wip.ty_id, .id_result = shift_id, .unsigned_value = rhs_elem_id, }); @@ -2446,7 +2416,7 @@ const DeclGen = struct { const value_id = self.spv.allocId(); const args = .{ - .id_result_type = wip.scalar_ty_id, + .id_result_type = wip.ty_id, .id_result = value_id, .base = lhs_elem_id, .shift = shift_id, @@ -2458,14 +2428,12 @@ const DeclGen = struct { try self.func.body.emit(self.spv.gpa, unsigned, args); } - result_id.* = try self.normalize(wip.scalar_ty_ref, value_id, info); + result_id.* = try self.normalize(wip.ty_ref, value_id, info); } return try wip.finalize(); } fn airMinMax(self: *DeclGen, inst: Air.Inst.Index, op: std.math.CompareOperator) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; const lhs_id = try self.resolve(bin_op.lhs); const rhs_id = try self.resolve(bin_op.rhs); @@ -2476,48 +2444,71 @@ const DeclGen = struct { fn minMax(self: *DeclGen, result_ty: Type, op: std.math.CompareOperator, lhs_id: IdRef, rhs_id: IdRef) !IdRef { const info = self.arithmeticTypeInfo(result_ty); + const target = self.getTarget(); - var wip = try self.elementWise(result_ty); + const use_backup_codegen = target.os.tag == .opencl and info.class != .float; + var wip = try self.elementWise(result_ty, use_backup_codegen); defer wip.deinit(); + for (wip.results, 0..) |*result_id, i| { const lhs_elem_id = try wip.elementAt(result_ty, lhs_id, i); const rhs_elem_id = try wip.elementAt(result_ty, rhs_id, i); - // TODO: Use fmin for OpenCL - const cmp_id = try self.cmp(op, Type.bool, wip.scalar_ty, lhs_elem_id, rhs_elem_id); - const selection_id = switch (info.class) { - .float => blk: { - // cmp uses OpFOrd. When we have 0 [<>] nan this returns false, - // but we want it to pick lhs. Therefore we also have to check if - // rhs is nan. We don't need to care about the result when both - // are nan. - const rhs_is_nan_id = self.spv.allocId(); - const bool_ty_ref = try self.resolveType(Type.bool, .direct); - try self.func.body.emit(self.spv.gpa, .OpIsNan, .{ - .id_result_type = self.typeId(bool_ty_ref), - .id_result = rhs_is_nan_id, - .x = rhs_elem_id, - }); - const float_cmp_id = self.spv.allocId(); - try self.func.body.emit(self.spv.gpa, .OpLogicalOr, .{ - .id_result_type = self.typeId(bool_ty_ref), - .id_result = float_cmp_id, - .operand_1 = cmp_id, - .operand_2 = rhs_is_nan_id, - }); - break :blk float_cmp_id; - }, - else => cmp_id, - }; + if (use_backup_codegen) { + const cmp_id = try self.cmp(op, Type.bool, wip.ty, lhs_elem_id, rhs_elem_id); + result_id.* = self.spv.allocId(); + try self.func.body.emit(self.spv.gpa, .OpSelect, .{ + .id_result_type = wip.ty_id, + .id_result = result_id.*, + .condition = cmp_id, + .object_1 = lhs_elem_id, + .object_2 = rhs_elem_id, + }); + } else { + const ext_inst: Word = switch (target.os.tag) { + .opencl => switch (op) { + .lt => 28, // fmin + .gt => 27, // fmax + else => unreachable, + }, + .vulkan => switch (info.class) { + .float => switch (op) { + .lt => 37, // FMin + .gt => 40, // FMax + else => unreachable, + }, + .integer, .strange_integer => switch (info.signedness) { + .signed => switch (op) { + .lt => 39, // SMin + .gt => 42, // SMax + else => unreachable, + }, + .unsigned => switch (op) { + .lt => 38, // UMin + .gt => 41, // UMax + else => unreachable, + }, + }, + .composite_integer => unreachable, // TODO + .bool => unreachable, + }, + else => unreachable, + }; + const set_id = switch (target.os.tag) { + .opencl => try self.spv.importInstructionSet(.opencl), + .vulkan => try self.spv.importInstructionSet(.glsl), + else => unreachable, + }; - result_id.* = self.spv.allocId(); - try self.func.body.emit(self.spv.gpa, .OpSelect, .{ - .id_result_type = wip.scalar_ty_id, - .id_result = result_id.*, - .condition = selection_id, - .object_1 = lhs_elem_id, - .object_2 = rhs_elem_id, - }); + result_id.* = self.spv.allocId(); + try self.func.body.emit(self.spv.gpa, .OpExtInst, .{ + .id_result_type = wip.ty_id, + .id_result = result_id.*, + .set = set_id, + .instruction = .{ .inst = ext_inst }, + .id_ref_4 = &.{ lhs_elem_id, rhs_elem_id }, + }); + } } return wip.finalize(); } @@ -2577,7 +2568,6 @@ const DeclGen = struct { comptime sop: Opcode, comptime uop: Opcode, ) !?IdRef { - if (self.liveness.isUnused(inst)) return null; // LHS and RHS are guaranteed to have the same type, and AIR guarantees // the result to be the same as the LHS and RHS, which matches SPIR-V. @@ -2617,7 +2607,7 @@ const DeclGen = struct { .bool => unreachable, }; - var wip = try self.elementWise(ty); + var wip = try self.elementWise(ty, false); defer wip.deinit(); for (wip.results, 0..) |*result_id, i| { const lhs_elem_id = try wip.elementAt(ty, lhs_id, i); @@ -2625,7 +2615,7 @@ const DeclGen = struct { const value_id = self.spv.allocId(); const operands = .{ - .id_result_type = wip.scalar_ty_id, + .id_result_type = wip.ty_id, .id_result = value_id, .operand_1 = lhs_elem_id, .operand_2 = rhs_elem_id, @@ -2640,66 +2630,59 @@ const DeclGen = struct { // TODO: Trap on overflow? Probably going to be annoying. // TODO: Look into SPV_KHR_no_integer_wrap_decoration which provides NoSignedWrap/NoUnsignedWrap. - result_id.* = try self.normalize(wip.scalar_ty_ref, value_id, info); + result_id.* = try self.normalize(wip.ty_ref, value_id, info); } return try wip.finalize(); } fn airAbs(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - - const mod = self.module; + const target = self.getTarget(); const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const operand_id = try self.resolve(ty_op.operand); // Note: operand_ty may be signed, while ty is always unsigned! const operand_ty = self.typeOf(ty_op.operand); - const ty = self.typeOfIndex(inst); - const info = self.arithmeticTypeInfo(ty); - const operand_scalar_ty = operand_ty.scalarType(mod); - const operand_scalar_ty_ref = try self.resolveType(operand_scalar_ty, .direct); + const result_ty = self.typeOfIndex(inst); + const operand_info = self.arithmeticTypeInfo(operand_ty); - var wip = try self.elementWise(ty); + var wip = try self.elementWise(result_ty, false); defer wip.deinit(); - const zero_id = switch (info.class) { - .float => try self.constFloat(operand_scalar_ty_ref, 0), - .integer, .strange_integer => try self.constInt(operand_scalar_ty_ref, 0), - .composite_integer => unreachable, // TODO - .bool => unreachable, - }; for (wip.results, 0..) |*result_id, i| { const elem_id = try wip.elementAt(operand_ty, operand_id, i); - // Idk why spir-v doesn't have a dedicated abs() instruction in the base - // instruction set. For now we're just going to negate and check to avoid - // importing the extinst. - // TODO: Make this a call to compiler rt / ext inst - const neg_id = self.spv.allocId(); - const args = .{ - .id_result_type = self.typeId(operand_scalar_ty_ref), - .id_result = neg_id, - .operand_1 = zero_id, - .operand_2 = elem_id, + + const ext_inst: Word = switch (target.os.tag) { + .opencl => switch (operand_info.class) { + .float => 23, // fabs + .integer, .strange_integer => switch (operand_info.signedness) { + .signed => 141, // s_abs + .unsigned => 201, // u_abs + }, + .composite_integer => unreachable, // TODO + .bool => unreachable, + }, + .vulkan => switch (operand_info.class) { + .float => 4, // FAbs + .integer, .strange_integer => 5, // SAbs + .composite_integer => unreachable, // TODO + .bool => unreachable, + }, + else => unreachable, }; - switch (info.class) { - .float => try self.func.body.emit(self.spv.gpa, .OpFSub, args), - .integer, .strange_integer => try self.func.body.emit(self.spv.gpa, .OpISub, args), - .composite_integer => unreachable, // TODO - .bool => unreachable, - } - const neg_norm_id = try self.normalize(wip.scalar_ty_ref, neg_id, info); - - const gt_zero_id = try self.cmp(.gt, Type.bool, operand_scalar_ty, elem_id, zero_id); - const abs_id = self.spv.allocId(); - try self.func.body.emit(self.spv.gpa, .OpSelect, .{ - .id_result_type = self.typeId(operand_scalar_ty_ref), - .id_result = abs_id, - .condition = gt_zero_id, - .object_1 = elem_id, - .object_2 = neg_norm_id, + const set_id = switch (target.os.tag) { + .opencl => try self.spv.importInstructionSet(.opencl), + .vulkan => try self.spv.importInstructionSet(.glsl), + else => unreachable, + }; + + result_id.* = self.spv.allocId(); + try self.func.body.emit(self.spv.gpa, .OpExtInst, .{ + .id_result_type = wip.ty_id, + .id_result = result_id.*, + .set = set_id, + .instruction = .{ .inst = ext_inst }, + .id_ref_4 = &.{elem_id}, }); - // For Shader, we may need to cast from signed to unsigned here. - result_id.* = try self.bitCast(wip.scalar_ty, operand_scalar_ty, abs_id); } return try wip.finalize(); } @@ -2711,8 +2694,7 @@ const DeclGen = struct { comptime ucmp: Opcode, comptime scmp: Opcode, ) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - + const mod = self.module; const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; const lhs = try self.resolve(extra.lhs); @@ -2723,6 +2705,10 @@ const DeclGen = struct { const ov_ty = result_ty.structFieldType(1, self.module); const bool_ty_ref = try self.resolveType(Type.bool, .direct); + const cmp_ty_ref = if (self.isVector(operand_ty)) + try self.spv.vectorType(operand_ty.vectorLen(mod), bool_ty_ref) + else + bool_ty_ref; const info = self.arithmeticTypeInfo(operand_ty); switch (info.class) { @@ -2731,9 +2717,9 @@ const DeclGen = struct { .float, .bool => unreachable, } - var wip_result = try self.elementWise(operand_ty); + var wip_result = try self.elementWise(operand_ty, false); defer wip_result.deinit(); - var wip_ov = try self.elementWise(ov_ty); + var wip_ov = try self.elementWise(ov_ty, false); defer wip_ov.deinit(); for (wip_result.results, wip_ov.results, 0..) |*result_id, *ov_id, i| { const lhs_elem_id = try wip_result.elementAt(operand_ty, lhs, i); @@ -2743,14 +2729,14 @@ const DeclGen = struct { const value_id = self.spv.allocId(); try self.func.body.emit(self.spv.gpa, add, .{ - .id_result_type = wip_result.scalar_ty_id, + .id_result_type = wip_result.ty_id, .id_result = value_id, .operand_1 = lhs_elem_id, .operand_2 = rhs_elem_id, }); // Normalize the result so that the comparisons go well - result_id.* = try self.normalize(wip_result.scalar_ty_ref, value_id, info); + result_id.* = try self.normalize(wip_result.ty_ref, value_id, info); const overflowed_id = switch (info.signedness) { .unsigned => blk: { @@ -2758,7 +2744,7 @@ const DeclGen = struct { // For subtraction the conditions need to be swapped. const overflowed_id = self.spv.allocId(); try self.func.body.emit(self.spv.gpa, ucmp, .{ - .id_result_type = self.typeId(bool_ty_ref), + .id_result_type = self.typeId(cmp_ty_ref), .id_result = overflowed_id, .operand_1 = result_id.*, .operand_2 = lhs_elem_id, @@ -2784,9 +2770,9 @@ const DeclGen = struct { // = (rhs < 0) == (lhs > value) const rhs_lt_zero_id = self.spv.allocId(); - const zero_id = try self.constInt(wip_result.scalar_ty_ref, 0); + const zero_id = try self.constInt(wip_result.ty_ref, 0); try self.func.body.emit(self.spv.gpa, .OpSLessThan, .{ - .id_result_type = self.typeId(bool_ty_ref), + .id_result_type = self.typeId(cmp_ty_ref), .id_result = rhs_lt_zero_id, .operand_1 = rhs_elem_id, .operand_2 = zero_id, @@ -2794,7 +2780,7 @@ const DeclGen = struct { const value_gt_lhs_id = self.spv.allocId(); try self.func.body.emit(self.spv.gpa, scmp, .{ - .id_result_type = self.typeId(bool_ty_ref), + .id_result_type = self.typeId(cmp_ty_ref), .id_result = value_gt_lhs_id, .operand_1 = lhs_elem_id, .operand_2 = result_id.*, @@ -2802,7 +2788,7 @@ const DeclGen = struct { const overflowed_id = self.spv.allocId(); try self.func.body.emit(self.spv.gpa, .OpLogicalEqual, .{ - .id_result_type = self.typeId(bool_ty_ref), + .id_result_type = self.typeId(cmp_ty_ref), .id_result = overflowed_id, .operand_1 = rhs_lt_zero_id, .operand_2 = value_gt_lhs_id, @@ -2811,18 +2797,16 @@ const DeclGen = struct { }, }; - ov_id.* = try self.intFromBool(wip_ov.scalar_ty_ref, overflowed_id); + ov_id.* = try self.intFromBool(wip_ov.ty_ref, overflowed_id); } - return try self.constructStruct( + return try self.constructComposite( result_ty, - &.{ operand_ty, ov_ty }, &.{ try wip_result.finalize(), try wip_ov.finalize() }, ); } fn airShlOverflow(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; const mod = self.module; const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; @@ -2832,11 +2816,15 @@ const DeclGen = struct { const result_ty = self.typeOfIndex(inst); const operand_ty = self.typeOf(extra.lhs); const shift_ty = self.typeOf(extra.rhs); - const scalar_shift_ty_ref = try self.resolveType(shift_ty.scalarType(mod), .direct); + const shift_ty_ref = try self.resolveType(shift_ty, .direct); const ov_ty = result_ty.structFieldType(1, self.module); const bool_ty_ref = try self.resolveType(Type.bool, .direct); + const cmp_ty_ref = if (self.isVector(operand_ty)) + try self.spv.vectorType(operand_ty.vectorLen(mod), bool_ty_ref) + else + bool_ty_ref; const info = self.arithmeticTypeInfo(operand_ty); switch (info.class) { @@ -2845,9 +2833,9 @@ const DeclGen = struct { .float, .bool => unreachable, } - var wip_result = try self.elementWise(operand_ty); + var wip_result = try self.elementWise(operand_ty, false); defer wip_result.deinit(); - var wip_ov = try self.elementWise(ov_ty); + var wip_ov = try self.elementWise(ov_ty, false); defer wip_ov.deinit(); for (wip_result.results, wip_ov.results, 0..) |*result_id, *ov_id, i| { const lhs_elem_id = try wip_result.elementAt(operand_ty, lhs, i); @@ -2855,10 +2843,10 @@ const DeclGen = struct { // Sometimes Zig doesn't make both of the arguments the same types here. SPIR-V expects that, // so just manually upcast it if required. - const shift_id = if (scalar_shift_ty_ref != wip_result.scalar_ty_ref) blk: { + const shift_id = if (shift_ty_ref != wip_result.ty_ref) blk: { const shift_id = self.spv.allocId(); try self.func.body.emit(self.spv.gpa, .OpUConvert, .{ - .id_result_type = wip_result.scalar_ty_id, + .id_result_type = wip_result.ty_id, .id_result = shift_id, .unsigned_value = rhs_elem_id, }); @@ -2867,18 +2855,18 @@ const DeclGen = struct { const value_id = self.spv.allocId(); try self.func.body.emit(self.spv.gpa, .OpShiftLeftLogical, .{ - .id_result_type = wip_result.scalar_ty_id, + .id_result_type = wip_result.ty_id, .id_result = value_id, .base = lhs_elem_id, .shift = shift_id, }); - result_id.* = try self.normalize(wip_result.scalar_ty_ref, value_id, info); + result_id.* = try self.normalize(wip_result.ty_ref, value_id, info); const right_shift_id = self.spv.allocId(); switch (info.signedness) { .signed => { try self.func.body.emit(self.spv.gpa, .OpShiftRightArithmetic, .{ - .id_result_type = wip_result.scalar_ty_id, + .id_result_type = wip_result.ty_id, .id_result = right_shift_id, .base = result_id.*, .shift = shift_id, @@ -2886,7 +2874,7 @@ const DeclGen = struct { }, .unsigned => { try self.func.body.emit(self.spv.gpa, .OpShiftRightLogical, .{ - .id_result_type = wip_result.scalar_ty_id, + .id_result_type = wip_result.ty_id, .id_result = right_shift_id, .base = result_id.*, .shift = shift_id, @@ -2896,25 +2884,22 @@ const DeclGen = struct { const overflowed_id = self.spv.allocId(); try self.func.body.emit(self.spv.gpa, .OpINotEqual, .{ - .id_result_type = self.typeId(bool_ty_ref), + .id_result_type = self.typeId(cmp_ty_ref), .id_result = overflowed_id, .operand_1 = lhs_elem_id, .operand_2 = right_shift_id, }); - ov_id.* = try self.intFromBool(wip_ov.scalar_ty_ref, overflowed_id); + ov_id.* = try self.intFromBool(wip_ov.ty_ref, overflowed_id); } - return try self.constructStruct( + return try self.constructComposite( result_ty, - &.{ operand_ty, ov_ty }, &.{ try wip_result.finalize(), try wip_ov.finalize() }, ); } fn airMulAdd(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; const extra = self.air.extraData(Air.Bin, pl_op.payload).data; @@ -2927,19 +2912,19 @@ const DeclGen = struct { const info = self.arithmeticTypeInfo(ty); assert(info.class == .float); // .mul_add is only emitted for floats - var wip = try self.elementWise(ty); + var wip = try self.elementWise(ty, false); defer wip.deinit(); for (0..wip.results.len) |i| { const mul_result = self.spv.allocId(); try self.func.body.emit(self.spv.gpa, .OpFMul, .{ - .id_result_type = wip.scalar_ty_id, + .id_result_type = wip.ty_id, .id_result = mul_result, .operand_1 = try wip.elementAt(ty, mulend1, i), .operand_2 = try wip.elementAt(ty, mulend2, i), }); try self.func.body.emit(self.spv.gpa, .OpFAdd, .{ - .id_result_type = wip.scalar_ty_id, + .id_result_type = wip.ty_id, .id_result = wip.allocId(i), .operand_1 = mul_result, .operand_2 = try wip.elementAt(ty, addend, i), @@ -2949,20 +2934,16 @@ const DeclGen = struct { } fn airSplat(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const operand_id = try self.resolve(ty_op.operand); const result_ty = self.typeOfIndex(inst); - var wip = try self.elementWise(result_ty); + var wip = try self.elementWise(result_ty, true); defer wip.deinit(); - for (wip.results) |*result_id| { - result_id.* = operand_id; - } + @memset(wip.results, operand_id); return try wip.finalize(); } fn airReduce(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; const mod = self.module; const reduce = self.air.instructions.items(.data)[@intFromEnum(inst)].reduce; const operand = try self.resolve(reduce.operand); @@ -3030,7 +3011,6 @@ const DeclGen = struct { fn airShuffle(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { const mod = self.module; - if (self.liveness.isUnused(inst)) return null; const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const extra = self.air.extraData(Air.Shuffle, ty_pl.payload).data; const a = try self.resolve(extra.a); @@ -3039,20 +3019,20 @@ const DeclGen = struct { const ty = self.typeOfIndex(inst); - var wip = try self.elementWise(ty); + var wip = try self.elementWise(ty, true); defer wip.deinit(); for (wip.results, 0..) |*result_id, i| { const elem = try mask.elemValue(mod, i); if (elem.isUndef(mod)) { - result_id.* = try self.spv.constUndef(wip.scalar_ty_ref); + result_id.* = try self.spv.constUndef(wip.ty_ref); continue; } const index = elem.toSignedInt(mod); if (index >= 0) { - result_id.* = try self.extractField(wip.scalar_ty, a, @intCast(index)); + result_id.* = try self.extractField(wip.ty, a, @intCast(index)); } else { - result_id.* = try self.extractField(wip.scalar_ty, b, @intCast(~index)); + result_id.* = try self.extractField(wip.ty, b, @intCast(~index)); } } return try wip.finalize(); @@ -3143,7 +3123,6 @@ const DeclGen = struct { } fn airPtrAdd(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; const ptr_id = try self.resolve(bin_op.lhs); @@ -3155,7 +3134,6 @@ const DeclGen = struct { } fn airPtrSub(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; const ptr_id = try self.resolve(bin_op.lhs); @@ -3262,7 +3240,7 @@ const DeclGen = struct { return result_id; }, .Vector => { - var wip = try self.elementWise(result_ty); + var wip = try self.elementWise(result_ty, true); defer wip.deinit(); const scalar_ty = ty.scalarType(mod); for (wip.results, 0..) |*result_id, i| { @@ -3331,7 +3309,6 @@ const DeclGen = struct { inst: Air.Inst.Index, comptime op: std.math.CompareOperator, ) !?IdRef { - if (self.liveness.isUnused(inst)) return null; const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; const lhs_id = try self.resolve(bin_op.lhs); const rhs_id = try self.resolve(bin_op.rhs); @@ -3342,8 +3319,6 @@ const DeclGen = struct { } fn airVectorCmp(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const vec_cmp = self.air.extraData(Air.VectorCmp, ty_pl.payload).data; const lhs_id = try self.resolve(vec_cmp.lhs); @@ -3425,7 +3400,6 @@ const DeclGen = struct { } fn airBitCast(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const operand_id = try self.resolve(ty_op.operand); const operand_ty = self.typeOf(ty_op.operand); @@ -3434,8 +3408,6 @@ const DeclGen = struct { } fn airIntCast(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const operand_id = try self.resolve(ty_op.operand); const src_ty = self.typeOf(ty_op.operand); @@ -3448,19 +3420,19 @@ const DeclGen = struct { return operand_id; } - var wip = try self.elementWise(dst_ty); + var wip = try self.elementWise(dst_ty, false); defer wip.deinit(); for (wip.results, 0..) |*result_id, i| { const elem_id = try wip.elementAt(src_ty, operand_id, i); const value_id = self.spv.allocId(); switch (dst_info.signedness) { .signed => try self.func.body.emit(self.spv.gpa, .OpSConvert, .{ - .id_result_type = wip.scalar_ty_id, + .id_result_type = wip.ty_id, .id_result = value_id, .signed_value = elem_id, }), .unsigned => try self.func.body.emit(self.spv.gpa, .OpUConvert, .{ - .id_result_type = wip.scalar_ty_id, + .id_result_type = wip.ty_id, .id_result = value_id, .unsigned_value = elem_id, }), @@ -3471,7 +3443,7 @@ const DeclGen = struct { // type, we don't need to normalize when growing the type. The // representation is already the same. if (dst_info.bits < src_info.bits) { - result_id.* = try self.normalize(wip.scalar_ty_ref, value_id, dst_info); + result_id.* = try self.normalize(wip.ty_ref, value_id, dst_info); } else { result_id.* = value_id; } @@ -3491,16 +3463,12 @@ const DeclGen = struct { } fn airIntFromPtr(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; const operand_id = try self.resolve(un_op); return try self.intFromPtr(operand_id); } fn airFloatFromInt(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const operand_ty = self.typeOf(ty_op.operand); const operand_id = try self.resolve(ty_op.operand); @@ -3525,8 +3493,6 @@ const DeclGen = struct { } fn airIntFromFloat(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const operand_id = try self.resolve(ty_op.operand); const dest_ty = self.typeOfIndex(inst); @@ -3550,24 +3516,20 @@ const DeclGen = struct { } fn airIntFromBool(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; const operand_id = try self.resolve(un_op); const result_ty = self.typeOfIndex(inst); - var wip = try self.elementWise(result_ty); + var wip = try self.elementWise(result_ty, false); defer wip.deinit(); for (wip.results, 0..) |*result_id, i| { const elem_id = try wip.elementAt(Type.bool, operand_id, i); - result_id.* = try self.intFromBool(wip.scalar_ty_ref, elem_id); + result_id.* = try self.intFromBool(wip.ty_ref, elem_id); } return try wip.finalize(); } fn airFloatCast(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const operand_id = try self.resolve(ty_op.operand); const dest_ty = self.typeOfIndex(inst); @@ -3583,18 +3545,17 @@ const DeclGen = struct { } fn airNot(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const operand_id = try self.resolve(ty_op.operand); const result_ty = self.typeOfIndex(inst); const info = self.arithmeticTypeInfo(result_ty); - var wip = try self.elementWise(result_ty); + var wip = try self.elementWise(result_ty, false); defer wip.deinit(); for (0..wip.results.len) |i| { const args = .{ - .id_result_type = wip.scalar_ty_id, + .id_result_type = wip.ty_id, .id_result = wip.allocId(i), .operand = try wip.elementAt(result_ty, operand_id, i), }; @@ -3615,8 +3576,6 @@ const DeclGen = struct { } fn airArrayToSlice(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - const mod = self.module; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const array_ptr_ty = self.typeOf(ty_op.operand); @@ -3637,35 +3596,22 @@ const DeclGen = struct { // Convert the pointer-to-array to a pointer to the first element. try self.accessChain(elem_ptr_ty_ref, array_ptr_id, &.{0}); - return try self.constructStruct( - slice_ty, - &.{ elem_ptr_ty, Type.usize }, - &.{ elem_ptr_id, len_id }, - ); + return try self.constructComposite(slice_ty, &.{ elem_ptr_id, len_id }); } fn airSlice(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; const ptr_id = try self.resolve(bin_op.lhs); const len_id = try self.resolve(bin_op.rhs); - const ptr_ty = self.typeOf(bin_op.lhs); const slice_ty = self.typeOfIndex(inst); // Note: Types should not need to be converted to direct, these types // dont need to be converted. - return try self.constructStruct( - slice_ty, - &.{ ptr_ty, Type.usize }, - &.{ ptr_id, len_id }, - ); + return try self.constructComposite(slice_ty, &.{ ptr_id, len_id }); } fn airAggregateInit(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - const mod = self.module; const ip = &mod.intern_pool; const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; @@ -3680,8 +3626,6 @@ const DeclGen = struct { unreachable; // TODO } - const types = try self.gpa.alloc(Type, elements.len); - defer self.gpa.free(types); const constituents = try self.gpa.alloc(IdRef, elements.len); defer self.gpa.free(constituents); var index: usize = 0; @@ -3693,7 +3637,6 @@ const DeclGen = struct { assert(Type.fromInterned(field_ty).hasRuntimeBits(mod)); const id = try self.resolve(element); - types[index] = Type.fromInterned(field_ty); constituents[index] = try self.convertToIndirect(Type.fromInterned(field_ty), id); index += 1; } @@ -3707,7 +3650,6 @@ const DeclGen = struct { assert(field_ty.hasRuntimeBitsIgnoreComptime(mod)); const id = try self.resolve(element); - types[index] = field_ty; constituents[index] = try self.convertToIndirect(field_ty, id); index += 1; } @@ -3715,11 +3657,7 @@ const DeclGen = struct { else => unreachable, } - return try self.constructStruct( - result_ty, - types[0..index], - constituents[0..index], - ); + return try self.constructComposite(result_ty, constituents[0..index]); }, .Vector => { const n_elems = result_ty.vectorLen(mod); @@ -3731,7 +3669,7 @@ const DeclGen = struct { elem_ids[i] = try self.convertToIndirect(result_ty.childType(mod), id); } - return try self.constructVector(result_ty, elem_ids); + return try self.constructComposite(result_ty, elem_ids); }, .Array => { const array_info = result_ty.arrayInfo(mod); @@ -3748,7 +3686,7 @@ const DeclGen = struct { elem_ids[n_elems - 1] = try self.constant(array_info.elem_type, sentinel_val, .indirect); } - return try self.constructArray(result_ty, elem_ids); + return try self.constructComposite(result_ty, elem_ids); }, else => unreachable, } @@ -3795,7 +3733,6 @@ const DeclGen = struct { } fn airSliceField(self: *DeclGen, inst: Air.Inst.Index, field: u32) !?IdRef { - if (self.liveness.isUnused(inst)) return null; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const field_ty = self.typeOfIndex(inst); const operand_id = try self.resolve(ty_op.operand); @@ -3852,8 +3789,6 @@ const DeclGen = struct { } fn airPtrElemPtr(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - const mod = self.module; const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; @@ -3871,8 +3806,6 @@ const DeclGen = struct { } fn airArrayElemVal(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - const mod = self.module; const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; const array_ty = self.typeOf(bin_op.lhs); @@ -3893,8 +3826,6 @@ const DeclGen = struct { } fn airPtrElemVal(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - const mod = self.module; const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; const ptr_ty = self.typeOf(bin_op.lhs); @@ -3951,8 +3882,6 @@ const DeclGen = struct { } fn airGetUnionTag(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const un_ty = self.typeOf(ty_op.operand); @@ -4036,8 +3965,6 @@ const DeclGen = struct { } fn airUnionInit(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - const mod = self.module; const ip = &mod.intern_pool; const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; @@ -4054,8 +3981,6 @@ const DeclGen = struct { } fn airStructFieldVal(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - const mod = self.module; const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const struct_field = self.air.extraData(Air.StructField, ty_pl.payload).data; @@ -4176,7 +4101,6 @@ const DeclGen = struct { } fn airStructFieldPtrIndex(self: *DeclGen, inst: Air.Inst.Index, field_index: u32) !?IdRef { - if (self.liveness.isUnused(inst)) return null; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const struct_ptr = try self.resolve(ty_op.operand); const struct_ptr_ty = self.typeOf(ty_op.operand); @@ -4230,7 +4154,6 @@ const DeclGen = struct { } fn airAlloc(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; const mod = self.module; const ptr_ty = self.typeOfIndex(inst); assert(ptr_ty.ptrAddressSpace(mod) == .generic); @@ -4814,9 +4737,7 @@ const DeclGen = struct { try self.beginSpvBlock(ok_block); } - if (self.liveness.isUnused(inst)) { - return null; - } + if (!eu_layout.payload_has_bits) { return null; } @@ -4826,8 +4747,6 @@ const DeclGen = struct { } fn airErrUnionErr(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - const mod = self.module; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const operand_id = try self.resolve(ty_op.operand); @@ -4851,8 +4770,6 @@ const DeclGen = struct { } fn airErrUnionPayload(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const operand_id = try self.resolve(ty_op.operand); const payload_ty = self.typeOfIndex(inst); @@ -4866,8 +4783,6 @@ const DeclGen = struct { } fn airWrapErrUnionErr(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - const mod = self.module; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const err_union_ty = self.typeOfIndex(inst); @@ -4885,16 +4800,10 @@ const DeclGen = struct { members[eu_layout.errorFieldIndex()] = operand_id; members[eu_layout.payloadFieldIndex()] = try self.spv.constUndef(payload_ty_ref); - var types: [2]Type = undefined; - types[eu_layout.errorFieldIndex()] = Type.anyerror; - types[eu_layout.payloadFieldIndex()] = payload_ty; - - return try self.constructStruct(err_union_ty, &types, &members); + return try self.constructComposite(err_union_ty, &members); } fn airWrapErrUnionPayload(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const err_union_ty = self.typeOfIndex(inst); const operand_id = try self.resolve(ty_op.operand); @@ -4910,16 +4819,10 @@ const DeclGen = struct { members[eu_layout.errorFieldIndex()] = try self.constInt(err_ty_ref, 0); members[eu_layout.payloadFieldIndex()] = try self.convertToIndirect(payload_ty, operand_id); - var types: [2]Type = undefined; - types[eu_layout.errorFieldIndex()] = Type.anyerror; - types[eu_layout.payloadFieldIndex()] = payload_ty; - - return try self.constructStruct(err_union_ty, &types, &members); + return try self.constructComposite(err_union_ty, &members); } fn airIsNull(self: *DeclGen, inst: Air.Inst.Index, is_pointer: bool, pred: enum { is_null, is_non_null }) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - const mod = self.module; const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; const operand_id = try self.resolve(un_op); @@ -4992,8 +4895,6 @@ const DeclGen = struct { } fn airIsErr(self: *DeclGen, inst: Air.Inst.Index, pred: enum { is_err, is_non_err }) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - const mod = self.module; const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; const operand_id = try self.resolve(un_op); @@ -5028,8 +4929,6 @@ const DeclGen = struct { } fn airUnwrapOptional(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - const mod = self.module; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const operand_id = try self.resolve(ty_op.operand); @@ -5046,8 +4945,6 @@ const DeclGen = struct { } fn airUnwrapOptionalPtr(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - const mod = self.module; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const operand_id = try self.resolve(ty_op.operand); @@ -5072,8 +4969,6 @@ const DeclGen = struct { } fn airWrapOptional(self: *DeclGen, inst: Air.Inst.Index) !?IdRef { - if (self.liveness.isUnused(inst)) return null; - const mod = self.module; const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; const payload_ty = self.typeOf(ty_op.operand); @@ -5091,8 +4986,7 @@ const DeclGen = struct { const payload_id = try self.convertToIndirect(payload_ty, operand_id); const members = [_]IdRef{ payload_id, try self.constBool(true, .indirect) }; - const types = [_]Type{ payload_ty, Type.bool }; - return try self.constructStruct(optional_ty, &types, &members); + return try self.constructComposite(optional_ty, &members); } fn airSwitchBr(self: *DeclGen, inst: Air.Inst.Index) !void { diff --git a/src/codegen/spirv/Module.zig b/src/codegen/spirv/Module.zig index 2c411b4590..61ef36162f 100644 --- a/src/codegen/spirv/Module.zig +++ b/src/codegen/spirv/Module.zig @@ -8,6 +8,7 @@ const Module = @This(); const std = @import("std"); +const builtin = @import("builtin"); const Allocator = std.mem.Allocator; const assert = std.debug.assert; @@ -114,8 +115,10 @@ sections: struct { capabilities: Section = .{}, /// OpExtension instructions extensions: Section = .{}, - // OpExtInstImport instructions - skip for now. - // memory model defined by target, not required here. + /// OpExtInstImport + extended_instruction_set: Section = .{}, + /// memory model defined by target + memory_model: Section = .{}, /// OpEntryPoint instructions - Handled by `self.entry_points`. /// OpExecutionMode and OpExecutionModeId instructions. execution_modes: Section = .{}, @@ -172,6 +175,9 @@ globals: struct { section: Section = .{}, } = .{}, +/// The list of extended instruction sets that should be imported. +extended_instruction_set: std.AutoHashMapUnmanaged(ExtendedInstructionSet, IdRef) = .{}, + pub fn init(gpa: Allocator) Module { return .{ .gpa = gpa, @@ -182,6 +188,8 @@ pub fn init(gpa: Allocator) Module { pub fn deinit(self: *Module) void { self.sections.capabilities.deinit(self.gpa); self.sections.extensions.deinit(self.gpa); + self.sections.extended_instruction_set.deinit(self.gpa); + self.sections.memory_model.deinit(self.gpa); self.sections.execution_modes.deinit(self.gpa); self.sections.debug_strings.deinit(self.gpa); self.sections.debug_names.deinit(self.gpa); @@ -200,6 +208,8 @@ pub fn deinit(self: *Module) void { self.globals.globals.deinit(self.gpa); self.globals.section.deinit(self.gpa); + self.extended_instruction_set.deinit(self.gpa); + self.* = undefined; } @@ -407,12 +417,12 @@ pub fn flush(self: *Module, file: std.fs.File, target: std.Target) !void { var types_constants = try self.cache.materialize(self); defer types_constants.deinit(self.gpa); - // TODO: Vulkan doesn't support initializer kernel - var init_func = if (target.os.tag != .vulkan) - try self.initializer(&entry_points) - else - Section{}; - defer init_func.deinit(self.gpa); + // // TODO: Pass global variables as function parameters + // var init_func = if (target.os.tag != .vulkan) + // try self.initializer(&entry_points) + // else + // Section{}; + // defer init_func.deinit(self.gpa); const header = [_]Word{ spec.magic_number, @@ -448,6 +458,8 @@ pub fn flush(self: *Module, file: std.fs.File, target: std.Target) !void { &header, self.sections.capabilities.toWords(), self.sections.extensions.toWords(), + self.sections.extended_instruction_set.toWords(), + self.sections.memory_model.toWords(), entry_points.toWords(), self.sections.execution_modes.toWords(), source.toWords(), @@ -458,22 +470,28 @@ pub fn flush(self: *Module, file: std.fs.File, target: std.Target) !void { self.sections.types_globals_constants.toWords(), globals.toWords(), self.sections.functions.toWords(), - init_func.toWords(), }; - var iovc_buffers: [buffers.len]std.os.iovec_const = undefined; - var file_size: u64 = 0; - for (&iovc_buffers, 0..) |*iovc, i| { - // Note, since spir-v supports both little and big endian we can ignore byte order here and - // just treat the words as a sequence of bytes. - const bytes = std.mem.sliceAsBytes(buffers[i]); - iovc.* = .{ .iov_base = bytes.ptr, .iov_len = bytes.len }; - file_size += bytes.len; - } + if (builtin.zig_backend == .stage2_x86_64) { + for (buffers) |buf| { + try file.writeAll(std.mem.sliceAsBytes(buf)); + } + } else { + // miscompiles with x86_64 backend + var iovc_buffers: [buffers.len]std.os.iovec_const = undefined; + var file_size: u64 = 0; + for (&iovc_buffers, 0..) |*iovc, i| { + // Note, since spir-v supports both little and big endian we can ignore byte order here and + // just treat the words as a sequence of bytes. + const bytes = std.mem.sliceAsBytes(buffers[i]); + iovc.* = .{ .iov_base = bytes.ptr, .iov_len = bytes.len }; + file_size += bytes.len; + } - try file.seekTo(0); - try file.setEndPos(file_size); - try file.pwritevAll(&iovc_buffers, 0); + try file.seekTo(0); + try file.setEndPos(file_size); + try file.pwritevAll(&iovc_buffers, 0); + } } /// Merge the sections making up a function declaration into this module. @@ -483,6 +501,29 @@ pub fn addFunction(self: *Module, decl_index: Decl.Index, func: Fn) !void { try self.declareDeclDeps(decl_index, func.decl_deps.keys()); } +pub const ExtendedInstructionSet = enum { + glsl, + opencl, +}; + +/// Imports or returns the existing id of an extended instruction set +pub fn importInstructionSet(self: *Module, set: ExtendedInstructionSet) !IdRef { + const gop = try self.extended_instruction_set.getOrPut(self.gpa, set); + if (gop.found_existing) return gop.value_ptr.*; + + const result_id = self.allocId(); + try self.sections.extended_instruction_set.emit(self.gpa, .OpExtInstImport, .{ + .id_result = result_id, + .name = switch (set) { + .glsl => "GLSL.std.450", + .opencl => "OpenCL.std", + }, + }); + gop.value_ptr.* = result_id; + + return result_id; +} + /// Fetch the result-id of an OpString instruction that encodes the path of the source /// file of the decl. This function may also emit an OpSource with source-level information regarding /// the decl. |
