From aa44f8f0fd1a4af9df65a9b9bfc5fe6fa0d31b9f Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Fri, 21 Jul 2023 00:51:50 -0400 Subject: [PATCH 1/5] llvm: convert attributes and non-intrinsic calls --- src/codegen/llvm.zig | 717 ++++++++++------ src/codegen/llvm/Builder.zig | 1447 +++++++++++++++++++++++++++++++-- src/codegen/llvm/bindings.zig | 38 +- src/zig_llvm.cpp | 10 +- 4 files changed, 1880 insertions(+), 332 deletions(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 57842ef1e09e..4f8e9503b3ba 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -359,7 +359,7 @@ const DataLayoutBuilder = struct { .macho => 'o', // Mach-O mangling: Private symbols get `L` prefix. // Other symbols get a `_` prefix. .coff => switch (self.target.os.tag) { - .windows => switch (self.target.cpu.arch) { + .uefi, .windows => switch (self.target.cpu.arch) { .x86 => 'x', // Windows x86 COFF mangling: Private symbols get the usual // prefix. Regular C symbols get a `_` prefix. Functions with `__stdcall`, //`__fastcall`, and `__vectorcall` have custom mangling that appends `@N` @@ -794,7 +794,7 @@ pub const Object = struct { builder.llvm.di_compile_unit = builder.llvm.di_builder.?.createCompileUnit( DW.LANG.C99, builder.llvm.di_builder.?.createFile(options.root_name, compile_unit_dir_z), - producer.toSlice(&builder).?, + producer.slice(&builder).?, options.optimize_mode != .Debug, "", // flags 0, // runtime version @@ -830,7 +830,7 @@ pub const Object = struct { target_machine = llvm.TargetMachine.create( builder.llvm.target.?, - builder.target_triple.toSlice(&builder).?, + builder.target_triple.slice(&builder).?, if (options.target.cpu.model.llvm_name) |s| s.ptr else null, options.llvm_cpu_features, opt_level, @@ -861,7 +861,7 @@ pub const Object = struct { defer llvm.disposeMessage(rep); std.testing.expectEqualStrings( std.mem.span(rep), - builder.data_layout.toSlice(&builder).?, + builder.data_layout.slice(&builder).?, ) catch unreachable; } } @@ -963,7 +963,7 @@ pub const Object = struct { llvm_error.* = try o.builder.structConst(llvm_slice_ty, &.{ global_index.toConst(), - try o.builder.intConst(llvm_usize_ty, name.toSlice(&o.builder).?.len), + try o.builder.intConst(llvm_usize_ty, name.slice(&o.builder).?.len), }); } @@ -1223,6 +1223,7 @@ pub const Object = struct { const func = mod.funcInfo(func_index); const decl_index = func.owner_decl; const decl = mod.declPtr(decl_index); + const fn_info = mod.typeToFunc(decl.ty).?; const target = mod.getTarget(); const ip = &mod.intern_pool; @@ -1237,28 +1238,43 @@ pub const Object = struct { const global = function.ptrConst(&o.builder).global; const llvm_func = global.toLlvm(&o.builder); + var attributes = try function.ptrConst(&o.builder).attributes.toWip(&o.builder); + defer attributes.deinit(&o.builder); + if (func.analysis(ip).is_noinline) { + try attributes.addFnAttr(.@"noinline", &o.builder); o.addFnAttr(llvm_func, "noinline"); } else { + _ = try attributes.removeFnAttr(.@"noinline"); Object.removeFnAttr(llvm_func, "noinline"); } if (func.analysis(ip).stack_alignment.toByteUnitsOptional()) |alignment| { + try attributes.addFnAttr(.{ .alignstack = Builder.Alignment.fromByteUnits(alignment) }, &o.builder); + try attributes.addFnAttr(.@"noinline", &o.builder); o.addFnAttrInt(llvm_func, "alignstack", alignment); o.addFnAttr(llvm_func, "noinline"); } else { + _ = try attributes.removeFnAttr(.alignstack); Object.removeFnAttr(llvm_func, "alignstack"); } if (func.analysis(ip).is_cold) { + try attributes.addFnAttr(.cold, &o.builder); o.addFnAttr(llvm_func, "cold"); } else { + _ = try attributes.removeFnAttr(.cold); Object.removeFnAttr(llvm_func, "cold"); } // TODO: disable this if safety is off for the function scope const ssp_buf_size = mod.comp.bin_file.options.stack_protector; if (ssp_buf_size != 0) { + try attributes.addFnAttr(.sspstrong, &o.builder); + try attributes.addFnAttr(.{ .string = .{ + .kind = try o.builder.string("stack-protector-buffer-size"), + .value = try o.builder.fmt("{d}", .{ssp_buf_size}), + } }, &o.builder); var buf: [12]u8 = undefined; const arg = std.fmt.bufPrintZ(&buf, "{d}", .{ssp_buf_size}) catch unreachable; o.addFnAttr(llvm_func, "sspstrong"); @@ -1267,8 +1283,16 @@ pub const Object = struct { // TODO: disable this if safety is off for the function scope if (mod.comp.bin_file.options.stack_check) { + try attributes.addFnAttr(.{ .string = .{ + .kind = try o.builder.string("probe-stack"), + .value = try o.builder.string("__zig_probe_stack"), + } }, &o.builder); o.addFnAttrString(llvm_func, "probe-stack", "__zig_probe_stack"); } else if (target.os.tag == .uefi) { + try attributes.addFnAttr(.{ .string = .{ + .kind = try o.builder.string("no-stack-arg-probe"), + .value = .empty, + } }, &o.builder); o.addFnAttrString(llvm_func, "no-stack-arg-probe", ""); } @@ -1286,18 +1310,22 @@ pub const Object = struct { var llvm_arg_i: u32 = 0; // This gets the LLVM values from the function and stores them in `dg.args`. - const fn_info = mod.typeToFunc(decl.ty).?; const sret = firstParamSRet(fn_info, mod); const ret_ptr: Builder.Value = if (sret) param: { const param = wip.arg(llvm_arg_i); llvm_arg_i += 1; break :param param; } else .none; - const gpa = o.gpa; if (ccAbiPromoteInt(fn_info.cc, mod, fn_info.return_type.toType())) |s| switch (s) { - .signed => o.addAttr(llvm_func, 0, "signext"), - .unsigned => o.addAttr(llvm_func, 0, "zeroext"), + .signed => { + try attributes.addRetAttr(.signext, &o.builder); + o.addAttr(llvm_func, 0, "signext"); + }, + .unsigned => { + try attributes.addRetAttr(.zeroext, &o.builder); + o.addAttr(llvm_func, 0, "zeroext"); + }, }; const err_return_tracing = fn_info.return_type.toType().isError(mod) and @@ -1312,6 +1340,7 @@ pub const Object = struct { // This is the list of args we will use that correspond directly to the AIR arg // instructions. Depending on the calling convention, this list is not necessarily // a bijection with the actual LLVM parameters of the function. + const gpa = o.gpa; var args: std.ArrayListUnmanaged(Builder.Value) = .{}; defer args.deinit(gpa); @@ -1337,7 +1366,7 @@ pub const Object = struct { } else { args.appendAssumeCapacity(param); - o.addByValParamAttrs(llvm_func, param_ty, param_index, fn_info, @intCast(llvm_arg_i)); + try o.addByValParamAttrsOld(&attributes, llvm_func, param_ty, param_index, fn_info, llvm_arg_i); } llvm_arg_i += 1; }, @@ -1347,7 +1376,7 @@ pub const Object = struct { const param = wip.arg(llvm_arg_i); const alignment = Builder.Alignment.fromByteUnits(param_ty.abiAlignment(mod)); - o.addByRefParamAttrs(llvm_func, @intCast(llvm_arg_i), @intCast(alignment.toByteUnits() orelse 0), it.byval_attr, param_llvm_ty); + try o.addByRefParamAttrsOld(&attributes, llvm_func, llvm_arg_i, alignment, it.byval_attr, param_llvm_ty); llvm_arg_i += 1; if (isByRef(param_ty, mod)) { @@ -1362,7 +1391,8 @@ pub const Object = struct { const param = wip.arg(llvm_arg_i); const alignment = Builder.Alignment.fromByteUnits(param_ty.abiAlignment(mod)); - o.addArgAttr(llvm_func, @intCast(llvm_arg_i), "noundef"); + try attributes.addParamAttr(llvm_arg_i, .noundef, &o.builder); + o.addArgAttr(llvm_func, llvm_arg_i, "noundef"); llvm_arg_i += 1; if (isByRef(param_ty, mod)) { @@ -1398,21 +1428,28 @@ pub const Object = struct { if (math.cast(u5, it.zig_index - 1)) |i| { if (@as(u1, @truncate(fn_info.noalias_bits >> i)) != 0) { - o.addArgAttr(llvm_func, @intCast(llvm_arg_i), "noalias"); + try attributes.addParamAttr(llvm_arg_i, .@"noalias", &o.builder); + o.addArgAttr(llvm_func, llvm_arg_i, "noalias"); } } if (param_ty.zigTypeTag(mod) != .Optional) { - o.addArgAttr(llvm_func, @intCast(llvm_arg_i), "nonnull"); + try attributes.addParamAttr(llvm_arg_i, .nonnull, &o.builder); + o.addArgAttr(llvm_func, llvm_arg_i, "nonnull"); } if (ptr_info.flags.is_const) { - o.addArgAttr(llvm_func, @intCast(llvm_arg_i), "readonly"); + try attributes.addParamAttr(llvm_arg_i, .readonly, &o.builder); + o.addArgAttr(llvm_func, llvm_arg_i, "readonly"); } - const elem_align = ptr_info.flags.alignment.toByteUnitsOptional() orelse - @max(ptr_info.child.toType().abiAlignment(mod), 1); - o.addArgAttrInt(llvm_func, @intCast(llvm_arg_i), "align", elem_align); - const ptr_param = wip.arg(llvm_arg_i + 0); - const len_param = wip.arg(llvm_arg_i + 1); - llvm_arg_i += 2; + const elem_align = Builder.Alignment.fromByteUnits( + ptr_info.flags.alignment.toByteUnitsOptional() orelse + @max(ptr_info.child.toType().abiAlignment(mod), 1), + ); + try attributes.addParamAttr(llvm_arg_i, .{ .@"align" = elem_align }, &o.builder); + o.addArgAttrInt(llvm_func, llvm_arg_i, "align", elem_align.toByteUnits() orelse 0); + const ptr_param = wip.arg(llvm_arg_i); + llvm_arg_i += 1; + const len_param = wip.arg(llvm_arg_i); + llvm_arg_i += 1; const slice_llvm_ty = try o.lowerType(param_ty); args.appendAssumeCapacity( @@ -1482,6 +1519,8 @@ pub const Object = struct { } } + function.ptr(&o.builder).attributes = try attributes.finish(&o.builder); + var di_file: ?*llvm.DIFile = null; var di_scope: ?*llvm.DIScope = null; @@ -1618,7 +1657,7 @@ pub const Object = struct { llvm_global.setDLLStorageClass(.Default); } if (self.di_map.get(decl)) |di_node| { - const decl_name_slice = decl_name.toSlice(&self.builder).?; + 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); @@ -1655,7 +1694,7 @@ pub const Object = struct { llvm_global.setDLLStorageClass(.DLLExport); } if (self.di_map.get(decl)) |di_node| { - const exp_name_slice = exp_name.toSlice(&self.builder).?; + const exp_name_slice = 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, exp_name_slice.ptr, exp_name_slice.len); @@ -2816,7 +2855,7 @@ pub const Object = struct { const fqn = try o.builder.string(ip.stringToSlice(try decl.getFullyQualifiedName(mod))); const llvm_addrspace = toLlvmAddressSpace(decl.@"addrspace", target); - const llvm_fn = o.llvm_module.addFunctionInAddressSpace(fqn.toSlice(&o.builder).?, fn_type.toLlvm(&o.builder), @intFromEnum(llvm_addrspace)); + const llvm_fn = o.llvm_module.addFunctionInAddressSpace(fqn.slice(&o.builder).?, fn_type.toLlvm(&o.builder), @intFromEnum(llvm_addrspace)); var global = Builder.Global{ .type = fn_type, @@ -2826,6 +2865,9 @@ pub const Object = struct { .global = @enumFromInt(o.builder.globals.count()), }; + var attributes: Builder.FunctionAttributes.Wip = .{}; + defer attributes.deinit(&o.builder); + const is_extern = decl.isExtern(mod); if (!is_extern) { global.linkage = .internal; @@ -2834,43 +2876,64 @@ pub const Object = struct { llvm_fn.setUnnamedAddr(.True); } else { if (target.isWasm()) { + try attributes.addFnAttr(.{ .string = .{ + .kind = try o.builder.string("wasm-import-name"), + .value = try o.builder.string(ip.stringToSlice(decl.name)), + } }, &o.builder); o.addFnAttrString(llvm_fn, "wasm-import-name", ip.stringToSlice(decl.name)); if (ip.stringToSliceUnwrap(decl.getOwnedExternFunc(mod).?.lib_name)) |lib_name| { if (!std.mem.eql(u8, lib_name, "c")) { + try attributes.addFnAttr(.{ .string = .{ + .kind = try o.builder.string("wasm-import-module"), + .value = try o.builder.string(lib_name), + } }, &o.builder); o.addFnAttrString(llvm_fn, "wasm-import-module", lib_name); } } } } + var llvm_arg_i: u32 = 0; if (sret) { - o.addArgAttr(llvm_fn, 0, "nonnull"); // Sret pointers must not be address 0 - o.addArgAttr(llvm_fn, 0, "noalias"); + // Sret pointers must not be address 0 + try attributes.addParamAttr(llvm_arg_i, .nonnull, &o.builder); + try attributes.addParamAttr(llvm_arg_i, .@"noalias", &o.builder); + o.addArgAttr(llvm_fn, llvm_arg_i, "nonnull"); // Sret pointers must not be address 0 + o.addArgAttr(llvm_fn, llvm_arg_i, "noalias"); + + const raw_llvm_ret_ty = try o.lowerType(fn_info.return_type.toType()); + try attributes.addParamAttr(llvm_arg_i, .{ .sret = raw_llvm_ret_ty }, &o.builder); + llvm_fn.addSretAttr(raw_llvm_ret_ty.toLlvm(&o.builder)); - const raw_llvm_ret_ty = (try o.lowerType(fn_info.return_type.toType())).toLlvm(&o.builder); - llvm_fn.addSretAttr(raw_llvm_ret_ty); + llvm_arg_i += 1; } const err_return_tracing = fn_info.return_type.toType().isError(mod) and mod.comp.bin_file.options.error_return_tracing; if (err_return_tracing) { - o.addArgAttr(llvm_fn, @intFromBool(sret), "nonnull"); + try attributes.addParamAttr(llvm_arg_i, .nonnull, &o.builder); + o.addArgAttr(llvm_fn, llvm_arg_i, "nonnull"); + llvm_arg_i += 1; } switch (fn_info.cc) { .Unspecified, .Inline => { + function.call_conv = .fastcc; llvm_fn.setFunctionCallConv(.Fast); }, .Naked => { + try attributes.addFnAttr(.naked, &o.builder); o.addFnAttr(llvm_fn, "naked"); }, .Async => { + function.call_conv = .fastcc; llvm_fn.setFunctionCallConv(.Fast); @panic("TODO: LLVM backend lower async function"); }, else => { - llvm_fn.setFunctionCallConv(toLlvmCallConv(fn_info.cc, target)); + function.call_conv = toLlvmCallConv(fn_info.cc, target); + llvm_fn.setFunctionCallConv(@enumFromInt(@intFromEnum(function.call_conv))); }, } @@ -2880,9 +2943,10 @@ pub const Object = struct { } // Function attributes that are independent of analysis results of the function body. - o.addCommonFnAttributes(llvm_fn); + try o.addCommonFnAttributes(&attributes, llvm_fn); if (fn_info.return_type == .noreturn_type) { + try attributes.addFnAttr(.noreturn, &o.builder); o.addFnAttr(llvm_fn, "noreturn"); } @@ -2890,23 +2954,24 @@ pub const Object = struct { // because functions with bodies are handled in `updateFunc`. if (is_extern) { var it = iterateParamTypes(o, fn_info); - it.llvm_index += @intFromBool(sret); - it.llvm_index += @intFromBool(err_return_tracing); + it.llvm_index = llvm_arg_i; while (try it.next()) |lowering| switch (lowering) { .byval => { const param_index = it.zig_index - 1; const param_ty = fn_info.param_types.get(ip)[param_index].toType(); if (!isByRef(param_ty, mod)) { - o.addByValParamAttrs(llvm_fn, param_ty, param_index, fn_info, it.llvm_index - 1); + try o.addByValParamAttrsOld(&attributes, llvm_fn, param_ty, param_index, fn_info, it.llvm_index - 1); } }, .byref => { const param_ty = fn_info.param_types.get(ip)[it.zig_index - 1]; const param_llvm_ty = try o.lowerType(param_ty.toType()); - const alignment = param_ty.toType().abiAlignment(mod); - o.addByRefParamAttrs(llvm_fn, it.llvm_index - 1, alignment, it.byval_attr, param_llvm_ty); + const alignment = + Builder.Alignment.fromByteUnits(param_ty.toType().abiAlignment(mod)); + try o.addByRefParamAttrsOld(&attributes, llvm_fn, it.llvm_index - 1, alignment, it.byval_attr, param_llvm_ty); }, .byref_mut => { + try attributes.addParamAttr(it.llvm_index - 1, .noundef, &o.builder); o.addArgAttr(llvm_fn, it.llvm_index - 1, "noundef"); }, // No attributes needed for these. @@ -2924,25 +2989,42 @@ pub const Object = struct { }; } + function.attributes = try attributes.finish(&o.builder); + try o.builder.llvm.globals.append(o.gpa, llvm_fn); gop.value_ptr.* = try o.builder.addGlobal(fqn, global); try o.builder.functions.append(o.gpa, function); return global.kind.function; } - fn addCommonFnAttributes(o: *Object, llvm_fn: *llvm.Value) void { + fn addCommonFnAttributes( + o: *Object, + attributes: *Builder.FunctionAttributes.Wip, + llvm_fn: *llvm.Value, + ) Allocator.Error!void { const comp = o.module.comp; if (!comp.bin_file.options.red_zone) { + try attributes.addFnAttr(.noredzone, &o.builder); o.addFnAttr(llvm_fn, "noredzone"); } if (comp.bin_file.options.omit_frame_pointer) { + try attributes.addFnAttr(.{ .string = .{ + .kind = try o.builder.string("frame-pointer"), + .value = try o.builder.string("none"), + } }, &o.builder); o.addFnAttrString(llvm_fn, "frame-pointer", "none"); } else { + try attributes.addFnAttr(.{ .string = .{ + .kind = try o.builder.string("frame-pointer"), + .value = try o.builder.string("all"), + } }, &o.builder); o.addFnAttrString(llvm_fn, "frame-pointer", "all"); } + try attributes.addFnAttr(.nounwind, &o.builder); o.addFnAttr(llvm_fn, "nounwind"); if (comp.unwind_tables) { + try attributes.addFnAttr(.{ .uwtable = Builder.Attribute.UwTable.default }, &o.builder); o.addFnAttrInt(llvm_fn, "uwtable", 2); } if (comp.bin_file.options.skip_linker_dependencies or @@ -2953,22 +3035,38 @@ pub const Object = struct { // and llvm detects that the body is equivalent to memcpy, it may replace the // body of memcpy with a call to memcpy, which would then cause a stack // overflow instead of performing memcpy. + try attributes.addFnAttr(.nobuiltin, &o.builder); o.addFnAttr(llvm_fn, "nobuiltin"); } if (comp.bin_file.options.optimize_mode == .ReleaseSmall) { + try attributes.addFnAttr(.minsize, &o.builder); + try attributes.addFnAttr(.optsize, &o.builder); o.addFnAttr(llvm_fn, "minsize"); o.addFnAttr(llvm_fn, "optsize"); } if (comp.bin_file.options.tsan) { + try attributes.addFnAttr(.sanitize_thread, &o.builder); o.addFnAttr(llvm_fn, "sanitize_thread"); } if (comp.getTarget().cpu.model.llvm_name) |s| { + try attributes.addFnAttr(.{ .string = .{ + .kind = try o.builder.string("target-cpu"), + .value = try o.builder.string(s), + } }, &o.builder); llvm_fn.addFunctionAttr("target-cpu", s); } if (comp.bin_file.options.llvm_cpu_features) |s| { + try attributes.addFnAttr(.{ .string = .{ + .kind = try o.builder.string("target-features"), + .value = try o.builder.string(std.mem.span(s)), + } }, &o.builder); llvm_fn.addFunctionAttr("target-features", s); } if (comp.getTarget().cpu.arch.isBpf()) { + try attributes.addFnAttr(.{ .string = .{ + .kind = try o.builder.string("no-builtins"), + .value = .empty, + } }, &o.builder); llvm_fn.addFunctionAttr("no-builtins", ""); } } @@ -3002,7 +3100,7 @@ pub const Object = struct { fqn; const llvm_global = o.llvm_module.addGlobalInAddressSpace( global.type.toLlvm(&o.builder), - fqn.toSlice(&o.builder).?, + fqn.slice(&o.builder).?, @intFromEnum(global.addr_space), ); @@ -4403,47 +4501,114 @@ pub const Object = struct { fn addByValParamAttrs( o: *Object, + attributes: *Builder.FunctionAttributes.Wip, + param_ty: Type, + param_index: u32, + fn_info: InternPool.Key.FuncType, + llvm_arg_i: u32, + ) Allocator.Error!void { + const mod = o.module; + if (param_ty.isPtrAtRuntime(mod)) { + const ptr_info = param_ty.ptrInfo(mod); + if (math.cast(u5, param_index)) |i| { + if (@as(u1, @truncate(fn_info.noalias_bits >> i)) != 0) { + try attributes.addParamAttr(llvm_arg_i, .@"noalias", &o.builder); + } + } + if (!param_ty.isPtrLikeOptional(mod) and !ptr_info.flags.is_allowzero) { + try attributes.addParamAttr(llvm_arg_i, .nonnull, &o.builder); + } + if (ptr_info.flags.is_const) { + try attributes.addParamAttr(llvm_arg_i, .readonly, &o.builder); + } + const elem_align = Builder.Alignment.fromByteUnits( + ptr_info.flags.alignment.toByteUnitsOptional() orelse + @max(ptr_info.child.toType().abiAlignment(mod), 1), + ); + try attributes.addParamAttr(llvm_arg_i, .{ .@"align" = elem_align }, &o.builder); + } else if (ccAbiPromoteInt(fn_info.cc, mod, param_ty)) |s| switch (s) { + .signed => try attributes.addParamAttr(llvm_arg_i, .signext, &o.builder), + .unsigned => try attributes.addParamAttr(llvm_arg_i, .zeroext, &o.builder), + }; + } + + fn addByRefParamAttrs( + o: *Object, + attributes: *Builder.FunctionAttributes.Wip, + llvm_arg_i: u32, + alignment: Builder.Alignment, + byval_attr: bool, + param_llvm_ty: Builder.Type, + ) Allocator.Error!void { + try attributes.addParamAttr(llvm_arg_i, .nonnull, &o.builder); + try attributes.addParamAttr(llvm_arg_i, .readonly, &o.builder); + try attributes.addParamAttr(llvm_arg_i, .{ .@"align" = alignment }, &o.builder); + if (byval_attr) { + try attributes.addParamAttr(llvm_arg_i, .{ .byval = param_llvm_ty }, &o.builder); + } + } + + fn addByValParamAttrsOld( + o: *Object, + attributes: *Builder.FunctionAttributes.Wip, llvm_fn: *llvm.Value, param_ty: Type, param_index: u32, fn_info: InternPool.Key.FuncType, llvm_arg_i: u32, - ) void { + ) Allocator.Error!void { const mod = o.module; if (param_ty.isPtrAtRuntime(mod)) { const ptr_info = param_ty.ptrInfo(mod); if (math.cast(u5, param_index)) |i| { if (@as(u1, @truncate(fn_info.noalias_bits >> i)) != 0) { + try attributes.addParamAttr(llvm_arg_i, .@"noalias", &o.builder); o.addArgAttr(llvm_fn, llvm_arg_i, "noalias"); } } if (!param_ty.isPtrLikeOptional(mod) and !ptr_info.flags.is_allowzero) { + try attributes.addParamAttr(llvm_arg_i, .nonnull, &o.builder); o.addArgAttr(llvm_fn, llvm_arg_i, "nonnull"); } if (ptr_info.flags.is_const) { + try attributes.addParamAttr(llvm_arg_i, .readonly, &o.builder); o.addArgAttr(llvm_fn, llvm_arg_i, "readonly"); } - const elem_align = ptr_info.flags.alignment.toByteUnitsOptional() orelse - @max(ptr_info.child.toType().abiAlignment(mod), 1); - o.addArgAttrInt(llvm_fn, llvm_arg_i, "align", elem_align); + const elem_align = Builder.Alignment.fromByteUnits( + ptr_info.flags.alignment.toByteUnitsOptional() orelse + @max(ptr_info.child.toType().abiAlignment(mod), 1), + ); + try attributes.addParamAttr(llvm_arg_i, .{ .@"align" = elem_align }, &o.builder); + o.addArgAttrInt(llvm_fn, llvm_arg_i, "align", elem_align.toByteUnits() orelse 0); } else if (ccAbiPromoteInt(fn_info.cc, mod, param_ty)) |s| switch (s) { - .signed => o.addArgAttr(llvm_fn, llvm_arg_i, "signext"), - .unsigned => o.addArgAttr(llvm_fn, llvm_arg_i, "zeroext"), + .signed => { + try attributes.addParamAttr(llvm_arg_i, .signext, &o.builder); + o.addArgAttr(llvm_fn, llvm_arg_i, "signext"); + }, + .unsigned => { + try attributes.addParamAttr(llvm_arg_i, .zeroext, &o.builder); + o.addArgAttr(llvm_fn, llvm_arg_i, "zeroext"); + }, }; } - fn addByRefParamAttrs( + fn addByRefParamAttrsOld( o: *Object, + attributes: *Builder.FunctionAttributes.Wip, llvm_fn: *llvm.Value, llvm_arg_i: u32, - alignment: u32, + alignment: Builder.Alignment, byval_attr: bool, param_llvm_ty: Builder.Type, - ) void { + ) Allocator.Error!void { + try attributes.addParamAttr(llvm_arg_i, .nonnull, &o.builder); + try attributes.addParamAttr(llvm_arg_i, .readonly, &o.builder); + try attributes.addParamAttr(llvm_arg_i, .{ .@"align" = alignment }, &o.builder); o.addArgAttr(llvm_fn, llvm_arg_i, "nonnull"); o.addArgAttr(llvm_fn, llvm_arg_i, "readonly"); - o.addArgAttrInt(llvm_fn, llvm_arg_i, "align", alignment); + o.addArgAttrInt(llvm_fn, llvm_arg_i, "align", alignment.toByteUnits() orelse 0); if (byval_attr) { + try attributes.addParamAttr(llvm_arg_i, .{ .byval = param_llvm_ty }, &o.builder); llvm_fn.addByValAttr(llvm_arg_i, param_llvm_ty.toLlvm(&o.builder)); } } @@ -4841,10 +5006,10 @@ pub const FuncGen = struct { .slice_ptr => try self.airSliceField(inst, 0), .slice_len => try self.airSliceField(inst, 1), - .call => try self.airCall(inst, .Auto), - .call_always_tail => try self.airCall(inst, .AlwaysTail), - .call_never_tail => try self.airCall(inst, .NeverTail), - .call_never_inline => try self.airCall(inst, .NeverInline), + .call => try self.airCall(inst, .auto), + .call_always_tail => try self.airCall(inst, .always_tail), + .call_never_tail => try self.airCall(inst, .never_tail), + .call_never_inline => try self.airCall(inst, .never_inline), .ptr_slice_ptr_ptr => try self.airPtrSliceFieldPtr(inst, 0), .ptr_slice_len_ptr => try self.airPtrSliceFieldPtr(inst, 1), @@ -4953,7 +5118,15 @@ pub const FuncGen = struct { } } - fn airCall(self: *FuncGen, inst: Air.Inst.Index, attr: llvm.CallAttr) !Builder.Value { + pub const CallAttr = enum { + Auto, + NeverTail, + NeverInline, + AlwaysTail, + AlwaysInline, + }; + + fn airCall(self: *FuncGen, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !Builder.Value { const pl_op = self.air.instructions.items(.data)[inst].pl_op; const extra = self.air.extraData(Air.Call, pl_op.payload); const args: []const Air.Inst.Ref = @ptrCast(self.air.extra[extra.end..][0..extra.data.args_len]); @@ -4972,14 +5145,25 @@ pub const FuncGen = struct { const target = mod.getTarget(); const sret = firstParamSRet(fn_info, mod); - var llvm_args = std.ArrayList(*llvm.Value).init(self.gpa); + var llvm_args = std.ArrayList(Builder.Value).init(self.gpa); defer llvm_args.deinit(); + var attributes: Builder.FunctionAttributes.Wip = .{}; + defer attributes.deinit(&o.builder); + + switch (modifier) { + .auto, .never_tail, .always_tail => {}, + .never_inline => try attributes.addFnAttr(.@"noinline", &o.builder), + .async_kw, .no_async, .always_inline, .compile_time => unreachable, + } + const ret_ptr = if (!sret) null else blk: { const llvm_ret_ty = try o.lowerType(return_type); + try attributes.addParamAttr(0, .{ .sret = llvm_ret_ty }, &o.builder); + const alignment = Builder.Alignment.fromByteUnits(return_type.abiAlignment(mod)); const ret_ptr = try self.buildAlloca(llvm_ret_ty, alignment); - try llvm_args.append(ret_ptr.toLlvm(&self.wip)); + try llvm_args.append(ret_ptr); break :blk ret_ptr; }; @@ -4987,7 +5171,7 @@ pub const FuncGen = struct { o.module.comp.bin_file.options.error_return_tracing; if (err_return_tracing) { assert(self.err_ret_trace != .none); - try llvm_args.append(self.err_ret_trace.toLlvm(&self.wip)); + try llvm_args.append(self.err_ret_trace); } var it = iterateParamTypes(o, fn_info); @@ -5001,9 +5185,9 @@ pub const FuncGen = struct { if (isByRef(param_ty, mod)) { const alignment = Builder.Alignment.fromByteUnits(param_ty.abiAlignment(mod)); const loaded = try self.wip.load(.normal, llvm_param_ty, llvm_arg, alignment, ""); - try llvm_args.append(loaded.toLlvm(&self.wip)); + try llvm_args.append(loaded); } else { - try llvm_args.append(llvm_arg.toLlvm(&self.wip)); + try llvm_args.append(llvm_arg); } }, .byref => { @@ -5011,13 +5195,13 @@ pub const FuncGen = struct { const param_ty = self.typeOf(arg); const llvm_arg = try self.resolveInst(arg); if (isByRef(param_ty, mod)) { - try llvm_args.append(llvm_arg.toLlvm(&self.wip)); + try llvm_args.append(llvm_arg); } else { const alignment = Builder.Alignment.fromByteUnits(param_ty.abiAlignment(mod)); const param_llvm_ty = llvm_arg.typeOfWip(&self.wip); const arg_ptr = try self.buildAlloca(param_llvm_ty, alignment); _ = try self.wip.store(.normal, llvm_arg, arg_ptr, alignment); - try llvm_args.append(arg_ptr.toLlvm(&self.wip)); + try llvm_args.append(arg_ptr); } }, .byref_mut => { @@ -5034,7 +5218,7 @@ pub const FuncGen = struct { } else { _ = try self.wip.store(.normal, llvm_arg, arg_ptr, alignment); } - try llvm_args.append(arg_ptr.toLlvm(&self.wip)); + try llvm_args.append(arg_ptr); }, .abi_sized_int => { const arg = args[it.zig_index - 1]; @@ -5045,7 +5229,7 @@ pub const FuncGen = struct { if (isByRef(param_ty, mod)) { const alignment = Builder.Alignment.fromByteUnits(param_ty.abiAlignment(mod)); const loaded = try self.wip.load(.normal, int_llvm_ty, llvm_arg, alignment, ""); - try llvm_args.append(loaded.toLlvm(&self.wip)); + try llvm_args.append(loaded); } else { // LLVM does not allow bitcasting structs so we must allocate // a local, store as one type, and then load as another type. @@ -5056,7 +5240,7 @@ pub const FuncGen = struct { const int_ptr = try self.buildAlloca(int_llvm_ty, alignment); _ = try self.wip.store(.normal, llvm_arg, int_ptr, alignment); const loaded = try self.wip.load(.normal, int_llvm_ty, int_ptr, alignment, ""); - try llvm_args.append(loaded.toLlvm(&self.wip)); + try llvm_args.append(loaded); } }, .slice => { @@ -5064,7 +5248,7 @@ pub const FuncGen = struct { const llvm_arg = try self.resolveInst(arg); const ptr = try self.wip.extractValue(llvm_arg, &.{0}, ""); const len = try self.wip.extractValue(llvm_arg, &.{1}, ""); - try llvm_args.appendSlice(&.{ ptr.toLlvm(&self.wip), len.toLlvm(&self.wip) }); + try llvm_args.appendSlice(&.{ ptr, len }); }, .multiple_llvm_types => { const arg = args[it.zig_index - 1]; @@ -5086,14 +5270,14 @@ pub const FuncGen = struct { Builder.Alignment.fromByteUnits(@divExact(target.ptrBitWidth(), 8)); const field_ptr = try self.wip.gepStruct(llvm_ty, arg_ptr, i, ""); const loaded = try self.wip.load(.normal, field_ty, field_ptr, alignment, ""); - llvm_args.appendAssumeCapacity(loaded.toLlvm(&self.wip)); + llvm_args.appendAssumeCapacity(loaded); } }, .as_u16 => { const arg = args[it.zig_index - 1]; const llvm_arg = try self.resolveInst(arg); const casted = try self.wip.cast(.bitcast, llvm_arg, .i16, ""); - try llvm_args.append(casted.toLlvm(&self.wip)); + try llvm_args.append(casted); }, .float_array => |count| { const arg = args[it.zig_index - 1]; @@ -5110,7 +5294,7 @@ pub const FuncGen = struct { const array_ty = try o.builder.arrayType(count, float_ty); const loaded = try self.wip.load(.normal, array_ty, llvm_arg, alignment, ""); - try llvm_args.append(loaded.toLlvm(&self.wip)); + try llvm_args.append(loaded); }, .i32_array, .i64_array => |arr_len| { const elem_size: u8 = if (lowering == .i32_array) 32 else 64; @@ -5127,24 +5311,10 @@ pub const FuncGen = struct { const array_ty = try o.builder.arrayType(arr_len, try o.builder.intType(@intCast(elem_size))); const loaded = try self.wip.load(.normal, array_ty, llvm_arg, alignment, ""); - try llvm_args.append(loaded.toLlvm(&self.wip)); + try llvm_args.append(loaded); }, }; - const llvm_fn_ty = try o.lowerType(zig_fn_ty); - const call = (try self.wip.unimplemented(llvm_fn_ty.functionReturn(&o.builder), "")).finish( - self.builder.buildCall( - llvm_fn_ty.toLlvm(&o.builder), - llvm_fn.toLlvm(&self.wip), - llvm_args.items.ptr, - @intCast(llvm_args.items.len), - toLlvmCallConv(fn_info.cc, target), - attr, - "", - ), - &self.wip, - ); - if (callee_ty.zigTypeTag(mod) == .Pointer) { // Add argument attributes for function pointer calls. it = iterateParamTypes(o, fn_info); @@ -5155,19 +5325,17 @@ pub const FuncGen = struct { const param_index = it.zig_index - 1; const param_ty = fn_info.param_types.get(ip)[param_index].toType(); if (!isByRef(param_ty, mod)) { - o.addByValParamAttrs(call.toLlvm(&self.wip), param_ty, param_index, fn_info, it.llvm_index - 1); + try o.addByValParamAttrs(&attributes, param_ty, param_index, fn_info, it.llvm_index - 1); } }, .byref => { const param_index = it.zig_index - 1; const param_ty = fn_info.param_types.get(ip)[param_index].toType(); const param_llvm_ty = try o.lowerType(param_ty); - const alignment = param_ty.abiAlignment(mod); - o.addByRefParamAttrs(call.toLlvm(&self.wip), it.llvm_index - 1, alignment, it.byval_attr, param_llvm_ty); - }, - .byref_mut => { - o.addArgAttr(call.toLlvm(&self.wip), it.llvm_index - 1, "noundef"); + const alignment = Builder.Alignment.fromByteUnits(param_ty.abiAlignment(mod)); + try o.addByRefParamAttrs(&attributes, it.llvm_index - 1, alignment, it.byval_attr, param_llvm_ty); }, + .byref_mut => try attributes.addParamAttr(it.llvm_index - 1, .noundef, &o.builder), // No attributes needed for these. .no_bits, .abi_sized_int, @@ -5186,23 +5354,40 @@ pub const FuncGen = struct { if (math.cast(u5, it.zig_index - 1)) |i| { if (@as(u1, @truncate(fn_info.noalias_bits >> i)) != 0) { - o.addArgAttr(call.toLlvm(&self.wip), llvm_arg_i, "noalias"); + try attributes.addParamAttr(llvm_arg_i, .@"noalias", &o.builder); } } if (param_ty.zigTypeTag(mod) != .Optional) { - o.addArgAttr(call.toLlvm(&self.wip), llvm_arg_i, "nonnull"); + try attributes.addParamAttr(llvm_arg_i, .nonnull, &o.builder); } if (ptr_info.flags.is_const) { - o.addArgAttr(call.toLlvm(&self.wip), llvm_arg_i, "readonly"); + try attributes.addParamAttr(llvm_arg_i, .readonly, &o.builder); } - const elem_align = ptr_info.flags.alignment.toByteUnitsOptional() orelse - @max(ptr_info.child.toType().abiAlignment(mod), 1); - o.addArgAttrInt(call.toLlvm(&self.wip), llvm_arg_i, "align", elem_align); + const elem_align = Builder.Alignment.fromByteUnits( + ptr_info.flags.alignment.toByteUnitsOptional() orelse + @max(ptr_info.child.toType().abiAlignment(mod), 1), + ); + try attributes.addParamAttr(llvm_arg_i, .{ .@"align" = elem_align }, &o.builder); }, }; } - if (fn_info.return_type == .noreturn_type and attr != .AlwaysTail) { + const call = try self.wip.call( + switch (modifier) { + .auto, .never_inline => .normal, + .never_tail => .notail, + .always_tail => .musttail, + .async_kw, .no_async, .always_inline, .compile_time => unreachable, + }, + toLlvmCallConv(fn_info.cc, target), + try attributes.finish(&o.builder), + try o.lowerType(zig_fn_ty), + llvm_fn, + llvm_args.items, + "", + ); + + if (fn_info.return_type == .noreturn_type and modifier != .always_tail) { return .none; } @@ -5211,9 +5396,7 @@ pub const FuncGen = struct { } const llvm_ret_ty = try o.lowerType(return_type); - if (ret_ptr) |rp| { - call.toLlvm(&self.wip).setCallSret(llvm_ret_ty.toLlvm(&o.builder)); if (isByRef(return_type, mod)) { return rp; } else { @@ -5269,25 +5452,24 @@ pub const FuncGen = struct { // ptr null, ; stack trace // ptr @2, ; addr (null ?usize) // ) - const args = [4]*llvm.Value{ - msg_ptr.toLlvm(&o.builder), - (try o.builder.intConst(llvm_usize, msg_len)).toLlvm(&o.builder), - (try o.builder.nullConst(.ptr)).toLlvm(&o.builder), - null_opt_addr_global.toLlvm(&o.builder), - }; const panic_func = mod.funcInfo(mod.panic_func_index); const panic_decl = mod.declPtr(panic_func.owner_decl); const fn_info = mod.typeToFunc(panic_decl.ty).?; const panic_global = try o.resolveLlvmFunction(panic_func.owner_decl); - _ = (try fg.wip.unimplemented(.void, "")).finish(fg.builder.buildCall( - (try o.lowerType(panic_decl.ty)).toLlvm(&o.builder), - panic_global.toLlvm(&o.builder), - &args, - args.len, + _ = try fg.wip.call( + .normal, toLlvmCallConv(fn_info.cc, target), - .Auto, + .none, + panic_global.typeOf(&o.builder), + panic_global.toValue(&o.builder), + &.{ + msg_ptr.toValue(), + try o.builder.intValue(llvm_usize, msg_len), + try o.builder.nullValue(.ptr), + null_opt_addr_global.toValue(), + }, "", - ), &fg.wip); + ); _ = try fg.wip.@"unreachable"(); } @@ -5395,7 +5577,7 @@ pub const FuncGen = struct { o.llvm_module.addFunction(llvm_fn_name, llvm_fn_ty.toLlvm(&o.builder)); const args: [2]*llvm.Value = .{ dest_list.toLlvm(&self.wip), src_list.toLlvm(&self.wip) }; - _ = (try self.wip.unimplemented(.void, "")).finish(self.builder.buildCall( + _ = (try self.wip.unimplemented(.void, "")).finish(self.builder.buildCallOld( llvm_fn_ty.toLlvm(&o.builder), llvm_fn, &args, @@ -5422,7 +5604,7 @@ pub const FuncGen = struct { o.llvm_module.addFunction(llvm_fn_name, llvm_fn_ty.toLlvm(&o.builder)); const args: [1]*llvm.Value = .{list.toLlvm(&self.wip)}; - _ = (try self.wip.unimplemented(.void, "")).finish(self.builder.buildCall( + _ = (try self.wip.unimplemented(.void, "")).finish(self.builder.buildCallOld( llvm_fn_ty.toLlvm(&o.builder), llvm_fn, &args, @@ -5449,7 +5631,7 @@ pub const FuncGen = struct { o.llvm_module.addFunction(llvm_fn_name, llvm_fn_ty.toLlvm(&o.builder)); const args: [1]*llvm.Value = .{list.toLlvm(&self.wip)}; - _ = (try self.wip.unimplemented(.void, "")).finish(self.builder.buildCall( + _ = (try self.wip.unimplemented(.void, "")).finish(self.builder.buildCallOld( llvm_fn_ty.toLlvm(&o.builder), llvm_fn, &args, @@ -5495,16 +5677,15 @@ pub const FuncGen = struct { const un_op = self.air.instructions.items(.data)[inst].un_op; const operand = try self.resolveInst(un_op); const llvm_fn = try self.getCmpLtErrorsLenFunction(); - const args: [1]*llvm.Value = .{operand.toLlvm(&self.wip)}; - return (try self.wip.unimplemented(.i1, "")).finish(self.builder.buildCall( - llvm_fn.typeOf(&o.builder).toLlvm(&o.builder), - llvm_fn.toLlvm(&o.builder), - &args, - args.len, - .Fast, - .Auto, + return self.wip.call( + .normal, + .fastcc, + .none, + llvm_fn.typeOf(&o.builder), + llvm_fn.toValue(&o.builder), + &.{operand}, "", - ), &self.wip); + ); } fn cmp( @@ -5953,16 +6134,15 @@ pub const FuncGen = struct { } const libc_fn = try self.getLibcFunction(fn_name, &.{param_type}, dest_llvm_ty); - const params = [1]*llvm.Value{extended.toLlvm(&self.wip)}; - return (try self.wip.unimplemented(dest_llvm_ty, "")).finish(self.builder.buildCall( - libc_fn.typeOf(&o.builder).toLlvm(&o.builder), - libc_fn.toLlvm(&o.builder), - ¶ms, - params.len, - .C, - .Auto, + return self.wip.call( + .normal, + .ccc, + .none, + libc_fn.typeOf(&o.builder), + libc_fn.toValue(&o.builder), + &.{extended}, "", - ), &self.wip); + ); } fn airIntFromFloat(self: *FuncGen, inst: Air.Inst.Index, want_fast_math: bool) !Builder.Value { @@ -6013,16 +6193,15 @@ pub const FuncGen = struct { const operand_llvm_ty = try o.lowerType(operand_ty); const libc_fn = try self.getLibcFunction(fn_name, &.{operand_llvm_ty}, libc_ret_ty); - const params = [1]*llvm.Value{operand.toLlvm(&self.wip)}; - var result = (try self.wip.unimplemented(libc_ret_ty, "")).finish(self.builder.buildCall( - libc_fn.typeOf(&o.builder).toLlvm(&o.builder), - libc_fn.toLlvm(&o.builder), - ¶ms, - params.len, - .C, - .Auto, + var result = try self.wip.call( + .normal, + .ccc, + .none, + libc_fn.typeOf(&o.builder), + libc_fn.toValue(&o.builder), + &.{operand}, "", - ), &self.wip); + ); if (libc_ret_ty != ret_ty) result = try self.wip.cast(.bitcast, result, ret_ty, ""); if (ret_ty != dest_llvm_ty) result = try self.wip.cast(.trunc, result, dest_llvm_ty, ""); @@ -6843,6 +7022,9 @@ pub const FuncGen = struct { } } + var attributes: Builder.FunctionAttributes.Wip = .{}; + defer attributes.deinit(&o.builder); + const ret_llvm_ty = switch (return_count) { 0 => .void, 1 => llvm_ret_types[0], @@ -6861,7 +7043,7 @@ pub const FuncGen = struct { .ATT, .False, ); - const call = (try self.wip.unimplemented(ret_llvm_ty, "")).finish(self.builder.buildCall( + const call = (try self.wip.unimplemented(ret_llvm_ty, "")).finish(self.builder.buildCallOld( llvm_fn_ty.toLlvm(&o.builder), asm_fn, llvm_param_values.ptr, @@ -6872,6 +7054,7 @@ pub const FuncGen = struct { ), &self.wip); for (llvm_param_attrs[0..param_count], 0..) |llvm_elem_ty, i| { if (llvm_elem_ty != .none) { + try attributes.addParamAttr(i, .{ .elementtype = llvm_elem_ty }, &o.builder); llvm.setCallElemTypeAttr(call.toLlvm(&self.wip), i, llvm_elem_ty.toLlvm(&o.builder)); } } @@ -7287,7 +7470,7 @@ pub const FuncGen = struct { const args: [1]*llvm.Value = .{ (try o.builder.intConst(.i32, index)).toLlvm(&o.builder), }; - return (try self.wip.unimplemented(.i32, "")).finish(self.builder.buildCall( + return (try self.wip.unimplemented(.i32, "")).finish(self.builder.buildCallOld( (try o.builder.fnType(.i32, &.{.i32}, .normal)).toLlvm(&o.builder), llvm_fn, &args, @@ -7308,7 +7491,7 @@ pub const FuncGen = struct { (try o.builder.intConst(.i32, index)).toLlvm(&o.builder), operand.toLlvm(&self.wip), }; - return (try self.wip.unimplemented(.i32, "")).finish(self.builder.buildCall( + return (try self.wip.unimplemented(.i32, "")).finish(self.builder.buildCallOld( (try o.builder.fnType(.i32, &.{ .i32, .i32 }, .normal)).toLlvm(&o.builder), llvm_fn, &args, @@ -7425,7 +7608,7 @@ pub const FuncGen = struct { }); const llvm_fn_ty = try o.builder.fnType(llvm_ret_ty, &.{ llvm_inst_ty, llvm_inst_ty }, .normal); const llvm_fn = try fg.getIntrinsic(intrinsic_name, &.{llvm_inst_ty}); - const result_struct = (try fg.wip.unimplemented(llvm_ret_ty, "")).finish(fg.builder.buildCall( + const result_struct = (try fg.wip.unimplemented(llvm_ret_ty, "")).finish(fg.builder.buildCallOld( llvm_fn_ty.toLlvm(&o.builder), llvm_fn, &[_]*llvm.Value{ lhs.toLlvm(&fg.wip), rhs.toLlvm(&fg.wip) }, @@ -7768,7 +7951,7 @@ pub const FuncGen = struct { ); const llvm_fn_ty = try o.builder.fnType(llvm_ret_ty, &.{ llvm_lhs_ty, llvm_lhs_ty }, .normal); const result_struct = (try self.wip.unimplemented(llvm_ret_ty, "")).finish( - self.builder.buildCall( + self.builder.buildCallOld( llvm_fn_ty.toLlvm(&o.builder), llvm_fn, &[_]*llvm.Value{ lhs.toLlvm(&self.wip), rhs.toLlvm(&self.wip) }, @@ -7818,29 +8001,23 @@ pub const FuncGen = struct { const o = self.dg.object; assert(args_vectors.len <= 3); - const llvm_fn_ty = llvm_fn.typeOf(&o.builder); - const llvm_scalar_ty = llvm_fn_ty.functionReturn(&o.builder); - var i: usize = 0; var result = result_vector; while (i < vector_len) : (i += 1) { const index_i32 = try o.builder.intValue(.i32, i); - var args: [3]*llvm.Value = undefined; + var args: [3]Builder.Value = undefined; for (args[0..args_vectors.len], args_vectors) |*arg_elem, arg_vector| { - arg_elem.* = (try self.wip.extractElement(arg_vector, index_i32, "")).toLlvm(&self.wip); + arg_elem.* = try self.wip.extractElement(arg_vector, index_i32, ""); } - const result_elem = (try self.wip.unimplemented(llvm_scalar_ty, "")).finish( - self.builder.buildCall( - llvm_fn_ty.toLlvm(&o.builder), - llvm_fn.toLlvm(&o.builder), - &args, - @intCast(args_vectors.len), - .C, - .Auto, - "", - ), - &self.wip, + const result_elem = try self.wip.call( + .normal, + .ccc, + .none, + llvm_fn.typeOf(&o.builder), + llvm_fn.toValue(&o.builder), + args[0..args_vectors.len], + "", ); result = try self.wip.insertElement(result, result_elem, index_i32, ""); } @@ -7861,7 +8038,7 @@ pub const FuncGen = struct { }; const fn_type = try o.builder.fnType(return_type, param_types, .normal); - const f = o.llvm_module.addFunction(fn_name.toSlice(&o.builder).?, fn_type.toLlvm(&o.builder)); + const f = o.llvm_module.addFunction(fn_name.slice(&o.builder).?, fn_type.toLlvm(&o.builder)); var global = Builder.Global{ .type = fn_type, @@ -7942,20 +8119,15 @@ pub const FuncGen = struct { return self.wip.icmp(int_cond, result, zero_vector, ""); } - const llvm_fn_ty = libc_fn.typeOf(&o.builder); - const llvm_params = [2]*llvm.Value{ params[0].toLlvm(&self.wip), params[1].toLlvm(&self.wip) }; - const result = (try self.wip.unimplemented( - llvm_fn_ty.functionReturn(&o.builder), - "", - )).finish(self.builder.buildCall( - libc_fn.typeOf(&o.builder).toLlvm(&o.builder), - libc_fn.toLlvm(&o.builder), - &llvm_params, - llvm_params.len, - .C, - .Auto, + const result = try self.wip.call( + .normal, + .ccc, + .none, + libc_fn.typeOf(&o.builder), + libc_fn.toValue(&o.builder), + ¶ms, "", - ), &self.wip); + ); return self.wip.icmp(int_cond, result, zero.toValue(), ""); } @@ -8085,7 +8257,7 @@ pub const FuncGen = struct { ); var llvm_params: [params_len]*llvm.Value = undefined; for (&llvm_params, params) |*llvm_param, param| llvm_param.* = param.toLlvm(&self.wip); - return (try self.wip.unimplemented(llvm_ty, "")).finish(self.builder.buildCall( + return (try self.wip.unimplemented(llvm_ty, "")).finish(self.builder.buildCallOld( llvm_fn_ty.toLlvm(&o.builder), llvm_fn, &llvm_params, @@ -8311,17 +8483,16 @@ pub const FuncGen = struct { compilerRtFloatAbbrev(src_bits), compilerRtFloatAbbrev(dest_bits), }); - const llvm_fn = try self.getLibcFunction(fn_name, &.{operand_llvm_ty}, dest_llvm_ty); - const params = [1]*llvm.Value{operand.toLlvm(&self.wip)}; - return (try self.wip.unimplemented(dest_llvm_ty, "")).finish(self.builder.buildCall( - llvm_fn.typeOf(&o.builder).toLlvm(&o.builder), - llvm_fn.toLlvm(&o.builder), - ¶ms, - params.len, - .C, - .Auto, + const libc_fn = try self.getLibcFunction(fn_name, &.{operand_llvm_ty}, dest_llvm_ty); + return self.wip.call( + .normal, + .ccc, + .none, + libc_fn.typeOf(&o.builder), + libc_fn.toValue(&o.builder), + &.{operand}, "", - ), &self.wip); + ); } } @@ -8346,17 +8517,16 @@ pub const FuncGen = struct { compilerRtFloatAbbrev(src_bits), compilerRtFloatAbbrev(dest_bits), }); - const llvm_fn = try self.getLibcFunction(fn_name, &.{operand_llvm_ty}, dest_llvm_ty); - const params = [1]*llvm.Value{operand.toLlvm(&self.wip)}; - return (try self.wip.unimplemented(dest_llvm_ty, "")).finish(self.builder.buildCall( - llvm_fn.typeOf(&o.builder).toLlvm(&o.builder), - llvm_fn.toLlvm(&o.builder), - ¶ms, - params.len, - .C, - .Auto, + const libc_fn = try self.getLibcFunction(fn_name, &.{operand_llvm_ty}, dest_llvm_ty); + return self.wip.call( + .normal, + .ccc, + .none, + libc_fn.typeOf(&o.builder), + libc_fn.toValue(&o.builder), + &.{operand}, "", - ), &self.wip); + ); } } @@ -8657,7 +8827,7 @@ pub const FuncGen = struct { _ = inst; const o = self.dg.object; const llvm_fn = try self.getIntrinsic("llvm.trap", &.{}); - _ = (try self.wip.unimplemented(.void, "")).finish(self.builder.buildCall( + _ = (try self.wip.unimplemented(.void, "")).finish(self.builder.buildCallOld( (try o.builder.fnType(.void, &.{}, .normal)).toLlvm(&o.builder), llvm_fn, undefined, @@ -8674,7 +8844,7 @@ pub const FuncGen = struct { _ = inst; const o = self.dg.object; const llvm_fn = try self.getIntrinsic("llvm.debugtrap", &.{}); - _ = (try self.wip.unimplemented(.void, "")).finish(self.builder.buildCall( + _ = (try self.wip.unimplemented(.void, "")).finish(self.builder.buildCallOld( (try o.builder.fnType(.void, &.{}, .normal)).toLlvm(&o.builder), llvm_fn, undefined, @@ -8701,7 +8871,7 @@ pub const FuncGen = struct { const params = [_]*llvm.Value{ (try o.builder.intConst(.i32, 0)).toLlvm(&o.builder), }; - const ptr_val = (try self.wip.unimplemented(.ptr, "")).finish(self.builder.buildCall( + const ptr_val = (try self.wip.unimplemented(.ptr, "")).finish(self.builder.buildCallOld( (try o.builder.fnType(.ptr, &.{.i32}, .normal)).toLlvm(&o.builder), llvm_fn, ¶ms, @@ -8727,7 +8897,7 @@ pub const FuncGen = struct { (try o.builder.intConst(.i32, 0)).toLlvm(&o.builder), }; const ptr_val = (try self.wip.unimplemented(llvm_fn_ty.functionReturn(&o.builder), "")).finish( - self.builder.buildCall( + self.builder.buildCallOld( llvm_fn_ty.toLlvm(&o.builder), llvm_fn, ¶ms, @@ -9256,7 +9426,7 @@ pub const FuncGen = struct { Builder.Constant.false.toLlvm(&o.builder), }; const wrong_size_result = (try self.wip.unimplemented(llvm_operand_ty, "")).finish( - self.builder.buildCall( + self.builder.buildCallOld( llvm_fn_ty.toLlvm(&o.builder), fn_val, ¶ms, @@ -9283,7 +9453,7 @@ pub const FuncGen = struct { const params = [_]*llvm.Value{operand.toLlvm(&self.wip)}; const wrong_size_result = (try self.wip.unimplemented(llvm_operand_ty, "")).finish( - self.builder.buildCall( + self.builder.buildCallOld( llvm_fn_ty.toLlvm(&o.builder), fn_val, ¶ms, @@ -9331,7 +9501,7 @@ pub const FuncGen = struct { const params = [_]*llvm.Value{operand.toLlvm(&self.wip)}; const wrong_size_result = (try self.wip.unimplemented(llvm_operand_ty, "")).finish( - self.builder.buildCall( + self.builder.buildCallOld( llvm_fn_ty.toLlvm(&o.builder), fn_val, ¶ms, @@ -9389,16 +9559,15 @@ pub const FuncGen = struct { const enum_ty = self.typeOf(un_op); const llvm_fn = try self.getIsNamedEnumValueFunction(enum_ty); - const params = [_]*llvm.Value{operand.toLlvm(&self.wip)}; - return (try self.wip.unimplemented(.i1, "")).finish(self.builder.buildCall( - llvm_fn.typeOf(&o.builder).toLlvm(&o.builder), - llvm_fn.toLlvm(&o.builder), - ¶ms, - params.len, - .Fast, - .Auto, + return self.wip.call( + .normal, + .fastcc, + .none, + llvm_fn.typeOf(&o.builder), + llvm_fn.toValue(&o.builder), + &.{operand}, "", - ), &self.wip); + ); } fn getIsNamedEnumValueFunction(self: *FuncGen, enum_ty: Type) !Builder.Function.Index { @@ -9416,13 +9585,16 @@ pub const FuncGen = struct { fqn.fmt(&mod.intern_pool), }); + var attributes: Builder.FunctionAttributes.Wip = .{}; + defer attributes.deinit(&o.builder); + const fn_type = try o.builder.fnType(.i1, &.{ try o.lowerType(enum_type.tag_ty.toType()), }, .normal); - const fn_val = o.llvm_module.addFunction(llvm_fn_name.toSlice(&o.builder).?, fn_type.toLlvm(&o.builder)); + const fn_val = o.llvm_module.addFunction(llvm_fn_name.slice(&o.builder).?, fn_type.toLlvm(&o.builder)); fn_val.setLinkage(.Internal); fn_val.setFunctionCallConv(.Fast); - o.addCommonFnAttributes(fn_val); + try o.addCommonFnAttributes(&attributes, fn_val); var global = Builder.Global{ .linkage = .internal, @@ -9431,6 +9603,8 @@ pub const FuncGen = struct { }; var function = Builder.Function{ .global = @enumFromInt(o.builder.globals.count()), + .call_conv = .fastcc, + .attributes = try attributes.finish(&o.builder), }; try o.builder.llvm.globals.append(self.gpa, fn_val); _ = try o.builder.addGlobal(llvm_fn_name, global); @@ -9470,19 +9644,14 @@ pub const FuncGen = struct { const enum_ty = self.typeOf(un_op); const llvm_fn = try self.getEnumTagNameFunction(enum_ty); - const llvm_fn_ty = llvm_fn.typeOf(&o.builder); - const params = [_]*llvm.Value{operand.toLlvm(&self.wip)}; - return (try self.wip.unimplemented(llvm_fn_ty.functionReturn(&o.builder), "")).finish( - self.builder.buildCall( - llvm_fn_ty.toLlvm(&o.builder), - llvm_fn.toLlvm(&o.builder), - ¶ms, - params.len, - .Fast, - .Auto, - "", - ), - &self.wip, + return self.wip.call( + .normal, + .fastcc, + .none, + llvm_fn.typeOf(&o.builder), + llvm_fn.toValue(&o.builder), + &.{operand}, + "", ); } @@ -9499,16 +9668,19 @@ pub const FuncGen = struct { const fqn = try mod.declPtr(enum_type.decl).getFullyQualifiedName(mod); const llvm_fn_name = try o.builder.fmt("__zig_tag_name_{}", .{fqn.fmt(&mod.intern_pool)}); + var attributes: Builder.FunctionAttributes.Wip = .{}; + defer attributes.deinit(&o.builder); + const ret_ty = try o.lowerType(Type.slice_const_u8_sentinel_0); const usize_ty = try o.lowerType(Type.usize); const fn_type = try o.builder.fnType(ret_ty, &.{ try o.lowerType(enum_type.tag_ty.toType()), }, .normal); - const fn_val = o.llvm_module.addFunction(llvm_fn_name.toSlice(&o.builder).?, fn_type.toLlvm(&o.builder)); + const fn_val = o.llvm_module.addFunction(llvm_fn_name.slice(&o.builder).?, fn_type.toLlvm(&o.builder)); fn_val.setLinkage(.Internal); fn_val.setFunctionCallConv(.Fast); - o.addCommonFnAttributes(fn_val); + try o.addCommonFnAttributes(&attributes, fn_val); var global = Builder.Global{ .linkage = .internal, @@ -9517,6 +9689,8 @@ pub const FuncGen = struct { }; var function = Builder.Function{ .global = @enumFromInt(o.builder.globals.count()), + .call_conv = .fastcc, + .attributes = try attributes.finish(&o.builder), }; try o.builder.llvm.globals.append(self.gpa, fn_val); gop.value_ptr.* = try o.builder.addGlobal(llvm_fn_name, global); @@ -9561,7 +9735,7 @@ pub const FuncGen = struct { const slice_val = try o.builder.structValue(ret_ty, &.{ global_index.toConst(), - try o.builder.intConst(usize_ty, name.toSlice(&o.builder).?.len), + try o.builder.intConst(usize_ty, name.slice(&o.builder).?.len), }); const return_block = try wip.block(1, "Name"); @@ -9590,11 +9764,14 @@ pub const FuncGen = struct { // Function signature: fn (anyerror) bool const fn_type = try o.builder.fnType(.i1, &.{Builder.Type.err_int}, .normal); - const llvm_fn = o.llvm_module.addFunction(name.toSlice(&o.builder).?, fn_type.toLlvm(&o.builder)); + const llvm_fn = o.llvm_module.addFunction(name.slice(&o.builder).?, fn_type.toLlvm(&o.builder)); + + var attributes: Builder.FunctionAttributes.Wip = .{}; + defer attributes.deinit(&o.builder); llvm_fn.setLinkage(.Internal); llvm_fn.setFunctionCallConv(.Fast); - o.addCommonFnAttributes(llvm_fn); + try o.addCommonFnAttributes(&attributes, llvm_fn); var global = Builder.Global{ .linkage = .internal, @@ -9603,6 +9780,8 @@ pub const FuncGen = struct { }; var function = Builder.Function{ .global = @enumFromInt(o.builder.globals.count()), + .call_conv = .fastcc, + .attributes = try attributes.finish(&o.builder), }; try o.builder.llvm.globals.append(self.gpa, llvm_fn); @@ -9731,18 +9910,14 @@ pub const FuncGen = struct { // accum = f(accum, vec[i]); const accum = try self.wip.load(.normal, llvm_result_ty, accum_ptr, .default, ""); const element = try self.wip.extractElement(operand_vector, i, ""); - const params = [2]*llvm.Value{ accum.toLlvm(&self.wip), element.toLlvm(&self.wip) }; - const new_accum = (try self.wip.unimplemented(llvm_result_ty, "")).finish( - self.builder.buildCall( - llvm_fn.typeOf(&o.builder).toLlvm(&o.builder), - llvm_fn.toLlvm(&o.builder), - ¶ms, - params.len, - .C, - .Auto, - "", - ), - &self.wip, + const new_accum = try self.wip.call( + .normal, + .ccc, + .none, + llvm_fn.typeOf(&o.builder), + llvm_fn.toValue(&o.builder), + &.{ accum, element }, + "", ); _ = try self.wip.store(.normal, new_accum, accum_ptr, .default); @@ -10190,7 +10365,7 @@ pub const FuncGen = struct { (try o.builder.intConst(.i32, prefetch.locality)).toLlvm(&o.builder), (try o.builder.intConst(.i32, @intFromEnum(prefetch.cache))).toLlvm(&o.builder), }; - _ = (try self.wip.unimplemented(.void, "")).finish(self.builder.buildCall( + _ = (try self.wip.unimplemented(.void, "")).finish(self.builder.buildCallOld( llvm_fn_ty.toLlvm(&o.builder), fn_val, ¶ms, @@ -10222,7 +10397,7 @@ pub const FuncGen = struct { const args: [0]*llvm.Value = .{}; const llvm_fn = try self.getIntrinsic(llvm_fn_name, &.{}); - return (try self.wip.unimplemented(.i32, "")).finish(self.builder.buildCall( + return (try self.wip.unimplemented(.i32, "")).finish(self.builder.buildCallOld( (try o.builder.fnType(.i32, &.{}, .normal)).toLlvm(&o.builder), llvm_fn, &args, @@ -10252,12 +10427,15 @@ pub const FuncGen = struct { const dimension = pl_op.payload; if (dimension >= 3) return o.builder.intValue(.i32, 1); + var attributes: Builder.FunctionAttributes.Wip = .{}; + defer attributes.deinit(&o.builder); + // Fetch the dispatch pointer, which points to this structure: // https://github.com/RadeonOpenCompute/ROCR-Runtime/blob/adae6c61e10d371f7cbc3d0e94ae2c070cab18a4/src/inc/hsa.h#L2913 const llvm_fn = try self.getIntrinsic("llvm.amdgcn.dispatch.ptr", &.{}); const args: [0]*llvm.Value = .{}; const llvm_ret_ty = try o.builder.ptrType(Builder.AddrSpace.amdgpu.constant); - const dispatch_ptr = (try self.wip.unimplemented(llvm_ret_ty, "")).finish(self.builder.buildCall( + const dispatch_ptr = (try self.wip.unimplemented(llvm_ret_ty, "")).finish(self.builder.buildCallOld( (try o.builder.fnType(llvm_ret_ty, &.{}, .normal)).toLlvm(&o.builder), llvm_fn, &args, @@ -10266,6 +10444,9 @@ pub const FuncGen = struct { .Auto, "", ), &self.wip); + try attributes.addRetAttr(.{ + .@"align" = comptime Builder.Alignment.fromByteUnits(4), + }, &o.builder); o.addAttrInt(dispatch_ptr.toLlvm(&self.wip), 0, "align", 4); // Load the work_group_* member from the struct as u16. @@ -10298,7 +10479,7 @@ pub const FuncGen = struct { const undef_init = try o.builder.undefConst(.ptr); // TODO: Address space const name = try o.builder.string("__zig_err_name_table"); - const error_name_table_global = o.llvm_module.addGlobal(Builder.Type.ptr.toLlvm(&o.builder), name.toSlice(&o.builder).?); + const error_name_table_global = o.llvm_module.addGlobal(Builder.Type.ptr.toLlvm(&o.builder), name.slice(&o.builder).?); error_name_table_global.setInitializer(undef_init.toLlvm(&o.builder)); error_name_table_global.setLinkage(.Private); error_name_table_global.setGlobalConstant(.True); @@ -10751,7 +10932,7 @@ pub const FuncGen = struct { ); const call = (try fg.wip.unimplemented(llvm_usize, "")).finish( - fg.builder.buildCall(fn_llvm_ty, asm_fn, &args, args.len, .C, .Auto, ""), + fg.builder.buildCallOld(fn_llvm_ty, asm_fn, &args, args.len, .C, .Auto, ""), &fg.wip, ); return call; @@ -10991,33 +11172,33 @@ fn toLlvmAtomicRmwBinOp( }; } -fn toLlvmCallConv(cc: std.builtin.CallingConvention, target: std.Target) llvm.CallConv { +fn toLlvmCallConv(cc: std.builtin.CallingConvention, target: std.Target) Builder.CallConv { return switch (cc) { - .Unspecified, .Inline, .Async => .Fast, - .C, .Naked => .C, - .Stdcall => .X86_StdCall, - .Fastcall => .X86_FastCall, + .Unspecified, .Inline, .Async => .fastcc, + .C, .Naked => .ccc, + .Stdcall => .x86_stdcallcc, + .Fastcall => .x86_fastcallcc, .Vectorcall => return switch (target.cpu.arch) { - .x86, .x86_64 => .X86_VectorCall, - .aarch64, .aarch64_be, .aarch64_32 => .AArch64_VectorCall, + .x86, .x86_64 => .x86_vectorcallcc, + .aarch64, .aarch64_be, .aarch64_32 => .aarch64_vector_pcs, else => unreachable, }, - .Thiscall => .X86_ThisCall, - .APCS => .ARM_APCS, - .AAPCS => .ARM_AAPCS, - .AAPCSVFP => .ARM_AAPCS_VFP, + .Thiscall => .x86_thiscallcc, + .APCS => .arm_apcscc, + .AAPCS => .arm_aapcscc, + .AAPCSVFP => .arm_aapcs_vfpcc, .Interrupt => return switch (target.cpu.arch) { - .x86, .x86_64 => .X86_INTR, - .avr => .AVR_INTR, - .msp430 => .MSP430_INTR, + .x86, .x86_64 => .x86_intrcc, + .avr => .avr_intrcc, + .msp430 => .msp430_intrcc, else => unreachable, }, - .Signal => .AVR_SIGNAL, - .SysV => .X86_64_SysV, - .Win64 => .Win64, + .Signal => .avr_signalcc, + .SysV => .x86_64_sysvcc, + .Win64 => .win64cc, .Kernel => return switch (target.cpu.arch) { - .nvptx, .nvptx64 => .PTX_Kernel, - .amdgcn => .AMDGPU_KERNEL, + .nvptx, .nvptx64 => .ptx_kernel, + .amdgcn => .amdgpu_kernel, else => unreachable, }, }; diff --git a/src/codegen/llvm/Builder.zig b/src/codegen/llvm/Builder.zig index 0bb65b22942b..1026b9c88ee1 100644 --- a/src/codegen/llvm/Builder.zig +++ b/src/codegen/llvm/Builder.zig @@ -4,13 +4,15 @@ strip: bool, llvm: if (build_options.have_llvm) struct { context: *llvm.Context, - module: ?*llvm.Module = null, - target: ?*llvm.Target = null, - di_builder: ?*llvm.DIBuilder = null, - di_compile_unit: ?*llvm.DICompileUnit = null, - types: std.ArrayListUnmanaged(*llvm.Type) = .{}, - globals: std.ArrayListUnmanaged(*llvm.Value) = .{}, - constants: std.ArrayListUnmanaged(*llvm.Value) = .{}, + 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), } else void, source_filename: String, @@ -18,8 +20,8 @@ data_layout: String, target_triple: String, string_map: std.AutoArrayHashMapUnmanaged(void, void), -string_bytes: std.ArrayListUnmanaged(u8), string_indices: std.ArrayListUnmanaged(u32), +string_bytes: std.ArrayListUnmanaged(u8), types: std.AutoArrayHashMapUnmanaged(String, Type), next_unnamed_type: String, @@ -28,6 +30,11 @@ type_map: std.AutoArrayHashMapUnmanaged(void, void), type_items: std.ArrayListUnmanaged(Type.Item), type_extra: std.ArrayListUnmanaged(u32), +attributes: std.AutoArrayHashMapUnmanaged(Attribute.Storage, void), +attributes_map: std.AutoArrayHashMapUnmanaged(void, void), +attributes_indices: std.ArrayListUnmanaged(u32), +attributes_extra: std.ArrayListUnmanaged(u32), + globals: std.AutoArrayHashMapUnmanaged(String, Global), next_unnamed_global: String, next_replaced_global: String, @@ -41,6 +48,7 @@ constant_items: std.MultiArrayList(Constant.Item), constant_extra: std.ArrayListUnmanaged(u32), constant_limbs: std.ArrayListUnmanaged(std.math.big.Limb), +pub const expected_args_len = 16; pub const expected_fields_len = 32; pub const expected_gep_indices_len = 8; pub const expected_cases_len = 8; @@ -65,7 +73,7 @@ pub const String = enum(u32) { return self.toIndex() == null; } - pub fn toSlice(self: String, b: *const Builder) ?[:0]const u8 { + pub fn slice(self: String, b: *const Builder) ?[:0]const u8 { const index = self.toIndex() orelse return null; const start = b.string_indices.items[index]; const end = b.string_indices.items[index + 1]; @@ -85,9 +93,9 @@ pub const String = enum(u32) { if (comptime std.mem.indexOfNone(u8, fmt_str, "@\"")) |_| @compileError("invalid format string: '" ++ fmt_str ++ "'"); assert(data.string != .none); - const slice = data.string.toSlice(data.builder) orelse + const sentinel_slice = data.string.slice(data.builder) orelse return writer.print("{d}", .{@intFromEnum(data.string)}); - const full_slice = slice[0 .. slice.len + comptime @intFromBool( + const full_slice = sentinel_slice[0 .. sentinel_slice.len + comptime @intFromBool( std.mem.indexOfScalar(u8, fmt_str, '@') != null, )]; const need_quotes = (comptime std.mem.indexOfScalar(u8, fmt_str, '"') != null) or @@ -108,6 +116,7 @@ pub const String = enum(u32) { return @enumFromInt(@as(u32, @intCast((index orelse return .none) + @intFromEnum(String.empty)))); } + fn toIndex(self: String) ?usize { return std.math.sub(u32, @intFromEnum(self), @intFromEnum(String.empty)) catch null; } @@ -118,7 +127,7 @@ pub const String = enum(u32) { return @truncate(std.hash.Wyhash.hash(0, key)); } pub fn eql(ctx: Adapter, lhs_key: []const u8, _: void, rhs_index: usize) bool { - return std.mem.eql(u8, lhs_key, String.fromIndex(rhs_index).toSlice(ctx.builder).?); + return std.mem.eql(u8, lhs_key, String.fromIndex(rhs_index).slice(ctx.builder).?); } }; }; @@ -290,6 +299,17 @@ pub const Type = enum(u32) { }; } + pub fn pointerAddrSpace(self: Type, builder: *const Builder) AddrSpace { + switch (self) { + .ptr => return .default, + else => { + const item = builder.type_items.items[@intFromEnum(self)]; + assert(item.tag == .pointer); + return @enumFromInt(item.data); + }, + } + } + pub fn isFunction(self: Type, builder: *const Builder) bool { return switch (self.tag(builder)) { .function, .vararg_function => true, @@ -606,7 +626,7 @@ pub const Type = enum(u32) { var extra = data.builder.typeExtraDataTrail(Type.Target, item.data); const types = extra.trail.next(extra.data.types_len, Type, data.builder); const ints = extra.trail.next(extra.data.ints_len, u32, data.builder); - try writer.print("t{s}", .{extra.data.name.toSlice(data.builder).?}); + try writer.print("t{s}", .{extra.data.name.slice(data.builder).?}); for (types) |ty| try writer.print("_{m}", .{ty.fmt(data.builder)}); for (ints) |int| try writer.print("_{d}", .{int}); try writer.writeByte('t'); @@ -641,7 +661,7 @@ pub const Type = enum(u32) { .named_structure => { const extra = data.builder.typeExtraData(Type.NamedStructure, item.data); try writer.writeAll("s_"); - if (extra.id.toSlice(data.builder)) |id| try writer.writeAll(id); + if (extra.id.slice(data.builder)) |id| try writer.writeAll(id); }, } return; @@ -823,6 +843,789 @@ pub const Type = enum(u32) { } }; +pub const Attribute = union(Kind) { + // Parameter Attributes + zeroext, + signext, + inreg, + byval: Type, + byref: Type, + preallocated: Type, + inalloca: Type, + sret: Type, + elementtype: Type, + @"align": Alignment, + @"noalias", + nocapture, + nofree, + nest, + returned, + nonnull, + dereferenceable: u32, + dereferenceable_or_null: u32, + swiftself, + swiftasync, + swifterror, + immarg, + noundef, + nofpclass: FpClass, + alignstack: Alignment, + allocalign, + allocptr, + readnone, + readonly, + writeonly, + + // Function Attributes + //alignstack: Alignment, + allockind: AllocKind, + allocsize: AllocSize, + alwaysinline, + builtin, + cold, + convergent, + disable_sanitizer_information, + fn_ret_thunk_extern, + hot, + inlinehint, + jumptable, + memory: Memory, + minsize, + naked, + nobuiltin, + nocallback, + noduplicate, + //nofree, + noimplicitfloat, + @"noinline", + nomerge, + nonlazybind, + noprofile, + skipprofile, + noredzone, + noreturn, + norecurse, + willreturn, + nosync, + nounwind, + nosanitize_bounds, + nosanitize_coverage, + null_pointer_is_valid, + optforfuzzing, + optnone, + optsize, + //preallocated: Type, + returns_twice, + safestack, + sanitize_address, + sanitize_memory, + sanitize_thread, + sanitize_hwaddress, + sanitize_memtag, + speculative_load_hardening, + speculatable, + ssp, + sspstrong, + sspreq, + strictfp, + uwtable: UwTable, + nocf_check, + shadowcallstack, + mustprogress, + vscale_range: VScaleRange, + + // Global Attributes + no_sanitize_address, + no_sanitize_hwaddress, + //sanitize_memtag, + sanitize_address_dyninit, + + string: struct { kind: String, value: String }, + none: noreturn, + + pub const Index = enum(u32) { + _, + + pub fn getKind(self: Index, builder: *const Builder) Kind { + return self.toStorage(builder).kind; + } + + pub fn toAttribute(self: Index, builder: *const Builder) Attribute { + @setEvalBranchQuota(2_000); + const storage = self.toStorage(builder); + if (storage.kind.toString()) |kind| return .{ .string = .{ + .kind = kind, + .value = @enumFromInt(storage.value), + } } else return switch (storage.kind) { + inline .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, + //.alignstack, + .allockind, + .allocsize, + .alwaysinline, + .builtin, + .cold, + .convergent, + .disable_sanitizer_information, + .fn_ret_thunk_extern, + .hot, + .inlinehint, + .jumptable, + .memory, + .minsize, + .naked, + .nobuiltin, + .nocallback, + .noduplicate, + //.nofree, + .noimplicitfloat, + .@"noinline", + .nomerge, + .nonlazybind, + .noprofile, + .skipprofile, + .noredzone, + .noreturn, + .norecurse, + .willreturn, + .nosync, + .nounwind, + .nosanitize_bounds, + .nosanitize_coverage, + .null_pointer_is_valid, + .optforfuzzing, + .optnone, + .optsize, + //.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, + .no_sanitize_address, + .no_sanitize_hwaddress, + .sanitize_address_dyninit, + => |kind| { + const field = @typeInfo(Attribute).Union.fields[@intFromEnum(kind)]; + 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)), + }); + }, + .string, .none => unreachable, + _ => unreachable, + }; + } + + const FormatData = struct { + attribute_index: Index, + builder: *const Builder, + }; + fn format( + data: FormatData, + comptime fmt_str: []const u8, + _: std.fmt.FormatOptions, + writer: anytype, + ) @TypeOf(writer).Error!void { + if (comptime std.mem.indexOfNone(u8, fmt_str, "\"")) |_| + @compileError("invalid format string: '" ++ fmt_str ++ "'"); + const attribute = data.attribute_index.toAttribute(data.builder); + switch (attribute) { + .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 writer.print(" {s}", .{@tagName(attribute)}), + .byval, + .byref, + .preallocated, + .inalloca, + .sret, + .elementtype, + => |ty| try writer.print(" {s}({%})", .{ @tagName(attribute), ty.fmt(data.builder) }), + .@"align" => @panic("todo"), + .dereferenceable, + .dereferenceable_or_null, + => @panic("todo"), + .nofpclass => @panic("todo"), + .alignstack => @panic("todo"), + .allockind => @panic("todo"), + .allocsize => @panic("todo"), + .memory => @panic("todo"), + .uwtable => @panic("todo"), + .vscale_range => @panic("todo"), + .string => |string_attr| if (comptime std.mem.indexOfScalar(u8, fmt_str, '"') != null) { + try writer.print(" {\"}", .{string_attr.kind.fmt(data.builder)}); + if (string_attr.value != .empty) + try writer.print("={\"}", .{string_attr.value.fmt(data.builder)}); + }, + .none => unreachable, + } + } + pub fn fmt(self: Index, builder: *const Builder) std.fmt.Formatter(format) { + return .{ .data = .{ .attribute_index = self, .builder = builder } }; + } + + 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, + + // 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, + //nofree, + noimplicitfloat, + @"noinline", + nomerge, + nonlazybind, + noprofile, + skipprofile, + noredzone, + noreturn, + norecurse, + willreturn, + nosync, + nounwind, + nosanitize_bounds, + nosanitize_coverage, + null_pointer_is_valid, + optforfuzzing, + optnone, + optsize, + //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, + + // Global Attributes + no_sanitize_address, + no_sanitize_hwaddress, + //sanitize_memtag, + sanitize_address_dyninit, + + string = std.math.maxInt(u31) - 1, + none = std.math.maxInt(u31), + _, + + pub const len = @typeInfo(Kind).Enum.fields.len - 2; + + pub fn fromString(str: String) Kind { + assert(!str.isAnon()); + return @enumFromInt(@intFromEnum(str)); + } + + fn toString(self: Kind) ?String { + const str: String = @enumFromInt(@intFromEnum(self)); + return if (str.isAnon()) null else str; + } + }; + + pub const FpClass = packed struct(u32) { + signaling_nan: bool = false, + quiet_nan: bool = false, + negative_infinity: bool = false, + negative_normal: bool = false, + negative_subnormal: bool = false, + negative_zero: bool = false, + positive_zero: bool = false, + positive_subnormal: bool = false, + positive_normal: bool = false, + positive_infinity: bool = false, + _: u22 = 0, + + pub const nan = FpClass{ .signaling_nan = true, .quiet_nan = true }; + pub const inf = FpClass{ .negative_infinity = true, .positive_infinity = true }; + pub const norm = FpClass{ .positive_normal = true, .negative_normal = true }; + pub const sub = FpClass{ .positive_subnormal = true, .negative_subnormal = true }; + pub const zero = FpClass{ .positive_zero = true, .negative_zero = true }; + pub const all = FpClass{ + .signaling_nan = true, + .quiet_nan = true, + .negative_infinity = true, + .negative_normal = true, + .negative_subnormal = true, + .negative_zero = true, + .positive_zero = true, + .positive_subnormal = true, + .positive_normal = true, + .positive_infinity = true, + }; + pub const snan = FpClass{ .signaling_nan = true }; + pub const qnan = FpClass{ .quiet_nan = true }; + pub const ninf = FpClass{ .negative_infinity = true }; + pub const nnorm = FpClass{ .negative_normal = true }; + pub const nsub = FpClass{ .negative_subnormal = true }; + pub const nzero = FpClass{ .negative_zero = true }; + pub const pzero = FpClass{ .positive_zero = true }; + pub const psub = FpClass{ .positive_subnormal = true }; + pub const pnorm = FpClass{ .positive_normal = true }; + pub const pinf = FpClass{ .positive_infinity = true }; + }; + + pub const AllocKind = packed struct(u32) { + alloc: bool, + realloc: bool, + free: bool, + uninitialized: bool, + zeroed: bool, + aligned: bool, + _: u26 = 0, + }; + + pub const AllocSize = packed struct(u32) { + elem_size: u16, + num_elems: u16, + + pub const none = std.math.maxInt(u16); + + fn toLlvm(self: AllocSize) packed struct(u64) { num_elems: u32, elem_size: u32 } { + return .{ .num_elems = switch (self.num_elems) { + else => self.num_elems, + none => std.math.maxInt(u32), + }, .elem_size = self.elem_size }; + } + }; + + pub const Memory = packed struct(u32) { + argmem: Effect, + inaccessiblemem: Effect, + other: Effect, + _: u26 = 0, + + pub const Effect = enum(u2) { none, read, write, readwrite }; + }; + + pub const UwTable = enum(u32) { + none, + sync, + @"async", + + pub const default = UwTable.@"async"; + }; + + pub const VScaleRange = packed struct(u32) { + min: Alignment, + max: Alignment, + _: u20 = 0, + + fn toLlvm(self: VScaleRange) packed struct(u64) { max: u32, min: u32 } { + return .{ + .max = @intCast(self.max.toByteUnits() orelse 0), + .min = @intCast(self.min.toByteUnits().?), + }; + } + }; + + pub fn getKind(self: Attribute) Kind { + return switch (self) { + else => self, + .string => |string_attr| Kind.fromString(string_attr.kind), + }; + } + + const Storage = extern struct { + kind: Kind, + value: u32, + }; + + fn toStorage(self: Attribute) Storage { + return switch (self) { + inline else => |value| .{ .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))), + } }, + .string => |string_attr| .{ + .kind = Kind.fromString(string_attr.kind), + .value = @intFromEnum(string_attr.value), + }, + .none => unreachable, + }; + } +}; + +pub const Attributes = enum(u32) { + none, + _, + + pub fn slice(self: Attributes, builder: *const Builder) []const Attribute.Index { + const start = builder.attributes_indices.items[@intFromEnum(self)]; + const end = builder.attributes_indices.items[@intFromEnum(self) + 1]; + return @ptrCast(builder.attributes_extra.items[start..end]); + } + + const FormatData = struct { + attributes: Attributes, + builder: *const Builder, + }; + fn format( + data: FormatData, + comptime fmt_str: []const u8, + fmt_opts: std.fmt.FormatOptions, + writer: anytype, + ) @TypeOf(writer).Error!void { + for (data.attributes.slice(data.builder)) |attribute_index| try Attribute.Index.format(.{ + .attribute_index = attribute_index, + .builder = data.builder, + }, fmt_str, fmt_opts, writer); + } + pub fn fmt(self: Attributes, builder: *const Builder) std.fmt.Formatter(format) { + return .{ .data = .{ .attributes = self, .builder = builder } }; + } +}; + +pub const FunctionAttributes = enum(u32) { + none, + _, + + const function_index = 0; + const return_index = 1; + const params_index = 2; + + pub const Wip = struct { + maps: Maps = .{}, + + const Map = std.AutoArrayHashMapUnmanaged(Attribute.Kind, Attribute.Index); + const Maps = std.ArrayListUnmanaged(Map); + + pub fn deinit(self: *Wip, builder: *const Builder) void { + for (self.maps.items) |*map| map.deinit(builder.gpa); + self.maps.deinit(builder.gpa); + self.* = undefined; + } + + pub fn addFnAttr(self: *Wip, attribute: Attribute, builder: *Builder) Allocator.Error!void { + try self.addAttr(function_index, attribute, builder); + } + + pub fn addFnAttrIndex( + self: *Wip, + attribute_index: Attribute.Index, + builder: *const Builder, + ) Allocator.Error!void { + try self.addAttrIndex(function_index, attribute_index, builder); + } + + pub fn removeFnAttr(self: *Wip, attribute_kind: Attribute.Kind) Allocator.Error!bool { + return self.removeAttr(function_index, attribute_kind); + } + + pub fn addRetAttr(self: *Wip, attribute: Attribute, builder: *Builder) Allocator.Error!void { + try self.addAttr(return_index, attribute, builder); + } + + pub fn addRetAttrIndex( + self: *Wip, + attribute_index: Attribute.Index, + builder: *const Builder, + ) Allocator.Error!void { + try self.addAttrIndex(return_index, attribute_index, builder); + } + + pub fn removeRetAttr(self: *Wip, attribute_kind: Attribute.Kind) Allocator.Error!bool { + return self.removeAttr(return_index, attribute_kind); + } + + pub fn addParamAttr( + self: *Wip, + param_index: usize, + attribute: Attribute, + builder: *Builder, + ) Allocator.Error!void { + try self.addAttr(params_index + param_index, attribute, builder); + } + + pub fn addParamAttrIndex( + self: *Wip, + param_index: usize, + attribute_index: Attribute.Index, + builder: *const Builder, + ) Allocator.Error!void { + try self.addAttrIndex(params_index + param_index, attribute_index, builder); + } + + pub fn removeParamAttr( + self: *Wip, + param_index: usize, + attribute_kind: Attribute.Kind, + ) Allocator.Error!bool { + return self.removeAttr(params_index + param_index, attribute_kind); + } + + pub fn finish(self: *const Wip, builder: *Builder) Allocator.Error!FunctionAttributes { + const attributes = try builder.gpa.alloc(Attributes, self.maps.items.len); + defer builder.gpa.free(attributes); + for (attributes, self.maps.items) |*attribute, map| + attribute.* = try builder.attrs(map.values()); + return builder.fnAttrs(attributes); + } + + fn addAttr( + self: *Wip, + index: usize, + attribute: Attribute, + builder: *Builder, + ) Allocator.Error!void { + const map = try self.getOrPutMap(builder.gpa, index); + try map.put(builder.gpa, attribute.getKind(), try builder.attr(attribute)); + } + + fn addAttrIndex( + self: *Wip, + index: usize, + attribute_index: Attribute.Index, + builder: *const Builder, + ) Allocator.Error!void { + const map = try self.getOrPutMap(builder.gpa, index); + try map.put(builder.gpa, attribute_index.getKind(builder), attribute_index); + } + + fn removeAttr(self: *Wip, index: usize, attribute_kind: Attribute.Kind) Allocator.Error!bool { + const map = self.getMap(index) orelse return false; + return map.swapRemove(attribute_kind); + } + + fn getOrPutMap(self: *Wip, allocator: Allocator, index: usize) Allocator.Error!*Map { + if (index >= self.maps.items.len) + try self.maps.appendNTimes(allocator, .{}, index + 1 - self.maps.items.len); + return &self.maps.items[index]; + } + + fn getMap(self: *Wip, index: usize) ?*Map { + return if (index >= self.maps.items.len) null else &self.maps.items[index]; + } + + fn ensureTotalLength(self: *Wip, new_len: usize) Allocator.Error!void { + try self.maps.appendNTimes( + .{}, + std.math.sub(usize, new_len, self.maps.items.len) catch return, + ); + } + }; + + pub fn func(self: FunctionAttributes, builder: *const Builder) Attributes { + return self.get(function_index, builder); + } + + pub fn ret(self: FunctionAttributes, builder: *const Builder) Attributes { + return self.get(return_index, builder); + } + + pub fn param(self: FunctionAttributes, param_index: usize, builder: *const Builder) Attributes { + return self.get(params_index + param_index, builder); + } + + pub fn toWip(self: FunctionAttributes, builder: *const Builder) Allocator.Error!Wip { + var wip: Wip = .{}; + errdefer wip.deinit(builder); + const attributes_slice = self.slice(builder); + try wip.maps.ensureTotalCapacityPrecise(builder.gpa, attributes_slice.len); + for (attributes_slice) |attributes| { + const map = wip.maps.addOneAssumeCapacity(); + map.* = .{}; + const attribute_slice = attributes.slice(builder); + try map.ensureTotalCapacity(builder.gpa, attribute_slice.len); + for (attributes.slice(builder)) |attribute| + map.putAssumeCapacityNoClobber(attribute.getKind(builder), attribute); + } + return wip; + } + + fn get(self: FunctionAttributes, index: usize, builder: *const Builder) Attributes { + const attribute_slice = self.slice(builder); + return if (index < attribute_slice.len) attribute_slice[index] else .none; + } + + fn slice(self: FunctionAttributes, builder: *const Builder) []const Attributes { + const start = builder.attributes_indices.items[@intFromEnum(self)]; + const end = builder.attributes_indices.items[@intFromEnum(self) + 1]; + return @ptrCast(builder.attributes_extra.items[start..end]); + } +}; + pub const Linkage = enum { external, private, @@ -1053,6 +1856,127 @@ pub const Alignment = enum(u6) { } }; +pub const CallConv = enum(u10) { + ccc, + + fastcc = 8, + coldcc, + ghccc, + + webkit_jscc = 12, + anyregcc, + preserve_mostcc, + preserve_allcc, + swiftcc, + cxx_fast_tlscc, + tailcc, + cfguard_checkcc, + swifttailcc, + + x86_stdcallcc = 64, + x86_fastcallcc, + arm_apcscc, + arm_aapcscc, + arm_aapcs_vfpcc, + msp430_intrcc, + x86_thiscallcc, + ptx_kernel, + ptx_device, + + spir_func = 75, + spir_kernel, + intel_ocl_bicc, + x86_64_sysvcc, + win64cc, + x86_vectorcallcc, + hhvmcc, + hhvm_ccc, + x86_intrcc, + avr_intrcc, + avr_signalcc, + + amdgpu_vs = 87, + amdgpu_gs, + amdgpu_ps, + amdgpu_cs, + amdgpu_kernel, + x86_regcallcc, + amdgpu_hs, + + amdgpu_ls = 95, + amdgpu_es, + aarch64_vector_pcs, + aarch64_sve_vector_pcs, + + amdgpu_gfx = 100, + + aarch64_sme_preservemost_from_x0 = 102, + aarch64_sme_preservemost_from_x2, + + _, + + pub const default = CallConv.ccc; + + pub fn format( + self: CallConv, + comptime _: []const u8, + _: std.fmt.FormatOptions, + writer: anytype, + ) @TypeOf(writer).Error!void { + switch (self) { + .ccc => {}, + .fastcc, + .coldcc, + .ghccc, + .webkit_jscc, + .anyregcc, + .preserve_mostcc, + .preserve_allcc, + .swiftcc, + .cxx_fast_tlscc, + .tailcc, + .cfguard_checkcc, + .swifttailcc, + .x86_stdcallcc, + .x86_fastcallcc, + .arm_apcscc, + .arm_aapcscc, + .arm_aapcs_vfpcc, + .msp430_intrcc, + .x86_thiscallcc, + .ptx_kernel, + .ptx_device, + .spir_func, + .spir_kernel, + .intel_ocl_bicc, + .x86_64_sysvcc, + .win64cc, + .x86_vectorcallcc, + .hhvmcc, + .hhvm_ccc, + .x86_intrcc, + .avr_intrcc, + .avr_signalcc, + .amdgpu_vs, + .amdgpu_gs, + .amdgpu_ps, + .amdgpu_cs, + .amdgpu_kernel, + .x86_regcallcc, + .amdgpu_hs, + .amdgpu_ls, + .amdgpu_es, + .aarch64_vector_pcs, + .aarch64_sve_vector_pcs, + .amdgpu_gfx, + .aarch64_sme_preservemost_from_x0, + .aarch64_sme_preservemost_from_x2, + => try writer.print(" {s}", .{@tagName(self)}), + _ => try writer.print(" cc{d}", .{@intFromEnum(self)}), + } + } +}; + pub const Global = struct { linkage: Linkage = .external, preemption: Preemption = .dso_preemptable, @@ -1170,7 +2094,7 @@ pub const Global = struct { fn updateName(self: Index, builder: *const Builder) void { if (!builder.useLibLlvm()) return; const index = @intFromEnum(self.unwrap(builder)); - const name_slice = self.name(builder).toSlice(builder) orelse ""; + const name_slice = self.name(builder).slice(builder) orelse ""; builder.llvm.globals.items[index].setValueName2(name_slice.ptr, name_slice.len); } @@ -1301,6 +2225,8 @@ pub const Variable = struct { pub const Function = struct { global: Global.Index, + call_conv: CallConv = CallConv.default, + attributes: FunctionAttributes = .none, section: String = .none, alignment: Alignment = .default, blocks: []const Block = &.{}, @@ -1364,6 +2290,8 @@ pub const Function = struct { block, br, br_cond, + call, + @"call fast", extractelement, extractvalue, fadd, @@ -1454,6 +2382,10 @@ pub const Function = struct { @"mul nsw", @"mul nuw", @"mul nuw nsw", + @"musttail call", + @"musttail call fast", + @"notail call", + @"notail call fast", @"or", phi, @"phi fast", @@ -1481,6 +2413,8 @@ pub const Function = struct { @"sub nuw", @"sub nuw nsw", @"switch", + @"tail call", + @"tail call fast", trunc, udiv, @"udiv exact", @@ -1530,6 +2464,15 @@ pub const Function = struct { .@"store volatile", .@"unreachable", => false, + .call, + .@"call fast", + .@"musttail call", + .@"musttail call fast", + .@"notail call", + .@"notail call fast", + .@"tail call", + .@"tail call fast", + => self.typeOfWip(wip) != .void, else => true, }; } @@ -1625,6 +2568,15 @@ pub const Function = struct { .@"switch", .@"unreachable", => .none, + .call, + .@"call fast", + .@"musttail call", + .@"musttail call fast", + .@"notail call", + .@"notail call fast", + .@"tail call", + .@"tail call fast", + => wip.extraData(Call, instruction.data).ty.functionReturn(wip.builder), .extractelement => wip.extraData(ExtractElement, instruction.data) .val.typeOfWip(wip).childType(wip.builder), .extractvalue => { @@ -1813,6 +2765,15 @@ pub const Function = struct { .@"switch", .@"unreachable", => .none, + .call, + .@"call fast", + .@"musttail call", + .@"musttail call fast", + .@"notail call", + .@"notail call fast", + .@"tail call", + .@"tail call fast", + => function.extraData(Call, instruction.data).ty.functionReturn(builder), .extractelement => function.extraData(ExtractElement, instruction.data) .val.typeOf(function_index, builder).childType(builder), .extractvalue => { @@ -1955,7 +2916,7 @@ pub const Function = struct { return if (wip.builder.strip) "" else - wip.names.items[@intFromEnum(self)].toSlice(wip.builder).?; + wip.names.items[@intFromEnum(self)].slice(wip.builder).?; } }; @@ -2063,6 +3024,30 @@ pub const Function = struct { rhs: Value, }; + pub const Call = struct { + info: Info, + attributes: FunctionAttributes, + ty: Type, + callee: Value, + args_len: u32, + //args: [args_len]Value, + + pub const Kind = enum { + normal, + fast, + musttail, + musttail_fast, + notail, + notail_fast, + tail, + tail_fast, + }; + pub const Info = packed struct(u32) { + call_conv: CallConv, + _: u22 = undefined, + }; + }; + pub const VaArg = struct { list: Value, type: Type, @@ -2117,8 +3102,17 @@ pub const Function = struct { inline for (fields, self.extra[index..][0..fields.len]) |field, value| @field(result, field.name) = switch (field.type) { u32 => value, - Alignment, AtomicOrdering, Block.Index, Type, Value => @enumFromInt(value), - MemoryAccessInfo, Instruction.Alloca.Info => @bitCast(value), + Alignment, + AtomicOrdering, + Block.Index, + FunctionAttributes, + Type, + Value, + => @enumFromInt(value), + MemoryAccessInfo, + Instruction.Alloca.Info, + Instruction.Call.Info, + => @bitCast(value), else => @compileError("bad field type: " ++ @typeName(field.type)), }; return .{ @@ -2243,7 +3237,7 @@ pub const WipFunction = struct { if (self.builder.useLibLlvm()) self.llvm.blocks.appendAssumeCapacity( self.builder.llvm.context.appendBasicBlock( self.function.toLlvm(self.builder), - final_name.toSlice(self.builder).?, + final_name.slice(self.builder).?, ), ); return index; @@ -3162,6 +4156,88 @@ pub const WipFunction = struct { return self.selectTag(.@"select fast", cond, lhs, rhs, name); } + pub fn call( + self: *WipFunction, + kind: Instruction.Call.Kind, + call_conv: CallConv, + function_attributes: FunctionAttributes, + ty: Type, + callee: Value, + args: []const Value, + name: []const u8, + ) if (build_options.have_llvm) Allocator.Error!Value else Value { + const ret_ty = ty.functionReturn(self.builder); + assert(ty.isFunction(self.builder)); + assert(callee.typeOfWip(self).isPointer(self.builder)); + const params = ty.functionParameters(self.builder); + for (params, args[0..params.len]) |param, arg_val| assert(param == arg_val.typeOfWip(self)); + + try self.ensureUnusedExtraCapacity(1, Instruction.Call, args.len); + const instruction = try self.addInst(switch (ret_ty) { + .void => null, + else => name, + }, .{ + .tag = .call, + .data = self.addExtraAssumeCapacity(Instruction.Call{ + .info = .{ .call_conv = call_conv }, + .attributes = function_attributes, + .ty = ty, + .callee = callee, + .args_len = @intCast(args.len), + }), + }); + 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(@enumFromInt(@intFromEnum(call_conv))); + 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| { + const attribute_index = @as(llvm.AttributeIndex, @intCast(index)) -% 1; + for (attributes.slice(self.builder)) |attribute| llvm_instruction.addCallSiteAttribute( + attribute_index, + attribute.toLlvm(self.builder), + ); + } + self.llvm.instructions.appendAssumeCapacity(llvm_instruction); + } + return instruction.toValue(); + } + pub fn vaArg(self: *WipFunction, list: Value, ty: Type, name: []const u8) Allocator.Error!Value { try self.ensureUnusedExtraCapacity(1, Instruction.VaArg, 0); const instruction = try self.addInst(name, .{ @@ -3246,8 +4322,17 @@ pub const WipFunction = struct { const value = @field(extra, field.name); wip_extra.items[wip_extra.index] = switch (field.type) { u32 => value, - Alignment, AtomicOrdering, Block.Index, Type, Value => @intFromEnum(value), - MemoryAccessInfo, Instruction.Alloca.Info => @bitCast(value), + Alignment, + AtomicOrdering, + Block.Index, + FunctionAttributes, + Type, + Value, + => @intFromEnum(value), + MemoryAccessInfo, + Instruction.Alloca.Info, + Instruction.Call.Info, + => @bitCast(value), else => @compileError("bad field type: " ++ @typeName(field.type)), }; wip_extra.index += 1; @@ -3256,13 +4341,14 @@ pub const WipFunction = struct { } fn appendSlice(wip_extra: *@This(), slice: anytype) void { - if (@typeInfo(@TypeOf(slice)).Pointer.child == Value) @compileError("use appendValues"); + if (@typeInfo(@TypeOf(slice)).Pointer.child == Value) + @compileError("use appendMappedValues"); const data: []const u32 = @ptrCast(slice); @memcpy(wip_extra.items[wip_extra.index..][0..data.len], data); wip_extra.index += @intCast(data.len); } - fn appendValues(wip_extra: *@This(), vals: []const Value, ctx: anytype) void { + fn appendMappedValues(wip_extra: *@This(), vals: []const Value, ctx: anytype) void { for (wip_extra.items[wip_extra.index..][0..vals.len], vals) |*extra, val| extra.* = @intFromEnum(ctx.map(val)); wip_extra.index += @intCast(vals.len); @@ -3494,6 +4580,26 @@ pub const WipFunction = struct { .@"else" = extra.@"else", }); }, + .call, + .@"call fast", + .@"musttail call", + .@"musttail call fast", + .@"notail call", + .@"notail call fast", + .@"tail call", + .@"tail call fast", + => { + var extra = self.extraDataTrail(Instruction.Call, instruction.data); + const args = extra.trail.next(extra.data.args_len, Value, self); + instruction.data = wip_extra.addExtra(Instruction.Call{ + .info = extra.data.info, + .attributes = extra.data.attributes, + .ty = extra.data.ty, + .callee = instructions.map(extra.data.callee), + .args_len = extra.data.args_len, + }); + wip_extra.appendMappedValues(args, instructions); + }, .extractvalue => { var extra = self.extraDataTrail(Instruction.ExtractValue, instruction.data); const indices = extra.trail.next(extra.data.indices_len, u32, self); @@ -3517,7 +4623,7 @@ pub const WipFunction = struct { .base = instructions.map(extra.data.base), .indices_len = extra.data.indices_len, }); - wip_extra.appendValues(indices, instructions); + wip_extra.appendMappedValues(indices, instructions); }, .insertelement => { const extra = self.extraData(Instruction.InsertElement, instruction.data); @@ -3559,7 +4665,7 @@ pub const WipFunction = struct { instruction.data = wip_extra.addExtra(Instruction.Phi{ .type = extra.data.type, }); - wip_extra.appendValues(incoming_vals, instructions); + wip_extra.appendMappedValues(incoming_vals, instructions); wip_extra.appendSlice(incoming_blocks); }, .select, @@ -3932,8 +5038,17 @@ pub const WipFunction = struct { const value = @field(extra, field.name); self.extra.appendAssumeCapacity(switch (field.type) { u32 => value, - Alignment, AtomicOrdering, Block.Index, Type, Value => @intFromEnum(value), - MemoryAccessInfo, Instruction.Alloca.Info => @bitCast(value), + Alignment, + AtomicOrdering, + Block.Index, + FunctionAttributes, + Type, + Value, + => @intFromEnum(value), + MemoryAccessInfo, + Instruction.Alloca.Info, + Instruction.Call.Info, + => @bitCast(value), else => @compileError("bad field type: " ++ @typeName(field.type)), }); } @@ -3971,8 +5086,17 @@ pub const WipFunction = struct { inline for (fields, self.extra.items[index..][0..fields.len]) |field, value| @field(result, field.name) = switch (field.type) { u32 => value, - Alignment, AtomicOrdering, Block.Index, Type, Value => @enumFromInt(value), - MemoryAccessInfo, Instruction.Alloca.Info => @bitCast(value), + Alignment, + AtomicOrdering, + Block.Index, + FunctionAttributes, + Type, + Value, + => @enumFromInt(value), + MemoryAccessInfo, + Instruction.Alloca.Info, + Instruction.Call.Info, + => @bitCast(value), else => @compileError("bad field type: " ++ @typeName(field.type)), }; return .{ @@ -4294,7 +5418,7 @@ pub const Constant = enum(u32) { .string, .string_null, => builder.arrayTypeAssumeCapacity( - @as(String, @enumFromInt(item.data)).toSlice(builder).?.len + + @as(String, @enumFromInt(item.data)).slice(builder).?.len + @intFromBool(item.tag == .string_null), .i8, ), @@ -4821,8 +5945,8 @@ pub fn init(options: Options) InitError!Builder { .target_triple = .none, .string_map = .{}, - .string_bytes = .{}, .string_indices = .{}, + .string_bytes = .{}, .types = .{}, .next_unnamed_type = @enumFromInt(0), @@ -4831,6 +5955,11 @@ pub fn init(options: Options) InitError!Builder { .type_items = .{}, .type_extra = .{}, + .attributes = .{}, + .attributes_map = .{}, + .attributes_indices = .{}, + .attributes_extra = .{}, + .globals = .{}, .next_unnamed_global = @enumFromInt(0), .next_replaced_global = .none, @@ -4844,7 +5973,18 @@ pub fn init(options: Options) InitError!Builder { .constant_extra = .{}, .constant_limbs = .{}, }; - if (self.useLibLlvm()) self.llvm = .{ .context = llvm.Context.create() }; + 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 = .{}, + }; errdefer self.deinit(); try self.string_indices.append(self.gpa, 0); @@ -4853,7 +5993,7 @@ pub fn init(options: Options) InitError!Builder { if (options.name.len > 0) self.source_filename = try self.string(options.name); self.initializeLLVMTarget(options.target.cpu.arch); if (self.useLibLlvm()) self.llvm.module = llvm.Module.createWithName( - (self.source_filename.toSlice(&self) orelse "").ptr, + (self.source_filename.slice(&self) orelse "").ptr, self.llvm.context, ); @@ -4864,20 +6004,20 @@ pub fn init(options: Options) InitError!Builder { var error_message: [*:0]const u8 = undefined; var target: *llvm.Target = undefined; if (llvm.Target.getFromTriple( - self.target_triple.toSlice(&self).?, + 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.toSlice(&self).?, + self.target_triple.slice(&self).?, error_message, }); return InitError.InvalidLlvmTriple; } self.llvm.target = target; - self.llvm.module.?.setTarget(self.target_triple.toSlice(&self).?); + self.llvm.module.?.setTarget(self.target_triple.slice(&self).?); } } @@ -4902,6 +6042,16 @@ pub fn init(options: Options) InitError!Builder { assert(self.ptrTypeAssumeCapacity(@enumFromInt(addr_space)) == .ptr); } + { + 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); + } + assert(try self.intConst(.i1, 0) == .false); assert(try self.intConst(.i1, 1) == .true); assert(try self.noneConst(.token) == .none); @@ -4911,8 +6061,8 @@ pub fn init(options: Options) InitError!Builder { pub fn deinit(self: *Builder) void { self.string_map.deinit(self.gpa); - self.string_bytes.deinit(self.gpa); self.string_indices.deinit(self.gpa); + self.string_bytes.deinit(self.gpa); self.types.deinit(self.gpa); self.next_unique_type_id.deinit(self.gpa); @@ -4920,6 +6070,11 @@ pub fn deinit(self: *Builder) void { self.type_items.deinit(self.gpa); self.type_extra.deinit(self.gpa); + self.attributes.deinit(self.gpa); + self.attributes_map.deinit(self.gpa); + self.attributes_indices.deinit(self.gpa); + self.attributes_extra.deinit(self.gpa); + self.globals.deinit(self.gpa); self.next_unique_global_id.deinit(self.gpa); self.aliases.deinit(self.gpa); @@ -4936,6 +6091,8 @@ pub fn deinit(self: *Builder) void { 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(); @@ -5230,7 +6387,7 @@ pub fn structType( pub fn opaqueType(self: *Builder, name: String) Allocator.Error!Type { try self.string_map.ensureUnusedCapacity(self.gpa, 1); - if (name.toSlice(self)) |id| { + if (name.slice(self)) |id| { const count: usize = comptime std.fmt.count("{d}" ++ .{0}, .{std.math.maxInt(u32)}); try self.string_bytes.ensureUnusedCapacity(self.gpa, id.len + count); } @@ -5268,6 +6425,99 @@ pub fn namedTypeSetBody( } } +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 kind_id = &self.llvm.attribute_kind_ids.?[@intFromEnum(attribute)]; + if (kind_id.* == 0) { + const name = @tagName(attribute); + kind_id.* = llvm.getEnumAttributeKindForName(name.ptr, name.len); + assert(kind_id.* != 0); + } + break :llvm_attr switch (attribute) { + else => switch (attribute) { + inline else => |value| self.llvm.context.createEnumAttribute( + 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(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, + }); + } + return @enumFromInt(gop.index); +} + +pub fn attrs(self: *Builder, attributes: []Attribute.Index) Allocator.Error!Attributes { + std.sort.heap(Attribute.Index, attributes, self, struct { + pub fn lessThan(builder: *const Builder, lhs: Attribute.Index, rhs: Attribute.Index) bool { + const lhs_kind = lhs.getKind(builder); + const rhs_kind = rhs.getKind(builder); + assert(lhs_kind != rhs_kind); + return @intFromEnum(lhs_kind) < @intFromEnum(rhs_kind); + } + }.lessThan); + return @enumFromInt(try self.attrGeneric(@ptrCast(attributes))); +} + +pub fn fnAttrs(self: *Builder, fn_attributes: []const Attributes) Allocator.Error!FunctionAttributes { + return @enumFromInt(try self.attrGeneric(@ptrCast( + fn_attributes[0..if (std.mem.lastIndexOfNone(Attributes, fn_attributes, &.{.none})) |last| + last + 1 + else + 0], + ))); +} + pub fn addGlobal(self: *Builder, name: String, global: Global) Allocator.Error!Global.Index { assert(!name.isAnon()); try self.ensureUnusedTypeCapacity(1, NoExtra, 0); @@ -5295,7 +6545,7 @@ pub fn addGlobalAssumeCapacity(self: *Builder, name: String, global: Global) Glo const unique_gop = self.next_unique_global_id.getOrPutAssumeCapacity(name); if (!unique_gop.found_existing) unique_gop.value_ptr.* = 2; - id = self.fmtAssumeCapacity("{s}.{d}", .{ name.toSlice(self).?, unique_gop.value_ptr.* }); + id = self.fmtAssumeCapacity("{s}.{d}", .{ name.slice(self).?, unique_gop.value_ptr.* }); unique_gop.value_ptr.* += 1; } } @@ -5309,8 +6559,9 @@ pub fn intConst(self: *Builder, ty: Type, value: anytype) Allocator.Error!Consta switch (@typeInfo(@TypeOf(value))) { .Int => |info| std.math.big.int.calcTwosCompLimbCount(info.bits), .ComptimeInt => std.math.big.int.calcLimbLen(value), - else => @compileError("intConst expected an integral value, got " ++ - @typeName(@TypeOf(value))), + else => @compileError( + "intConst expected an integral value, got " ++ @typeName(@TypeOf(value)), + ), } ]std.math.big.Limb = undefined; return self.bigIntConst(ty, std.math.big.int.Mutable.init(&limbs, value).toConst()); @@ -5770,7 +7021,7 @@ pub fn printUnbuffered( \\; ModuleID = '{s}' \\source_filename = {"} \\ - , .{ self.source_filename.toSlice(self).?, self.source_filename.fmt(self) }); + , .{ self.source_filename.slice(self).?, self.source_filename.fmt(self) }); if (self.data_layout != .none) try writer.print( \\target datalayout = {"} \\ @@ -5780,11 +7031,13 @@ pub fn printUnbuffered( \\ , .{self.target_triple.fmt(self)}); try writer.writeByte('\n'); + for (self.types.keys(), self.types.values()) |id, ty| try writer.print( \\%{} = type {} \\ , .{ id.fmt(self), ty.fmt(self) }); try writer.writeByte('\n'); + for (self.variables.items) |variable| { if (variable.global.getReplacement(self) != .none) continue; const global = variable.global.ptrConst(self); @@ -5808,28 +7061,42 @@ pub fn printUnbuffered( }); } try writer.writeByte('\n'); + + var attribute_groups: std.AutoArrayHashMapUnmanaged(Attributes, void) = .{}; + defer attribute_groups.deinit(self.gpa); for (0.., self.functions.items) |function_i, function| { const function_index: Function.Index = @enumFromInt(function_i); if (function.global.getReplacement(self) != .none) continue; const global = function.global.ptrConst(self); const params_len = global.type.functionParameters(self).len; + const function_attributes = function.attributes.func(self); + if (function_attributes != .none) try writer.print( + \\; Function Attrs:{} + \\ + , .{function_attributes.fmt(self)}); try writer.print( - \\{s}{}{}{}{} {} {}( + \\{s}{}{}{}{}{}{"} {} {}( , .{ if (function.instructions.len > 0) "define" else "declare", global.linkage, global.preemption, global.visibility, global.dll_storage_class, + function.call_conv, + function.attributes.ret(self).fmt(self), global.type.functionReturn(self).fmt(self), function.global.fmt(self), }); for (0..params_len) |arg| { if (arg > 0) try writer.writeAll(", "); + try writer.print( + \\{%}{"} + , .{ + global.type.functionParameters(self)[arg].fmt(self), + function.attributes.param(arg, self).fmt(self), + }); if (function.instructions.len > 0) - try writer.print("{%}", .{function.arg(@intCast(arg)).fmt(function_index, self)}) - else - try writer.print("{%}", .{global.type.functionParameters(self)[arg].fmt(self)}); + try writer.print(" {}", .{function.arg(@intCast(arg)).fmt(function_index, self)}); } switch (global.type.functionKind(self)) { .normal => {}, @@ -5838,7 +7105,11 @@ pub fn printUnbuffered( try writer.writeAll("..."); }, } - try writer.print("){}{}", .{ global.unnamed_addr, function.alignment }); + try writer.print("){}{}", .{ global.unnamed_addr, global.addr_space }); + if (function_attributes != .none) try writer.print(" #{d}", .{ + (try attribute_groups.getOrPutValue(self.gpa, function_attributes, {})).index, + }); + try writer.print("{}", .{function.alignment}); if (function.instructions.len > 0) { var block_incoming_len: u32 = undefined; try writer.writeAll(" {\n"); @@ -5992,6 +7263,48 @@ pub fn printUnbuffered( extra.@"else".toInst(&function).fmt(function_index, self), }); }, + .call, + .@"call fast", + .@"musttail call", + .@"musttail call fast", + .@"notail call", + .@"notail call fast", + .@"tail call", + .@"tail call fast", + => |tag| { + var extra = + function.extraDataTrail(Function.Instruction.Call, instruction.data); + const args = extra.trail.next(extra.data.args_len, Value, &function); + try writer.writeAll(" "); + const ret_ty = extra.data.ty.functionReturn(self); + switch (ret_ty) { + .void => {}, + else => try writer.print("%{} = ", .{ + instruction_index.name(&function).fmt(self), + }), + .none => unreachable, + } + try writer.print("{s}{}{}{} {%} {}(", .{ + @tagName(tag), + extra.data.info.call_conv, + extra.data.attributes.ret(self).fmt(self), + extra.data.callee.typeOf(function_index, self).pointerAddrSpace(self), + switch (extra.data.ty.functionKind(self)) { + .normal => ret_ty, + .vararg => extra.data.ty, + }.fmt(self), + extra.data.callee.fmt(function_index, self), + }); + for (0.., args) |arg_index, arg| { + if (arg_index > 0) try writer.writeAll(", "); + 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 writer.print("){}\n", .{extra.data.attributes.func(self).fmt(self)}); + }, .extractelement => |tag| { const extra = function.extraData(Function.Instruction.ExtractElement, instruction.data); @@ -6218,6 +7531,12 @@ pub fn printUnbuffered( } try writer.writeAll("\n\n"); } + + for (0.., attribute_groups.keys()) |attribute_group_index, attribute_group| + try writer.print( + \\attribute #{d} = {{{"} }} + \\ + , .{ attribute_group_index, attribute_group.fmt(self) }); } pub inline fn useLibLlvm(self: *const Builder) bool { @@ -6238,7 +7557,7 @@ fn isValidIdentifier(id: []const u8) bool { 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.toSlice(self)) |id| { + if (name.slice(self)) |id| { const count: usize = comptime std.fmt.count("{d}" ++ .{0}, .{std.math.maxInt(u32)}); try self.string_bytes.ensureUnusedCapacity(self.gpa, id.len + count); } @@ -6528,14 +7847,14 @@ 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.toSlice(self) orelse ""), + self.llvm.context.structCreateNamed(id.slice(self) orelse ""), ); return result; } const unique_gop = self.next_unique_type_id.getOrPutAssumeCapacity(name); if (!unique_gop.found_existing) unique_gop.value_ptr.* = 2; - id = self.fmtAssumeCapacity("{s}.{d}", .{ name.toSlice(self).?, unique_gop.value_ptr.* }); + id = self.fmtAssumeCapacity("{s}.{d}", .{ name.slice(self).?, unique_gop.value_ptr.* }); unique_gop.value_ptr.* += 1; } } @@ -6636,6 +7955,30 @@ fn typeExtraData(self: *const Builder, comptime T: type, index: Type.Item.ExtraI return self.typeExtraDataTrail(T, index).data; } +fn attrGeneric(self: *Builder, data: []const u32) Allocator.Error!u32 { + try self.attributes_map.ensureUnusedCapacity(self.gpa, 1); + try self.attributes_indices.ensureUnusedCapacity(self.gpa, 1); + try self.attributes_extra.ensureUnusedCapacity(self.gpa, data.len); + + const Adapter = struct { + builder: *const Builder, + pub fn hash(_: @This(), key: []const u32) u32 { + return @truncate(std.hash.Wyhash.hash(1, std.mem.sliceAsBytes(key))); + } + pub fn eql(ctx: @This(), lhs_key: []const u32, _: void, rhs_index: usize) bool { + const start = ctx.builder.attributes_indices.items[rhs_index]; + const end = ctx.builder.attributes_indices.items[rhs_index + 1]; + return std.mem.eql(u32, lhs_key, ctx.builder.attributes_extra.items[start..end]); + } + }; + const gop = self.attributes_map.getOrPutAssumeCapacityAdapted(data, Adapter{ .builder = self }); + if (!gop.found_existing) { + self.attributes_extra.appendSliceAssumeCapacity(data); + self.attributes_indices.appendAssumeCapacity(@intCast(self.attributes_extra.items.len)); + } + return @intCast(gop.index); +} + fn bigIntConstAssumeCapacity( self: *Builder, ty: Type, @@ -7073,7 +8416,7 @@ fn arrayConstAssumeCapacity( } fn stringConstAssumeCapacity(self: *Builder, val: String) Constant { - const slice = val.toSlice(self).?; + const slice = val.slice(self).?; const ty = self.arrayTypeAssumeCapacity(slice.len, .i8); if (std.mem.allEqual(u8, slice, 0)) return self.zeroInitConstAssumeCapacity(ty); const result = self.getOrPutConstantNoExtraAssumeCapacity( @@ -7086,7 +8429,7 @@ fn stringConstAssumeCapacity(self: *Builder, val: String) Constant { } fn stringNullConstAssumeCapacity(self: *Builder, val: String) Constant { - const slice = val.toSlice(self).?; + 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( diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig index 3b99ae1fe14b..7f0afee2af82 100644 --- a/src/codegen/llvm/bindings.zig +++ b/src/codegen/llvm/bindings.zig @@ -26,10 +26,13 @@ pub const Context = opaque { extern fn LLVMContextDispose(C: *Context) void; pub const createEnumAttribute = LLVMCreateEnumAttribute; - extern fn LLVMCreateEnumAttribute(*Context, KindID: c_uint, Val: u64) *Attribute; + 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(*Context, Key: [*]const u8, Key_Len: c_uint, Value: [*]const u8, Value_Len: c_uint) *Attribute; + 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; @@ -309,12 +312,18 @@ pub const Value = opaque { pub const setAlignment = LLVMSetAlignment; extern fn LLVMSetAlignment(V: *Value, Bytes: c_uint) void; - pub const getFunctionCallConv = LLVMGetFunctionCallConv; - extern fn LLVMGetFunctionCallConv(Fn: *Value) CallConv; - 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; @@ -642,7 +651,17 @@ pub const Builder = opaque { Name: [*:0]const u8, ) *Value; - pub const buildCall = ZigLLVMBuildCall; + pub const buildCall = LLVMBuildCall2; + extern fn LLVMBuildCall2( + *Builder, + *Type, + Fn: *Value, + Args: [*]const *Value, + NumArgs: c_uint, + Name: [*:0]const u8, + ) *Value; + + pub const buildCallOld = ZigLLVMBuildCall; extern fn ZigLLVMBuildCall( *Builder, *Type, @@ -1605,6 +1624,13 @@ pub const CallAttr = enum(c_int) { AlwaysInline, }; +pub const TailCallKind = enum(c_uint) { + None, + Tail, + MustTail, + NoTail, +}; + pub const DLLStorageClass = enum(c_uint) { Default, DLLImport, diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp index 26ea04aca616..d22bfc1b4765 100644 --- a/src/zig_llvm.cpp +++ b/src/zig_llvm.cpp @@ -453,6 +453,10 @@ LLVMValueRef ZigLLVMBuildCall(LLVMBuilderRef B, LLVMTypeRef Ty, LLVMValueRef Fn, return wrap(call_inst); } +ZIG_EXTERN_C void ZigLLVMSetTailCallKind(LLVMValueRef Call, CallInst::TailCallKind TailCallKind) { + unwrap(Call)->setTailCallKind(TailCallKind); +} + void ZigLLVMAddAttributeAtIndex(LLVMValueRef Val, unsigned Idx, LLVMAttributeRef A) { if (isa(unwrap(Val))) { unwrap(Val)->addAttributeAtIndex(Idx, unwrap(A)); @@ -461,7 +465,6 @@ void ZigLLVMAddAttributeAtIndex(LLVMValueRef Val, unsigned Idx, LLVMAttributeRef } } - LLVMValueRef ZigLLVMBuildMemCpy(LLVMBuilderRef B, LLVMValueRef Dst, unsigned DstAlign, LLVMValueRef Src, unsigned SrcAlign, LLVMValueRef Size, bool isVolatile) { @@ -1116,11 +1119,6 @@ void ZigLLVMAddFunctionAttr(LLVMValueRef fn_ref, const char *attr_name, const ch func->addFnAttr(attr_name, attr_value); } -void ZigLLVMAddFunctionAttrCold(LLVMValueRef fn_ref) { - Function *func = unwrap(fn_ref); - func->addFnAttr(Attribute::Cold); -} - void ZigLLVMParseCommandLineOptions(size_t argc, const char *const *argv) { cl::ParseCommandLineOptions(argc, argv); } From 06af9cc101d0e688f7c6a1c2cc009b917f0a7bdf Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sun, 23 Jul 2023 23:45:01 -0400 Subject: [PATCH 2/5] llvm: fix datalayout generation for more targets Closes #16482 --- lib/std/target.zig | 10 +++++--- src/codegen/llvm.zig | 61 +++++++++++++++++++++++++++----------------- 2 files changed, 44 insertions(+), 27 deletions(-) diff --git a/lib/std/target.zig b/lib/std/target.zig index e00a1c15d780..015aee61ba7b 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -1912,7 +1912,7 @@ pub const Target = struct { return switch (target.cpu.arch) { .amdgcn => 4, .x86 => switch (target.os.tag) { - .windows => 4, + .windows, .uefi => 4, else => 16, }, .arm, @@ -1931,8 +1931,6 @@ pub const Target = struct { .bpfel, .mips64, .mips64el, - .powerpc64, - .powerpc64le, .riscv32, .riscv64, .sparc64, @@ -1941,6 +1939,12 @@ pub const Target = struct { .wasm32, .wasm64, => 16, + .powerpc64, + .powerpc64le, + => switch (target.os.tag) { + else => 8, + .linux => 16, + }, else => @divExact(target.ptrBitWidth(), 8), }; } diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 4f8e9503b3ba..6648e0a8e407 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -340,7 +340,6 @@ const DataLayoutBuilder = struct { _: std.fmt.FormatOptions, writer: anytype, ) @TypeOf(writer).Error!void { - const is_aarch64_windows = self.target.cpu.arch == .aarch64 and self.target.os.tag == .windows; try writer.writeByte(switch (self.target.cpu.arch.endian()) { .Little => 'e', .Big => 'E', @@ -407,7 +406,8 @@ const DataLayoutBuilder = struct { }; if (self.target.cpu.arch == .aarch64_32) continue; if (!info.force_in_data_layout and matches_default and - self.target.cpu.arch != .riscv64 and !is_aarch64_windows and + self.target.cpu.arch != .riscv64 and !(self.target.cpu.arch == .aarch64 and + (self.target.os.tag == .uefi or self.target.os.tag == .windows)) and self.target.cpu.arch != .bpfeb and self.target.cpu.arch != .bpfel) continue; try writer.writeAll("-p"); if (info.llvm != .default) try writer.print("{d}", .{@intFromEnum(info.llvm)}); @@ -423,7 +423,7 @@ const DataLayoutBuilder = struct { if (self.target.cpu.arch == .s390x) try self.typeAlignment(.integer, 1, 8, 8, false, writer); try self.typeAlignment(.integer, 8, 8, 8, false, writer); try self.typeAlignment(.integer, 16, 16, 16, false, writer); - try self.typeAlignment(.integer, 32, if (is_aarch64_windows) 0 else 32, 32, false, writer); + try self.typeAlignment(.integer, 32, 32, 32, false, writer); try self.typeAlignment(.integer, 64, 32, 64, false, writer); try self.typeAlignment(.integer, 128, 32, 64, false, writer); if (backendSupportsF16(self.target)) try self.typeAlignment(.float, 16, 16, 16, false, writer); @@ -453,8 +453,15 @@ const DataLayoutBuilder = struct { try self.typeAlignment(.vector, 128, 128, 128, true, writer); }, } - if (self.target.os.tag != .windows and self.target.cpu.arch != .avr) - try self.typeAlignment(.aggregate, 0, 0, 64, false, writer); + const swap_agg_nat = switch (self.target.cpu.arch) { + .x86, .x86_64 => switch (self.target.os.tag) { + .uefi, .windows => true, + else => false, + }, + .avr => true, + else => false, + }; + if (!swap_agg_nat) try self.typeAlignment(.aggregate, 0, 0, 64, false, writer); for (@as([]const u24, switch (self.target.cpu.arch) { .avr => &.{8}, .msp430 => &.{ 8, 16 }, @@ -498,6 +505,7 @@ const DataLayoutBuilder = struct { 0 => try writer.print("-n{d}", .{natural}), else => try writer.print(":{d}", .{natural}), }; + if (swap_agg_nat) try self.typeAlignment(.aggregate, 0, 0, 64, false, writer); if (self.target.cpu.arch == .hexagon) { try self.typeAlignment(.integer, 64, 64, 64, true, writer); try self.typeAlignment(.integer, 32, 32, 32, true, writer); @@ -506,11 +514,9 @@ const DataLayoutBuilder = struct { try self.typeAlignment(.float, 32, 32, 32, true, writer); try self.typeAlignment(.float, 64, 64, 64, true, writer); } - if (self.target.os.tag == .windows or self.target.cpu.arch == .avr) - try self.typeAlignment(.aggregate, 0, 0, 64, false, writer); const stack_abi = self.target.stackAlignment() * 8; - if (self.target.os.tag == .windows or self.target.cpu.arch == .msp430 or - stack_abi != ptr_bit_width) + if (self.target.os.tag == .uefi or self.target.os.tag == .windows or + self.target.cpu.arch == .msp430 or stack_abi != ptr_bit_width) try writer.print("-S{d}", .{stack_abi}); switch (self.target.cpu.arch) { .hexagon, .ve => { @@ -571,22 +577,21 @@ const DataLayoutBuilder = struct { .integer => { if (self.target.ptrBitWidth() <= 16 and size >= 128) return; abi = @min(abi, self.target.maxIntAlignment() * 8); - switch (self.target.os.tag) { - .linux => switch (self.target.cpu.arch) { - .aarch64, - .aarch64_be, - .aarch64_32, - .mips, - .mipsel, - => pref = @max(pref, 32), - else => {}, - }, - else => {}, - } switch (self.target.cpu.arch) { .aarch64, .aarch64_be, .aarch64_32, + => if (size == 128) { + abi = size; + pref = size; + } else switch (self.target.os.tag) { + .macos => {}, + .uefi, .windows => { + pref = size; + force_abi = size >= 32; + }, + else => pref = @max(pref, 32), + }, .bpfeb, .bpfel, .nvptx, @@ -597,6 +602,9 @@ const DataLayoutBuilder = struct { pref = size; }, .hexagon => force_abi = true, + .mips, + .mipsel, + => pref = @max(pref, 32), .mips64, .mips64el, => if (size <= 32) { @@ -617,7 +625,8 @@ const DataLayoutBuilder = struct { 128 => abi = 64, else => {}, } - } else if ((self.target.cpu.arch.isPPC64() and (size == 256 or size == 512)) or + } else if ((self.target.cpu.arch.isPPC64() and self.target.os.tag == .linux and + (size == 256 or size == 512)) or (self.target.cpu.arch.isNvptx() and (size == 16 or size == 32))) { force_abi = true; @@ -646,17 +655,21 @@ const DataLayoutBuilder = struct { .hexagon => if (size == 32 or size == 64) { force_abi = true; }, - .aarch64_32 => if (size == 128) { + .aarch64_32, .amdgcn => if (size == 128) { abi = size; pref = size; }, + .wasm32, .wasm64 => if (self.target.os.tag == .emscripten and size == 128) { + abi = 64; + pref = 64; + }, .ve => if (size == 64) { abi = size; pref = size; }, else => {}, }, - .aggregate => if (self.target.os.tag == .windows or + .aggregate => if (self.target.os.tag == .uefi or self.target.os.tag == .windows or self.target.cpu.arch.isARM() or self.target.cpu.arch.isThumb()) { pref = @min(pref, self.target.ptrBitWidth()); From c610cde1eb460ddf80910f34e19c8a68d1bc86a9 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sun, 23 Jul 2023 23:45:31 -0400 Subject: [PATCH 3/5] test: test for issues starting codegen on many targets Specifically this is to make sure llvm data layout generation doesn't regress. The no emit bin is to allow testing targets that can't currently be linked. The commented out targets are ones that fail in the linker anyway when no emit bin is passed. --- lib/std/Build/Step.zig | 4 +- lib/std/Build/Step/Compile.zig | 7 +- lib/std/Build/Step/TranslateC.zig | 4 +- src/Compilation.zig | 1 + test/cases.zig | 1 + test/llvm_targets.zig | 117 ++++++++++++++++++++++++++++++ test/src/Cases.zig | 16 ++++ 7 files changed, 144 insertions(+), 6 deletions(-) create mode 100644 test/llvm_targets.zig diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig index f21ef8bc8f7b..991283dbc526 100644 --- a/lib/std/Build/Step.zig +++ b/lib/std/Build/Step.zig @@ -294,7 +294,7 @@ pub fn evalZigProcess( s: *Step, argv: []const []const u8, prog_node: *std.Progress.Node, -) ![]const u8 { +) !?[]const u8 { assert(argv.len != 0); const b = s.owner; const arena = b.allocator; @@ -423,6 +423,8 @@ pub fn evalZigProcess( }); } + if (s.cast(Compile)) |compile| if (compile.emit_bin == .no_emit) return result; + return result orelse return s.fail( "the following command failed to communicate the compilation result:\n{s}", .{try allocPrintCmd(arena, null, argv)}, diff --git a/lib/std/Build/Step/Compile.zig b/lib/std/Build/Step/Compile.zig index 7ff82e4b7b10..6dd0bd2d11de 100644 --- a/lib/std/Build/Step/Compile.zig +++ b/lib/std/Build/Step/Compile.zig @@ -1997,7 +1997,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { try zig_args.append(resolved_args_file); } - const output_bin_path = step.evalZigProcess(zig_args.items, prog_node) catch |err| switch (err) { + const maybe_output_bin_path = step.evalZigProcess(zig_args.items, prog_node) catch |err| switch (err) { error.NeedCompileErrorCheck => { assert(self.expect_errors.len != 0); try checkCompileErrors(self); @@ -2005,10 +2005,11 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { }, else => |e| return e, }; - const output_dir = fs.path.dirname(output_bin_path).?; // Update generated files - { + if (maybe_output_bin_path) |output_bin_path| { + const output_dir = fs.path.dirname(output_bin_path).?; + self.output_dirname_source.path = output_dir; self.output_path_source.path = b.pathJoin( diff --git a/lib/std/Build/Step/TranslateC.zig b/lib/std/Build/Step/TranslateC.zig index 60e35e940b11..cfbabc2fa958 100644 --- a/lib/std/Build/Step/TranslateC.zig +++ b/lib/std/Build/Step/TranslateC.zig @@ -148,8 +148,8 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { const output_path = try step.evalZigProcess(argv_list.items, prog_node); - self.out_basename = fs.path.basename(output_path); - const output_dir = fs.path.dirname(output_path).?; + self.out_basename = fs.path.basename(output_path.?); + const output_dir = fs.path.dirname(output_path.?).?; self.output_file.path = try fs.path.join( b.allocator, diff --git a/src/Compilation.zig b/src/Compilation.zig index dc3101d598d8..ba0e643fda32 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -1053,6 +1053,7 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation { buf.appendSliceAssumeCapacity(","); } } + if (buf.items.len == 0) break :blk ""; assert(mem.endsWith(u8, buf.items, ",")); buf.items[buf.items.len - 1] = 0; buf.shrinkAndFree(buf.items.len); diff --git a/test/cases.zig b/test/cases.zig index ffe046c70ede..6c4b1c994f3a 100644 --- a/test/cases.zig +++ b/test/cases.zig @@ -4,5 +4,6 @@ const Cases = @import("src/Cases.zig"); pub fn addCases(cases: *Cases) !void { try @import("compile_errors.zig").addCases(cases); try @import("cbe.zig").addCases(cases); + try @import("llvm_targets.zig").addCases(cases); try @import("nvptx.zig").addCases(cases); } diff --git a/test/llvm_targets.zig b/test/llvm_targets.zig new file mode 100644 index 000000000000..d042ce500cfb --- /dev/null +++ b/test/llvm_targets.zig @@ -0,0 +1,117 @@ +const std = @import("std"); +const Cases = @import("src/Cases.zig"); + +const targets = [_]std.zig.CrossTarget{ + .{ .cpu_arch = .aarch64, .os_tag = .freestanding, .abi = .none }, + .{ .cpu_arch = .aarch64, .os_tag = .linux, .abi = .none }, + .{ .cpu_arch = .aarch64, .os_tag = .macos, .abi = .none }, + .{ .cpu_arch = .aarch64, .os_tag = .uefi, .abi = .none }, + .{ .cpu_arch = .aarch64, .os_tag = .windows, .abi = .gnu }, + .{ .cpu_arch = .aarch64, .os_tag = .windows, .abi = .msvc }, + .{ .cpu_arch = .aarch64_be, .os_tag = .freestanding, .abi = .none }, + .{ .cpu_arch = .aarch64_be, .os_tag = .linux, .abi = .none }, + .{ .cpu_arch = .aarch64_32, .os_tag = .freestanding, .abi = .none }, + .{ .cpu_arch = .aarch64_32, .os_tag = .linux, .abi = .none }, + .{ .cpu_arch = .amdgcn, .os_tag = .amdhsa, .abi = .none }, + .{ .cpu_arch = .amdgcn, .os_tag = .amdpal, .abi = .none }, + .{ .cpu_arch = .amdgcn, .os_tag = .linux, .abi = .none }, + //.{ .cpu_arch = .amdgcn, .os_tag = .mesa3d, .abi = .none }, + .{ .cpu_arch = .arm, .os_tag = .freestanding, .abi = .none }, + .{ .cpu_arch = .arm, .os_tag = .linux, .abi = .none }, + .{ .cpu_arch = .arm, .os_tag = .uefi, .abi = .none }, + .{ .cpu_arch = .armeb, .os_tag = .freestanding, .abi = .none }, + .{ .cpu_arch = .armeb, .os_tag = .linux, .abi = .none }, + .{ .cpu_arch = .avr, .os_tag = .freebsd, .abi = .none }, + .{ .cpu_arch = .avr, .os_tag = .freestanding, .abi = .none }, + .{ .cpu_arch = .avr, .os_tag = .linux, .abi = .none }, + .{ .cpu_arch = .bpfel, .os_tag = .linux, .abi = .gnu }, + .{ .cpu_arch = .bpfel, .os_tag = .linux, .abi = .none }, + .{ .cpu_arch = .bpfeb, .os_tag = .linux, .abi = .gnu }, + .{ .cpu_arch = .bpfeb, .os_tag = .linux, .abi = .none }, + .{ .cpu_arch = .hexagon, .os_tag = .linux, .abi = .none }, + .{ .cpu_arch = .mips, .os_tag = .linux, .abi = .gnueabihf }, + .{ .cpu_arch = .mips, .os_tag = .linux, .abi = .musl }, + .{ .cpu_arch = .mips, .os_tag = .linux, .abi = .none }, + .{ .cpu_arch = .mipsel, .os_tag = .linux, .abi = .gnueabihf }, + .{ .cpu_arch = .mipsel, .os_tag = .linux, .abi = .musl }, + .{ .cpu_arch = .mipsel, .os_tag = .linux, .abi = .none }, + .{ .cpu_arch = .mips64, .os_tag = .linux, .abi = .none }, + .{ .cpu_arch = .mips64el, .os_tag = .linux, .abi = .none }, + .{ .cpu_arch = .msp430, .os_tag = .freebsd, .abi = .none }, + .{ .cpu_arch = .msp430, .os_tag = .freestanding, .abi = .none }, + .{ .cpu_arch = .msp430, .os_tag = .linux, .abi = .none }, + //.{ .cpu_arch = .nvptx, .os_tag = .cuda, .abi = .none }, + //.{ .cpu_arch = .nvptx64, .os_tag = .cuda, .abi = .none }, + .{ .cpu_arch = .powerpc, .os_tag = .freebsd, .abi = .none }, + .{ .cpu_arch = .powerpc, .os_tag = .freestanding, .abi = .none }, + .{ .cpu_arch = .powerpc, .os_tag = .linux, .abi = .gnueabihf }, + .{ .cpu_arch = .powerpc, .os_tag = .linux, .abi = .musl }, + .{ .cpu_arch = .powerpc, .os_tag = .linux, .abi = .none }, + .{ .cpu_arch = .powerpcle, .os_tag = .freebsd, .abi = .none }, + .{ .cpu_arch = .powerpcle, .os_tag = .freestanding, .abi = .none }, + .{ .cpu_arch = .powerpcle, .os_tag = .linux, .abi = .gnu }, + .{ .cpu_arch = .powerpcle, .os_tag = .linux, .abi = .musl }, + .{ .cpu_arch = .powerpcle, .os_tag = .linux, .abi = .none }, + .{ .cpu_arch = .powerpc64, .os_tag = .freebsd, .abi = .none }, + .{ .cpu_arch = .powerpc64, .os_tag = .freestanding, .abi = .none }, + .{ .cpu_arch = .powerpc64, .os_tag = .linux, .abi = .gnu }, + .{ .cpu_arch = .powerpc64, .os_tag = .linux, .abi = .musl }, + .{ .cpu_arch = .powerpc64, .os_tag = .linux, .abi = .none }, + .{ .cpu_arch = .powerpc64le, .os_tag = .freebsd, .abi = .none }, + .{ .cpu_arch = .powerpc64le, .os_tag = .freestanding, .abi = .none }, + .{ .cpu_arch = .powerpc64le, .os_tag = .linux, .abi = .gnu }, + .{ .cpu_arch = .powerpc64le, .os_tag = .linux, .abi = .musl }, + .{ .cpu_arch = .powerpc64le, .os_tag = .linux, .abi = .none }, + //.{ .cpu_arch = .r600, .os_tag = .mesa3d, .abi = .none }, + .{ .cpu_arch = .riscv32, .os_tag = .freestanding, .abi = .none }, + .{ .cpu_arch = .riscv32, .os_tag = .linux, .abi = .none }, + .{ .cpu_arch = .riscv64, .os_tag = .freestanding, .abi = .none }, + .{ .cpu_arch = .riscv64, .os_tag = .linux, .abi = .gnu }, + .{ .cpu_arch = .riscv64, .os_tag = .linux, .abi = .musl }, + .{ .cpu_arch = .riscv64, .os_tag = .linux, .abi = .none }, + .{ .cpu_arch = .s390x, .os_tag = .freestanding, .abi = .none }, + .{ .cpu_arch = .s390x, .os_tag = .linux, .abi = .gnu }, + .{ .cpu_arch = .sparc, .os_tag = .freestanding, .abi = .none }, + .{ .cpu_arch = .sparc, .os_tag = .linux, .abi = .gnu }, + .{ .cpu_arch = .sparc, .os_tag = .linux, .abi = .none }, + .{ .cpu_arch = .sparcel, .os_tag = .freestanding, .abi = .none }, + .{ .cpu_arch = .sparcel, .os_tag = .linux, .abi = .gnu }, + .{ .cpu_arch = .sparc64, .os_tag = .freestanding, .abi = .none }, + .{ .cpu_arch = .sparc64, .os_tag = .linux, .abi = .gnu }, + //.{ .cpu_arch = .spirv32, .os_tag = .opencl, .abi = .none }, + //.{ .cpu_arch = .spirv32, .os_tag = .glsl450, .abi = .none }, + //.{ .cpu_arch = .spirv32, .os_tag = .vulkan, .abi = .none }, + //.{ .cpu_arch = .spirv64, .os_tag = .opencl, .abi = .none }, + //.{ .cpu_arch = .spirv64, .os_tag = .glsl450, .abi = .none }, + //.{ .cpu_arch = .spirv64, .os_tag = .vulkan, .abi = .none }, + .{ .cpu_arch = .thumb, .os_tag = .freestanding, .abi = .none }, + .{ .cpu_arch = .thumbeb, .os_tag = .freestanding, .abi = .none }, + .{ .cpu_arch = .ve, .os_tag = .linux, .abi = .none }, + .{ .cpu_arch = .wasm32, .os_tag = .emscripten, .abi = .none }, + .{ .cpu_arch = .wasm32, .os_tag = .freestanding, .abi = .none }, + .{ .cpu_arch = .wasm32, .os_tag = .linux, .abi = .none }, + .{ .cpu_arch = .wasm32, .os_tag = .wasi, .abi = .none }, + .{ .cpu_arch = .wasm64, .os_tag = .emscripten, .abi = .none }, + .{ .cpu_arch = .wasm64, .os_tag = .freestanding, .abi = .none }, + .{ .cpu_arch = .wasm64, .os_tag = .linux, .abi = .none }, + .{ .cpu_arch = .wasm64, .os_tag = .wasi, .abi = .none }, + .{ .cpu_arch = .x86, .os_tag = .freestanding, .abi = .none }, + .{ .cpu_arch = .x86, .os_tag = .linux, .abi = .none }, + .{ .cpu_arch = .x86, .os_tag = .uefi, .abi = .none }, + .{ .cpu_arch = .x86, .os_tag = .windows, .abi = .gnu }, + .{ .cpu_arch = .x86, .os_tag = .windows, .abi = .msvc }, + .{ .cpu_arch = .x86_64, .os_tag = .freebsd, .abi = .none }, + .{ .cpu_arch = .x86_64, .os_tag = .freestanding, .abi = .none }, + .{ .cpu_arch = .x86_64, .os_tag = .linux, .abi = .none }, + .{ .cpu_arch = .x86_64, .os_tag = .macos, .abi = .none }, + .{ .cpu_arch = .x86_64, .os_tag = .uefi, .abi = .none }, + .{ .cpu_arch = .x86_64, .os_tag = .windows, .abi = .gnu }, + .{ .cpu_arch = .x86_64, .os_tag = .windows, .abi = .msvc }, +}; + +pub fn addCases(ctx: *Cases) !void { + for (targets) |target| { + var case = ctx.noEmitUsingLlvmBackend("llvm_targets", target); + case.addCompile(""); + } +} diff --git a/test/src/Cases.zig b/test/src/Cases.zig index 92ec3cb35f96..ce080b5490f8 100644 --- a/test/src/Cases.zig +++ b/test/src/Cases.zig @@ -76,6 +76,7 @@ pub const Case = struct { output_mode: std.builtin.OutputMode, optimize_mode: std.builtin.Mode = .Debug, updates: std.ArrayList(Update), + emit_bin: bool = true, emit_h: bool = false, is_test: bool = false, expect_exact: bool = false, @@ -176,6 +177,19 @@ pub fn exeFromCompiledC(ctx: *Cases, name: []const u8, target: CrossTarget) *Cas return &ctx.cases.items[ctx.cases.items.len - 1]; } +pub fn noEmitUsingLlvmBackend(ctx: *Cases, name: []const u8, target: CrossTarget) *Case { + ctx.cases.append(Case{ + .name = name, + .target = target, + .updates = std.ArrayList(Update).init(ctx.cases.allocator), + .output_mode = .Obj, + .emit_bin = false, + .deps = std.ArrayList(DepModule).init(ctx.arena), + .backend = .llvm, + }) catch @panic("out of memory"); + return &ctx.cases.items[ctx.cases.items.len - 1]; +} + /// Adds a test case that uses the LLVM backend to emit an executable. /// Currently this implies linking libc, because only then we can generate a testable executable. pub fn exeUsingLlvmBackend(ctx: *Cases, name: []const u8, target: CrossTarget) *Case { @@ -537,6 +551,8 @@ pub fn lowerToBuildSteps( }), }; + artifact.emit_bin = if (case.emit_bin) .default else .no_emit; + if (case.link_libc) artifact.linkLibC(); switch (case.backend) { From 533111e849c5f8010f4e9b97c09c433058c5a3e0 Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sun, 23 Jul 2023 05:01:12 -0400 Subject: [PATCH 4/5] llvm: convert inline assembly Also, implement TODOs from a previous commit. --- src/codegen/llvm.zig | 48 ++---- src/codegen/llvm/Builder.zig | 317 +++++++++++++++++++++++++++++++---- 2 files changed, 297 insertions(+), 68 deletions(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 6648e0a8e407..43a4b9886662 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -6780,7 +6780,7 @@ pub const FuncGen = struct { const max_param_count = inputs.len + outputs.len; const llvm_param_types = try arena.alloc(Builder.Type, max_param_count); - const llvm_param_values = try arena.alloc(*llvm.Value, max_param_count); + const llvm_param_values = try arena.alloc(Builder.Value, max_param_count); // This stores whether we need to add an elementtype attribute and // if so, the element type itself. const llvm_param_attrs = try arena.alloc(Builder.Type, max_param_count); @@ -6820,7 +6820,7 @@ pub const FuncGen = struct { // Pass the result by reference as an indirect output (e.g. "=*m") llvm_constraints.appendAssumeCapacity('*'); - llvm_param_values[llvm_param_i] = output_inst.toLlvm(&self.wip); + llvm_param_values[llvm_param_i] = output_inst; llvm_param_types[llvm_param_i] = output_inst.typeOfWip(&self.wip); llvm_param_attrs[llvm_param_i] = elem_llvm_ty; llvm_param_i += 1; @@ -6870,25 +6870,25 @@ pub const FuncGen = struct { if (isByRef(arg_ty, mod)) { llvm_elem_ty = try o.lowerPtrElemTy(arg_ty); if (constraintAllowsMemory(constraint)) { - llvm_param_values[llvm_param_i] = arg_llvm_value.toLlvm(&self.wip); + llvm_param_values[llvm_param_i] = arg_llvm_value; llvm_param_types[llvm_param_i] = arg_llvm_value.typeOfWip(&self.wip); } else { const alignment = Builder.Alignment.fromByteUnits(arg_ty.abiAlignment(mod)); const arg_llvm_ty = try o.lowerType(arg_ty); const load_inst = try self.wip.load(.normal, arg_llvm_ty, arg_llvm_value, alignment, ""); - llvm_param_values[llvm_param_i] = load_inst.toLlvm(&self.wip); + llvm_param_values[llvm_param_i] = load_inst; llvm_param_types[llvm_param_i] = arg_llvm_ty; } } else { if (constraintAllowsRegister(constraint)) { - llvm_param_values[llvm_param_i] = arg_llvm_value.toLlvm(&self.wip); + llvm_param_values[llvm_param_i] = arg_llvm_value; llvm_param_types[llvm_param_i] = arg_llvm_value.typeOfWip(&self.wip); } else { const alignment = Builder.Alignment.fromByteUnits(arg_ty.abiAlignment(mod)); const arg_ptr = try self.buildAlloca(arg_llvm_value.typeOfWip(&self.wip), alignment); _ = try self.wip.store(.normal, arg_llvm_value, arg_ptr, alignment); - llvm_param_values[llvm_param_i] = arg_ptr.toLlvm(&self.wip); + llvm_param_values[llvm_param_i] = arg_ptr; llvm_param_types[llvm_param_i] = arg_ptr.typeOfWip(&self.wip); } } @@ -7037,40 +7037,24 @@ pub const FuncGen = struct { var attributes: Builder.FunctionAttributes.Wip = .{}; defer attributes.deinit(&o.builder); + for (llvm_param_attrs[0..param_count], 0..) |llvm_elem_ty, i| if (llvm_elem_ty != .none) + try attributes.addParamAttr(i, .{ .elementtype = llvm_elem_ty }, &o.builder); const ret_llvm_ty = switch (return_count) { 0 => .void, 1 => llvm_ret_types[0], else => try o.builder.structType(.normal, llvm_ret_types), }; - const llvm_fn_ty = try o.builder.fnType(ret_llvm_ty, llvm_param_types[0..param_count], .normal); - const asm_fn = llvm.getInlineAsm( - llvm_fn_ty.toLlvm(&o.builder), - rendered_template.items.ptr, - rendered_template.items.len, - llvm_constraints.items.ptr, - llvm_constraints.items.len, - llvm.Bool.fromBool(is_volatile), - .False, - .ATT, - .False, - ); - const call = (try self.wip.unimplemented(ret_llvm_ty, "")).finish(self.builder.buildCallOld( - llvm_fn_ty.toLlvm(&o.builder), - asm_fn, - llvm_param_values.ptr, - @intCast(param_count), - .C, - .Auto, + const call = try self.wip.callAsm( + try attributes.finish(&o.builder), + llvm_fn_ty, + .{ .sideeffect = is_volatile }, + try o.builder.string(rendered_template.items), + try o.builder.string(llvm_constraints.items), + llvm_param_values[0..param_count], "", - ), &self.wip); - for (llvm_param_attrs[0..param_count], 0..) |llvm_elem_ty, i| { - if (llvm_elem_ty != .none) { - try attributes.addParamAttr(i, .{ .elementtype = llvm_elem_ty }, &o.builder); - llvm.setCallElemTypeAttr(call.toLlvm(&self.wip), i, llvm_elem_ty.toLlvm(&o.builder)); - } - } + ); var ret_val = call; llvm_ret_i = 0; diff --git a/src/codegen/llvm/Builder.zig b/src/codegen/llvm/Builder.zig index 1026b9c88ee1..08ada8a81bb1 100644 --- a/src/codegen/llvm/Builder.zig +++ b/src/codegen/llvm/Builder.zig @@ -1072,7 +1072,7 @@ pub const Attribute = union(Kind) { _: 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, "\"#")) |_| @compileError("invalid format string: '" ++ fmt_str ++ "'"); const attribute = data.attribute_index.toAttribute(data.builder); switch (attribute) { @@ -1154,17 +1154,72 @@ pub const Attribute = union(Kind) { .sret, .elementtype, => |ty| try writer.print(" {s}({%})", .{ @tagName(attribute), ty.fmt(data.builder) }), - .@"align" => @panic("todo"), + .@"align" => |alignment| try writer.print("{}", .{alignment}), .dereferenceable, .dereferenceable_or_null, - => @panic("todo"), - .nofpclass => @panic("todo"), - .alignstack => @panic("todo"), - .allockind => @panic("todo"), - .allocsize => @panic("todo"), - .memory => @panic("todo"), - .uwtable => @panic("todo"), - .vscale_range => @panic("todo"), + => |size| try writer.print(" {s}({d})", .{ @tagName(attribute), size }), + .nofpclass => |fpclass| { + const Int = @typeInfo(FpClass).Struct.backing_integer.?; + try writer.print("{s}(", .{@tagName(attribute)}); + var any = false; + var remaining: Int = @bitCast(fpclass); + inline for (@typeInfo(FpClass).Struct.decls) |decl| { + if (!decl.is_pub) continue; + const pattern: Int = @bitCast(@field(FpClass, decl.name)); + if (remaining & pattern == pattern) { + if (!any) { + try writer.writeByte(' '); + any = true; + } + try writer.writeAll(decl.name); + remaining &= ~pattern; + } + } + try writer.writeByte(')'); + }, + .alignstack => |alignment| try writer.print( + if (comptime std.mem.indexOfScalar(u8, fmt_str, '#') != null) + "{s}={d}" + else + "{s}({d})", + .{ @tagName(attribute), alignment.toByteUnits() orelse return }, + ), + .allockind => |allockind| { + try writer.print("{s}(\"", .{@tagName(attribute)}); + var any = false; + inline for (@typeInfo(AllocKind).Struct.fields) |field| { + if (comptime std.mem.eql(u8, field.name, "_")) continue; + if (@field(allockind, field.name)) { + if (!any) { + try writer.writeByte(','); + any = true; + } + try writer.writeAll(field.name); + } + } + try writer.writeAll("\")"); + }, + .allocsize => |allocsize| { + try writer.print("{s}({d}", .{ @tagName(attribute), allocsize.elem_size }); + if (allocsize.num_elems != AllocSize.none) + try writer.print(",{d}", .{allocsize.num_elems}); + try writer.writeByte(')'); + }, + .memory => |memory| try writer.print("{s}({s}, argmem: {s}, inaccessiblemem: {s})", .{ + @tagName(attribute), + @tagName(memory.other), + @tagName(memory.argmem), + @tagName(memory.inaccessiblemem), + }), + .uwtable => |uwtable| if (uwtable != .none) { + try writer.writeAll(@tagName(attribute)); + if (uwtable != UwTable.default) try writer.print("({s})", .{@tagName(uwtable)}); + }, + .vscale_range => |vscale_range| try writer.print("{s}({d},{d})", .{ + @tagName(attribute), + vscale_range.min.toByteUnits().?, + vscale_range.max.toByteUnits() orelse 0, + }), .string => |string_attr| if (comptime std.mem.indexOfScalar(u8, fmt_str, '"') != null) { try writer.print(" {\"}", .{string_attr.kind.fmt(data.builder)}); if (string_attr.value != .empty) @@ -1314,11 +1369,6 @@ pub const Attribute = union(Kind) { positive_infinity: bool = false, _: u22 = 0, - pub const nan = FpClass{ .signaling_nan = true, .quiet_nan = true }; - pub const inf = FpClass{ .negative_infinity = true, .positive_infinity = true }; - pub const norm = FpClass{ .positive_normal = true, .negative_normal = true }; - pub const sub = FpClass{ .positive_subnormal = true, .negative_subnormal = true }; - pub const zero = FpClass{ .positive_zero = true, .negative_zero = true }; pub const all = FpClass{ .signaling_nan = true, .quiet_nan = true, @@ -1331,16 +1381,26 @@ pub const Attribute = union(Kind) { .positive_normal = true, .positive_infinity = true, }; + + pub const nan = FpClass{ .signaling_nan = true, .quiet_nan = true }; pub const snan = FpClass{ .signaling_nan = true }; pub const qnan = FpClass{ .quiet_nan = true }; + + pub const inf = FpClass{ .negative_infinity = true, .positive_infinity = true }; pub const ninf = FpClass{ .negative_infinity = true }; - pub const nnorm = FpClass{ .negative_normal = true }; - pub const nsub = FpClass{ .negative_subnormal = true }; + pub const pinf = FpClass{ .positive_infinity = true }; + + pub const zero = FpClass{ .positive_zero = true, .negative_zero = true }; pub const nzero = FpClass{ .negative_zero = true }; pub const pzero = FpClass{ .positive_zero = true }; + + pub const sub = FpClass{ .positive_subnormal = true, .negative_subnormal = true }; + pub const nsub = FpClass{ .negative_subnormal = true }; pub const psub = FpClass{ .positive_subnormal = true }; + + pub const norm = FpClass{ .positive_normal = true, .negative_normal = true }; + pub const nnorm = FpClass{ .negative_normal = true }; pub const pnorm = FpClass{ .positive_normal = true }; - pub const pinf = FpClass{ .positive_infinity = true }; }; pub const AllocKind = packed struct(u32) { @@ -1924,7 +1984,7 @@ pub const CallConv = enum(u10) { writer: anytype, ) @TypeOf(writer).Error!void { switch (self) { - .ccc => {}, + default => {}, .fastcc, .coldcc, .ghccc, @@ -2445,6 +2505,7 @@ pub const Function = struct { .br_cond, .ret, .@"ret void", + .@"switch", .@"unreachable", => true, else => false, @@ -2462,6 +2523,7 @@ pub const Function = struct { .@"store atomic", .@"store atomic volatile", .@"store volatile", + .@"switch", .@"unreachable", => false, .call, @@ -2472,6 +2534,7 @@ pub const Function = struct { .@"notail call fast", .@"tail call", .@"tail call fast", + .unimplemented, => self.typeOfWip(wip) != .void, else => true, }; @@ -4165,7 +4228,7 @@ pub const WipFunction = struct { callee: Value, args: []const Value, name: []const u8, - ) if (build_options.have_llvm) Allocator.Error!Value else Value { + ) Allocator.Error!Value { const ret_ty = ty.functionReturn(self.builder); assert(ty.isFunction(self.builder)); assert(callee.typeOfWip(self).isPointer(self.builder)); @@ -4238,6 +4301,20 @@ pub const WipFunction = struct { return instruction.toValue(); } + pub fn callAsm( + self: *WipFunction, + function_attributes: FunctionAttributes, + ty: Type, + kind: Constant.Asm.Info, + assembly: String, + constraints: String, + args: []const Value, + name: []const u8, + ) Allocator.Error!Value { + const callee = try self.builder.asmValue(ty, kind, assembly, constraints); + return self.call(.normal, CallConv.default, function_attributes, ty, callee, args, name); + } + pub fn vaArg(self: *WipFunction, list: Value, ty: Type, name: []const u8) Allocator.Error!Value { try self.ensureUnusedExtraCapacity(1, Instruction.VaArg, 0); const instruction = try self.addInst(name, .{ @@ -5216,7 +5293,7 @@ pub const Constant = enum(u32) { const first_global: Constant = @enumFromInt(1 << 30); - pub const Tag = enum(u6) { + pub const Tag = enum(u7) { positive_integer, negative_integer, half, @@ -5276,6 +5353,22 @@ pub const Constant = enum(u32) { @"and", @"or", xor, + @"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", }; pub const Item = struct { @@ -5371,6 +5464,19 @@ pub const Constant = enum(u32) { rhs: Constant, }; + pub const Asm = extern struct { + type: Type, + assembly: String, + constraints: String, + + pub const Info = packed struct { + sideeffect: bool = false, + alignstack: bool = false, + inteldialect: bool = false, + unwind: bool = false, + }; + }; + pub fn unwrap(self: Constant) union(enum) { constant: u30, global: Global.Index, @@ -5489,6 +5595,23 @@ pub const Constant = enum(u32) { .@"or", .xor, => builder.constantExtraData(Binary, item.data).lhs.typeOf(builder), + .@"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", + => .ptr, }; }, .global => |global| return builder.ptrTypeAssumeCapacity( @@ -5836,6 +5959,30 @@ pub const Constant = enum(u32) { extra.rhs.fmt(data.builder), }); }, + .@"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 = data.builder.constantExtraData(Asm, item.data); + try writer.print("{s} {\"}, {\"}", .{ + @tagName(tag), + extra.assembly.fmt(data.builder), + extra.constraints.fmt(data.builder), + }); + }, } }, .global => |global| try writer.print("{}", .{global.fmt(data.builder)}), @@ -6972,6 +7119,27 @@ pub fn binValue(self: *Builder, tag: Constant.Tag, lhs: Constant, rhs: Constant) return (try self.binConst(tag, lhs, rhs)).toValue(); } +pub fn asmConst( + self: *Builder, + ty: Type, + info: Constant.Asm.Info, + assembly: String, + constraints: String, +) Allocator.Error!Constant { + try self.ensureUnusedConstantCapacity(1, Constant.Asm, 0); + return self.asmConstAssumeCapacity(ty, info, assembly, constraints); +} + +pub fn asmValue( + self: *Builder, + ty: Type, + info: Constant.Asm.Info, + assembly: String, + constraints: String, +) Allocator.Error!Value { + return (try self.asmConst(ty, info, assembly, constraints)).toValue(); +} + pub fn dump(self: *Builder) void { if (self.useLibLlvm()) self.llvm.module.?.dump() @@ -7303,7 +7471,15 @@ pub fn printUnbuffered( arg.fmt(function_index, self), }); } - try writer.print("){}\n", .{extra.data.attributes.func(self).fmt(self)}); + try writer.writeByte(')'); + const call_function_attributes = extra.data.attributes.func(self); + if (call_function_attributes != .none) try writer.print(" #{d}", .{ + (try attribute_groups.getOrPutValue( + self.gpa, + call_function_attributes, + {}, + )).index, + }); }, .extractelement => |tag| { const extra = @@ -7401,13 +7577,19 @@ pub fn printUnbuffered( => |tag| { const extra = function.extraData(Function.Instruction.Binary, instruction.data); const ty = instruction_index.typeOf(function_index, self); - try writer.print(" %{} = call {%} @{s}{m}({%}, {%})\n", .{ + try writer.print(" %{} = call {%} @{s}{m}({%}, {%}{s})\n", .{ instruction_index.name(&function).fmt(self), ty.fmt(self), @tagName(tag), ty.fmt(self), extra.lhs.fmt(function_index, self), extra.rhs.fmt(function_index, self), + switch (tag) { + .@"llvm.smul.fix.sat.", + .@"llvm.umul.fix.sat.", + => ", i32 0", + else => "", + }, }); }, .load, @@ -7494,7 +7676,7 @@ pub fn printUnbuffered( const vals = extra.trail.next(extra.data.cases_len, Constant, &function); const blocks = extra.trail.next(extra.data.cases_len, Function.Block.Index, &function); - try writer.print(" {s} {%}, {%} [", .{ + try writer.print(" {s} {%}, {%} [\n", .{ @tagName(tag), extra.data.val.fmt(function_index, self), extra.data.default.toInst(&function).fmt(function_index, self), @@ -7507,14 +7689,22 @@ pub fn printUnbuffered( }, .unimplemented => |tag| { const ty: Type = @enumFromInt(instruction.data); - try writer.writeAll(" "); - switch (ty) { + if (true) { + try writer.writeAll(" "); + switch (ty) { + .none, .void => {}, + else => try writer.print("%{} = ", .{ + instruction_index.name(&function).fmt(self), + }), + } + try writer.print("{s} {%}\n", .{ @tagName(tag), ty.fmt(self) }); + } else switch (ty) { .none, .void => {}, - else => try writer.print("%{} = ", .{ + else => try writer.print(" %{} = load {%}, ptr undef\n", .{ instruction_index.name(&function).fmt(self), + ty.fmt(self), }), } - try writer.print("{s} {%}\n", .{ @tagName(tag), ty.fmt(self) }); }, .va_arg => |tag| { const extra = function.extraData(Function.Instruction.VaArg, instruction.data); @@ -7534,7 +7724,7 @@ pub fn printUnbuffered( for (0.., attribute_groups.keys()) |attribute_group_index, attribute_group| try writer.print( - \\attribute #{d} = {{{"} }} + \\attributes #{d} = {{{#"} }} \\ , .{ attribute_group_index, attribute_group.fmt(self) }); } @@ -9080,30 +9270,30 @@ fn binConstAssumeCapacity( => {}, else => unreachable, } - const Key = struct { tag: Constant.Tag, bin: Constant.Binary }; + const Key = struct { tag: Constant.Tag, extra: Constant.Binary }; const Adapter = struct { builder: *const Builder, pub fn hash(_: @This(), key: Key) u32 { return @truncate(std.hash.Wyhash.hash( std.hash.uint32(@intFromEnum(key.tag)), - std.mem.asBytes(&key.bin), + std.mem.asBytes(&key.extra), )); } pub fn eql(ctx: @This(), lhs_key: Key, _: void, rhs_index: usize) bool { if (lhs_key.tag != ctx.builder.constant_items.items(.tag)[rhs_index]) return false; const rhs_data = ctx.builder.constant_items.items(.data)[rhs_index]; const rhs_extra = ctx.builder.constantExtraData(Constant.Binary, rhs_data); - return std.meta.eql(lhs_key.bin, rhs_extra); + return std.meta.eql(lhs_key.extra, rhs_extra); } }; - const data = Key{ .tag = tag, .bin = .{ .lhs = lhs, .rhs = rhs } }; + const data = Key{ .tag = tag, .extra = .{ .lhs = lhs, .rhs = rhs } }; const gop = self.constant_map.getOrPutAssumeCapacityAdapted(data, Adapter{ .builder = self }); if (!gop.found_existing) { gop.key_ptr.* = {}; gop.value_ptr.* = {}; self.constant_items.appendAssumeCapacity(.{ .tag = tag, - .data = self.addConstantExtraAssumeCapacity(data.bin), + .data = self.addConstantExtraAssumeCapacity(data.extra), }); if (self.useLibLlvm()) self.llvm.constants.appendAssumeCapacity(switch (tag) { .add => &llvm.Value.constAdd, @@ -9121,6 +9311,61 @@ fn binConstAssumeCapacity( return @enumFromInt(gop.index); } +fn asmConstAssumeCapacity( + self: *Builder, + ty: Type, + info: Constant.Asm.Info, + assembly: String, + constraints: String, +) Constant { + const Key = struct { tag: Constant.Tag, extra: Constant.Asm }; + const Adapter = struct { + builder: *const Builder, + pub fn hash(_: @This(), key: Key) u32 { + return @truncate(std.hash.Wyhash.hash( + std.hash.uint32(@intFromEnum(key.tag)), + std.mem.asBytes(&key.extra), + )); + } + pub fn eql(ctx: @This(), lhs_key: Key, _: void, rhs_index: usize) bool { + if (lhs_key.tag != ctx.builder.constant_items.items(.tag)[rhs_index]) return false; + const rhs_data = ctx.builder.constant_items.items(.data)[rhs_index]; + const rhs_extra = ctx.builder.constantExtraData(Constant.Asm, rhs_data); + return std.meta.eql(lhs_key.extra, rhs_extra); + } + }; + + const data = Key{ + .tag = @enumFromInt(@intFromEnum(Constant.Tag.@"asm") + @as(u4, @bitCast(info))), + .extra = .{ .type = ty, .assembly = assembly, .constraints = constraints }, + }; + const gop = self.constant_map.getOrPutAssumeCapacityAdapted(data, Adapter{ .builder = self }); + if (!gop.found_existing) { + gop.key_ptr.* = {}; + gop.value_ptr.* = {}; + self.constant_items.appendAssumeCapacity(.{ + .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); +} + fn ensureUnusedConstantCapacity( self: *Builder, count: usize, @@ -9211,7 +9456,7 @@ fn addConstantExtraAssumeCapacity(self: *Builder, extra: anytype) Constant.Item. const value = @field(extra, field.name); self.constant_extra.appendAssumeCapacity(switch (field.type) { u32 => value, - Type, Constant, Function.Index, Function.Block.Index => @intFromEnum(value), + String, Type, Constant, Function.Index, Function.Block.Index => @intFromEnum(value), Constant.GetElementPtr.Info => @bitCast(value), else => @compileError("bad field type: " ++ @typeName(field.type)), }); @@ -9250,7 +9495,7 @@ fn constantExtraDataTrail( inline for (fields, self.constant_extra.items[index..][0..fields.len]) |field, value| @field(result, field.name) = switch (field.type) { u32 => value, - Type, Constant, Function.Index, Function.Block.Index => @enumFromInt(value), + String, Type, Constant, Function.Index, Function.Block.Index => @enumFromInt(value), Constant.GetElementPtr.Info => @bitCast(value), else => @compileError("bad field type: " ++ @typeName(field.type)), }; From 3fc2e36de2d125b2de255b03abc24bea30899caf Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sun, 23 Jul 2023 06:15:26 -0400 Subject: [PATCH 5/5] llvm: convert global assembly --- src/codegen/llvm.zig | 14 +- src/codegen/llvm/Builder.zig | 1160 ++++++++++++++++++--------------- src/codegen/llvm/bindings.zig | 2 +- 3 files changed, 632 insertions(+), 544 deletions(-) diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 43a4b9886662..87ed52c31b77 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -1034,15 +1034,11 @@ pub const Object = struct { fn genModuleLevelAssembly(object: *Object) !void { const mod = object.module; - if (mod.global_assembly.count() == 0) return; - var buffer = std.ArrayList(u8).init(mod.gpa); - defer buffer.deinit(); - var it = mod.global_assembly.iterator(); - while (it.next()) |kv| { - try buffer.appendSlice(kv.value_ptr.*); - try buffer.append('\n'); - } - object.llvm_module.setModuleInlineAsm2(buffer.items.ptr, buffer.items.len - 1); + + const writer = object.builder.setModuleAsm(); + var it = mod.global_assembly.valueIterator(); + while (it.next()) |assembly| try writer.print("{s}\n", .{assembly.*}); + try object.builder.finishModuleAsm(); } fn resolveExportExternCollisions(object: *Object) !void { diff --git a/src/codegen/llvm/Builder.zig b/src/codegen/llvm/Builder.zig index 08ada8a81bb1..1b25e285ff06 100644 --- a/src/codegen/llvm/Builder.zig +++ b/src/codegen/llvm/Builder.zig @@ -18,6 +18,7 @@ llvm: if (build_options.have_llvm) struct { source_filename: String, data_layout: String, target_triple: String, +module_asm: std.ArrayListUnmanaged(u8), string_map: std.AutoArrayHashMapUnmanaged(void, void), string_indices: std.ArrayListUnmanaged(u32), @@ -95,18 +96,12 @@ pub const String = enum(u32) { assert(data.string != .none); const sentinel_slice = data.string.slice(data.builder) orelse return writer.print("{d}", .{@intFromEnum(data.string)}); - const full_slice = sentinel_slice[0 .. sentinel_slice.len + comptime @intFromBool( + try printEscapedString(sentinel_slice[0 .. sentinel_slice.len + comptime @intFromBool( std.mem.indexOfScalar(u8, fmt_str, '@') != null, - )]; - const need_quotes = (comptime std.mem.indexOfScalar(u8, fmt_str, '"') != null) or - !isValidIdentifier(full_slice); - if (need_quotes) try writer.writeByte('"'); - for (full_slice) |character| switch (character) { - '\\' => try writer.writeAll("\\\\"), - ' '...'"' - 1, '"' + 1...'\\' - 1, '\\' + 1...'~' => try writer.writeByte(character), - else => try writer.print("\\{X:0>2}", .{character}), - }; - if (need_quotes) try writer.writeByte('"'); + )], 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 } }; @@ -3812,7 +3807,7 @@ pub const WipFunction = struct { @intFromEnum(addr_space), instruction.llvmName(self), ); - if (alignment.toByteUnits()) |a| llvm_instruction.setAlignment(@intCast(a)); + if (alignment.toByteUnits()) |bytes| llvm_instruction.setAlignment(@intCast(bytes)); self.llvm.instructions.appendAssumeCapacity(llvm_instruction); } return instruction.toValue(); @@ -3868,7 +3863,7 @@ pub const WipFunction = struct { instruction.llvmName(self), ); if (ordering != .none) llvm_instruction.setOrdering(@enumFromInt(@intFromEnum(ordering))); - if (alignment.toByteUnits()) |a| llvm_instruction.setAlignment(@intCast(a)); + if (alignment.toByteUnits()) |bytes| llvm_instruction.setAlignment(@intCast(bytes)); self.llvm.instructions.appendAssumeCapacity(llvm_instruction); } return instruction.toValue(); @@ -3922,7 +3917,7 @@ pub const WipFunction = struct { .@"volatile" => llvm_instruction.setVolatile(.True), } if (ordering != .none) llvm_instruction.setOrdering(@enumFromInt(@intFromEnum(ordering))); - if (alignment.toByteUnits()) |a| llvm_instruction.setAlignment(@intCast(a)); + if (alignment.toByteUnits()) |bytes| llvm_instruction.setAlignment(@intCast(bytes)); self.llvm.instructions.appendAssumeCapacity(llvm_instruction); } return instruction; @@ -6090,6 +6085,7 @@ pub fn init(options: Options) InitError!Builder { .source_filename = .none, .data_layout = .none, .target_triple = .none, + .module_asm = .{}, .string_map = .{}, .string_indices = .{}, @@ -6207,6 +6203,8 @@ pub fn init(options: Options) InitError!Builder { } pub fn deinit(self: *Builder) void { + self.module_asm.deinit(self.gpa); + self.string_map.deinit(self.gpa); self.string_indices.deinit(self.gpa); self.string_bytes.deinit(self.gpa); @@ -6440,6 +6438,22 @@ pub fn initializeLLVMTarget(self: *const Builder, arch: std.Target.Cpu.Arch) voi } } +pub fn setModuleAsm(self: *Builder) std.ArrayListUnmanaged(u8).Writer { + self.module_asm.clearRetainingCapacity(); + return self.appendModuleAsm(); +} + +pub fn appendModuleAsm(self: *Builder) std.ArrayListUnmanaged(u8).Writer { + return self.module_asm.writer(self.gpa); +} + +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_indices.ensureUnusedCapacity(self.gpa, 1); @@ -7185,548 +7199,605 @@ pub fn printUnbuffered( self: *Builder, writer: anytype, ) (@TypeOf(writer).Error || Allocator.Error)!void { - if (self.source_filename != .none) try writer.print( - \\; ModuleID = '{s}' - \\source_filename = {"} - \\ - , .{ self.source_filename.slice(self).?, self.source_filename.fmt(self) }); - if (self.data_layout != .none) try writer.print( - \\target datalayout = {"} - \\ - , .{self.data_layout.fmt(self)}); - if (self.target_triple != .none) try writer.print( - \\target triple = {"} - \\ - , .{self.target_triple.fmt(self)}); - try writer.writeByte('\n'); - - for (self.types.keys(), self.types.values()) |id, ty| try writer.print( - \\%{} = type {} - \\ - , .{ id.fmt(self), ty.fmt(self) }); - try writer.writeByte('\n'); - - for (self.variables.items) |variable| { - if (variable.global.getReplacement(self) != .none) continue; - const global = variable.global.ptrConst(self); - try writer.print( - \\{} ={}{}{}{}{}{}{}{} {s} {%}{ }{,} + var need_newline = false; + + if (self.source_filename != .none or self.data_layout != .none or self.target_triple != .none) { + if (need_newline) try writer.writeByte('\n'); + if (self.source_filename != .none) try writer.print( + \\; ModuleID = '{s}' + \\source_filename = {"} \\ - , .{ - variable.global.fmt(self), - global.linkage, - global.preemption, - global.visibility, - global.dll_storage_class, - variable.thread_local, - global.unnamed_addr, - global.addr_space, - global.externally_initialized, - @tagName(variable.mutability), - global.type.fmt(self), - variable.init.fmt(self), - variable.alignment, - }); + , .{ self.source_filename.slice(self).?, self.source_filename.fmt(self) }); + if (self.data_layout != .none) try writer.print( + \\target datalayout = {"} + \\ + , .{self.data_layout.fmt(self)}); + if (self.target_triple != .none) try writer.print( + \\target triple = {"} + \\ + , .{self.target_triple.fmt(self)}); + need_newline = true; } - try writer.writeByte('\n'); - var attribute_groups: std.AutoArrayHashMapUnmanaged(Attributes, void) = .{}; - defer attribute_groups.deinit(self.gpa); - for (0.., self.functions.items) |function_i, function| { - const function_index: Function.Index = @enumFromInt(function_i); - if (function.global.getReplacement(self) != .none) continue; - const global = function.global.ptrConst(self); - const params_len = global.type.functionParameters(self).len; - const function_attributes = function.attributes.func(self); - if (function_attributes != .none) try writer.print( - \\; Function Attrs:{} + if (self.module_asm.items.len > 0) { + if (need_newline) try writer.writeByte('\n'); + 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'); + for (self.types.keys(), self.types.values()) |id, ty| try writer.print( + \\%{} = type {} \\ - , .{function_attributes.fmt(self)}); - try writer.print( - \\{s}{}{}{}{}{}{"} {} {}( - , .{ - if (function.instructions.len > 0) "define" else "declare", - global.linkage, - global.preemption, - global.visibility, - global.dll_storage_class, - function.call_conv, - function.attributes.ret(self).fmt(self), - global.type.functionReturn(self).fmt(self), - function.global.fmt(self), - }); - for (0..params_len) |arg| { - if (arg > 0) try writer.writeAll(", "); + , .{ id.fmt(self), ty.fmt(self) }); + need_newline = true; + } + + if (self.variables.items.len > 0) { + if (need_newline) try writer.writeByte('\n'); + for (self.variables.items) |variable| { + if (variable.global.getReplacement(self) != .none) continue; + const global = variable.global.ptrConst(self); try writer.print( - \\{%}{"} + \\{} ={}{}{}{}{}{}{}{} {s} {%}{ }{,} + \\ , .{ - global.type.functionParameters(self)[arg].fmt(self), - function.attributes.param(arg, self).fmt(self), + variable.global.fmt(self), + global.linkage, + global.preemption, + global.visibility, + global.dll_storage_class, + variable.thread_local, + global.unnamed_addr, + global.addr_space, + global.externally_initialized, + @tagName(variable.mutability), + global.type.fmt(self), + variable.init.fmt(self), + variable.alignment, }); - if (function.instructions.len > 0) - try writer.print(" {}", .{function.arg(@intCast(arg)).fmt(function_index, self)}); - } - switch (global.type.functionKind(self)) { - .normal => {}, - .vararg => { - if (params_len > 0) try writer.writeAll(", "); - try writer.writeAll("..."); - }, } - try writer.print("){}{}", .{ global.unnamed_addr, global.addr_space }); - if (function_attributes != .none) try writer.print(" #{d}", .{ - (try attribute_groups.getOrPutValue(self.gpa, function_attributes, {})).index, - }); - try writer.print("{}", .{function.alignment}); - if (function.instructions.len > 0) { - var block_incoming_len: u32 = undefined; - try writer.writeAll(" {\n"); - 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)); - switch (instruction.tag) { - .add, - .@"add nsw", - .@"add nuw", - .@"add nuw nsw", - .@"and", - .ashr, - .@"ashr exact", - .fadd, - .@"fadd fast", - .@"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", - .fdiv, - .@"fdiv fast", - .fmul, - .@"fmul fast", - .frem, - .@"frem fast", - .fsub, - .@"fsub fast", - .@"icmp eq", - .@"icmp ne", - .@"icmp sge", - .@"icmp sgt", - .@"icmp sle", - .@"icmp slt", - .@"icmp uge", - .@"icmp ugt", - .@"icmp ule", - .@"icmp ult", - .lshr, - .@"lshr exact", - .mul, - .@"mul nsw", - .@"mul nuw", - .@"mul nuw nsw", - .@"or", - .sdiv, - .@"sdiv exact", - .srem, - .shl, - .@"shl nsw", - .@"shl nuw", - .@"shl nuw nsw", - .sub, - .@"sub nsw", - .@"sub nuw", - .@"sub nuw nsw", - .udiv, - .@"udiv exact", - .urem, - .xor, - => |tag| { - const extra = function.extraData(Function.Instruction.Binary, instruction.data); - try writer.print(" %{} = {s} {%}, {}\n", .{ - instruction_index.name(&function).fmt(self), - @tagName(tag), - extra.lhs.fmt(function_index, self), - extra.rhs.fmt(function_index, self), - }); - }, - .addrspacecast, - .bitcast, - .fpext, - .fptosi, - .fptoui, - .fptrunc, - .inttoptr, - .ptrtoint, - .sext, - .sitofp, - .trunc, - .uitofp, - .zext, - => |tag| { - const extra = function.extraData(Function.Instruction.Cast, instruction.data); - try writer.print(" %{} = {s} {%} to {%}\n", .{ - instruction_index.name(&function).fmt(self), - @tagName(tag), - extra.val.fmt(function_index, self), - extra.type.fmt(self), - }); - }, - .alloca, - .@"alloca inalloca", - => |tag| { - const extra = function.extraData(Function.Instruction.Alloca, instruction.data); - try writer.print(" %{} = {s} {%}{,%}{,}{,}\n", .{ - instruction_index.name(&function).fmt(self), - @tagName(tag), - extra.type.fmt(self), - extra.len.fmt(function_index, self), - extra.info.alignment, - extra.info.addr_space, - }); - }, - .arg => unreachable, - .block => { - block_incoming_len = instruction.data; - const name = instruction_index.name(&function); - if (@intFromEnum(instruction_index) > params_len) try writer.writeByte('\n'); - try writer.print("{}:\n", .{name.fmt(self)}); - }, - .br => |tag| { - const target: Function.Block.Index = @enumFromInt(instruction.data); - try writer.print(" {s} {%}\n", .{ - @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", .{ - extra.cond.fmt(function_index, self), - extra.then.toInst(&function).fmt(function_index, self), - extra.@"else".toInst(&function).fmt(function_index, self), - }); - }, - .call, - .@"call fast", - .@"musttail call", - .@"musttail call fast", - .@"notail call", - .@"notail call fast", - .@"tail call", - .@"tail call fast", - => |tag| { - var extra = - function.extraDataTrail(Function.Instruction.Call, instruction.data); - const args = extra.trail.next(extra.data.args_len, Value, &function); - try writer.writeAll(" "); - const ret_ty = extra.data.ty.functionReturn(self); - switch (ret_ty) { - .void => {}, - else => try writer.print("%{} = ", .{ + need_newline = true; + } + + var attribute_groups: std.AutoArrayHashMapUnmanaged(Attributes, void) = .{}; + defer attribute_groups.deinit(self.gpa); + + if (self.functions.items.len > 0) { + if (need_newline) try writer.writeByte('\n'); + for (0.., self.functions.items) |function_i, function| { + if (function_i > 0) try writer.writeByte('\n'); + const function_index: Function.Index = @enumFromInt(function_i); + if (function.global.getReplacement(self) != .none) continue; + const global = function.global.ptrConst(self); + const params_len = global.type.functionParameters(self).len; + const function_attributes = function.attributes.func(self); + if (function_attributes != .none) try writer.print( + \\; Function Attrs:{} + \\ + , .{function_attributes.fmt(self)}); + try writer.print( + \\{s}{}{}{}{}{}{"} {} {}( + , .{ + if (function.instructions.len > 0) "define" else "declare", + global.linkage, + global.preemption, + global.visibility, + global.dll_storage_class, + function.call_conv, + function.attributes.ret(self).fmt(self), + global.type.functionReturn(self).fmt(self), + function.global.fmt(self), + }); + for (0..params_len) |arg| { + if (arg > 0) try writer.writeAll(", "); + try writer.print( + \\{%}{"} + , .{ + global.type.functionParameters(self)[arg].fmt(self), + function.attributes.param(arg, self).fmt(self), + }); + if (function.instructions.len > 0) + try writer.print(" {}", .{function.arg(@intCast(arg)).fmt(function_index, self)}); + } + switch (global.type.functionKind(self)) { + .normal => {}, + .vararg => { + if (params_len > 0) try writer.writeAll(", "); + try writer.writeAll("..."); + }, + } + try writer.print("){}{}", .{ global.unnamed_addr, global.addr_space }); + if (function_attributes != .none) try writer.print(" #{d}", .{ + (try attribute_groups.getOrPutValue(self.gpa, function_attributes, {})).index, + }); + try writer.print("{}", .{function.alignment}); + if (function.instructions.len > 0) { + var block_incoming_len: u32 = undefined; + try writer.writeAll(" {\n"); + 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)); + switch (instruction.tag) { + .add, + .@"add nsw", + .@"add nuw", + .@"add nuw nsw", + .@"and", + .ashr, + .@"ashr exact", + .fadd, + .@"fadd fast", + .@"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", + .fdiv, + .@"fdiv fast", + .fmul, + .@"fmul fast", + .frem, + .@"frem fast", + .fsub, + .@"fsub fast", + .@"icmp eq", + .@"icmp ne", + .@"icmp sge", + .@"icmp sgt", + .@"icmp sle", + .@"icmp slt", + .@"icmp uge", + .@"icmp ugt", + .@"icmp ule", + .@"icmp ult", + .lshr, + .@"lshr exact", + .mul, + .@"mul nsw", + .@"mul nuw", + .@"mul nuw nsw", + .@"or", + .sdiv, + .@"sdiv exact", + .srem, + .shl, + .@"shl nsw", + .@"shl nuw", + .@"shl nuw nsw", + .sub, + .@"sub nsw", + .@"sub nuw", + .@"sub nuw nsw", + .udiv, + .@"udiv exact", + .urem, + .xor, + => |tag| { + const extra = + function.extraData(Function.Instruction.Binary, instruction.data); + try writer.print(" %{} = {s} {%}, {}\n", .{ instruction_index.name(&function).fmt(self), - }), - .none => unreachable, - } - try writer.print("{s}{}{}{} {%} {}(", .{ - @tagName(tag), - extra.data.info.call_conv, - extra.data.attributes.ret(self).fmt(self), - extra.data.callee.typeOf(function_index, self).pointerAddrSpace(self), - switch (extra.data.ty.functionKind(self)) { - .normal => ret_ty, - .vararg => extra.data.ty, - }.fmt(self), - extra.data.callee.fmt(function_index, self), - }); - for (0.., args) |arg_index, arg| { - if (arg_index > 0) try writer.writeAll(", "); - try writer.print("{%}{} {}", .{ - arg.typeOf(function_index, self).fmt(self), - extra.data.attributes.param(arg_index, self).fmt(self), - arg.fmt(function_index, self), + @tagName(tag), + extra.lhs.fmt(function_index, self), + extra.rhs.fmt(function_index, self), }); - } - try writer.writeByte(')'); - const call_function_attributes = extra.data.attributes.func(self); - if (call_function_attributes != .none) try writer.print(" #{d}", .{ - (try attribute_groups.getOrPutValue( - self.gpa, - call_function_attributes, - {}, - )).index, - }); - }, - .extractelement => |tag| { - const extra = - function.extraData(Function.Instruction.ExtractElement, instruction.data); - try writer.print(" %{} = {s} {%}, {%}\n", .{ - instruction_index.name(&function).fmt(self), - @tagName(tag), - extra.val.fmt(function_index, self), - extra.index.fmt(function_index, self), - }); - }, - .extractvalue => |tag| { - var extra = - function.extraDataTrail(Function.Instruction.ExtractValue, instruction.data); - const indices = extra.trail.next(extra.data.indices_len, u32, &function); - try writer.print(" %{} = {s} {%}", .{ - instruction_index.name(&function).fmt(self), - @tagName(tag), - 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); - try writer.print(" {s}{}{}", .{ @tagName(tag), info.scope, info.ordering }); - }, - .fneg, - .@"fneg fast", - .ret, - => |tag| { - const val: Value = @enumFromInt(instruction.data); - try writer.print(" {s} {%}\n", .{ - @tagName(tag), - val.fmt(function_index, self), - }); - }, - .getelementptr, - .@"getelementptr inbounds", - => |tag| { - var extra = function.extraDataTrail( - Function.Instruction.GetElementPtr, - instruction.data, - ); - const indices = extra.trail.next(extra.data.indices_len, Value, &function); - try writer.print(" %{} = {s} {%}, {%}", .{ - instruction_index.name(&function).fmt(self), - @tagName(tag), - extra.data.type.fmt(self), - extra.data.base.fmt(function_index, self), - }); - 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", .{ - instruction_index.name(&function).fmt(self), - @tagName(tag), - extra.val.fmt(function_index, self), - extra.elem.fmt(function_index, self), - extra.index.fmt(function_index, self), - }); - }, - .insertvalue => |tag| { - var extra = - function.extraDataTrail(Function.Instruction.InsertValue, instruction.data); - const indices = extra.trail.next(extra.data.indices_len, u32, &function); - try writer.print(" %{} = {s} {%}, {%}", .{ - instruction_index.name(&function).fmt(self), - @tagName(tag), - extra.data.val.fmt(function_index, self), - extra.data.elem.fmt(function_index, self), - }); - for (indices) |index| try writer.print(", {d}", .{index}); - try writer.writeByte('\n'); - }, - .@"llvm.maxnum.", - .@"llvm.minnum.", - .@"llvm.sadd.sat.", - .@"llvm.smax.", - .@"llvm.smin.", - .@"llvm.smul.fix.sat.", - .@"llvm.sshl.sat.", - .@"llvm.ssub.sat.", - .@"llvm.uadd.sat.", - .@"llvm.umax.", - .@"llvm.umin.", - .@"llvm.umul.fix.sat.", - .@"llvm.ushl.sat.", - .@"llvm.usub.sat.", - => |tag| { - const extra = function.extraData(Function.Instruction.Binary, instruction.data); - const ty = instruction_index.typeOf(function_index, self); - try writer.print(" %{} = call {%} @{s}{m}({%}, {%}{s})\n", .{ - instruction_index.name(&function).fmt(self), - ty.fmt(self), - @tagName(tag), - ty.fmt(self), - extra.lhs.fmt(function_index, self), - extra.rhs.fmt(function_index, self), - switch (tag) { - .@"llvm.smul.fix.sat.", - .@"llvm.umul.fix.sat.", - => ", i32 0", - else => "", - }, - }); - }, - .load, - .@"load atomic", - .@"load atomic volatile", - .@"load volatile", - => |tag| { - const extra = function.extraData(Function.Instruction.Load, instruction.data); - try writer.print(" %{} = {s} {%}, {%}{}{}{,}\n", .{ - instruction_index.name(&function).fmt(self), - @tagName(tag), - extra.type.fmt(self), - extra.ptr.fmt(function_index, self), - extra.info.scope, - extra.info.ordering, - extra.info.alignment, - }); - }, - .phi, - .@"phi fast", - => |tag| { - var extra = function.extraDataTrail(Function.Instruction.Phi, instruction.data); - const vals = extra.trail.next(block_incoming_len, Value, &function); - const blocks = - extra.trail.next(block_incoming_len, Function.Block.Index, &function); - try writer.print(" %{} = {s} {%} ", .{ - instruction_index.name(&function).fmt(self), - @tagName(tag), - vals[0].typeOf(function_index, self).fmt(self), - }); - for (0.., vals, blocks) |incoming_index, incoming_val, incoming_block| { - if (incoming_index > 0) try writer.writeAll(", "); - try writer.print("[ {}, {} ]", .{ - incoming_val.fmt(function_index, self), - incoming_block.toInst(&function).fmt(function_index, self), + }, + .addrspacecast, + .bitcast, + .fpext, + .fptosi, + .fptoui, + .fptrunc, + .inttoptr, + .ptrtoint, + .sext, + .sitofp, + .trunc, + .uitofp, + .zext, + => |tag| { + const extra = + function.extraData(Function.Instruction.Cast, instruction.data); + try writer.print(" %{} = {s} {%} to {%}\n", .{ + instruction_index.name(&function).fmt(self), + @tagName(tag), + extra.val.fmt(function_index, self), + extra.type.fmt(self), }); - } - try writer.writeByte('\n'); - }, - .@"ret void", - .@"unreachable", - => |tag| try writer.print(" {s}\n", .{@tagName(tag)}), - .select, - .@"select fast", - => |tag| { - const extra = function.extraData(Function.Instruction.Select, instruction.data); - try writer.print(" %{} = {s} {%}, {%}, {%}\n", .{ - instruction_index.name(&function).fmt(self), - @tagName(tag), - extra.cond.fmt(function_index, self), - extra.lhs.fmt(function_index, self), - extra.rhs.fmt(function_index, self), - }); - }, - .shufflevector => |tag| { - const extra = - function.extraData(Function.Instruction.ShuffleVector, instruction.data); - try writer.print(" %{} = {s} {%}, {%}, {%}\n", .{ - instruction_index.name(&function).fmt(self), - @tagName(tag), - extra.lhs.fmt(function_index, self), - extra.rhs.fmt(function_index, self), - extra.mask.fmt(function_index, self), - }); - }, - .store, - .@"store atomic", - .@"store atomic volatile", - .@"store volatile", - => |tag| { - const extra = function.extraData(Function.Instruction.Store, instruction.data); - try writer.print(" {s} {%}, {%}{}{}{,}\n", .{ - @tagName(tag), - extra.val.fmt(function_index, self), - extra.ptr.fmt(function_index, self), - extra.info.scope, - extra.info.ordering, - extra.info.alignment, - }); - }, - .@"switch" => |tag| { - var extra = - function.extraDataTrail(Function.Instruction.Switch, instruction.data); - const vals = extra.trail.next(extra.data.cases_len, Constant, &function); - const blocks = - extra.trail.next(extra.data.cases_len, Function.Block.Index, &function); - try writer.print(" {s} {%}, {%} [\n", .{ - @tagName(tag), - extra.data.val.fmt(function_index, self), - extra.data.default.toInst(&function).fmt(function_index, self), - }); - for (vals, blocks) |case_val, case_block| try writer.print(" {%}, {%}\n", .{ - case_val.fmt(self), - case_block.toInst(&function).fmt(function_index, self), - }); - try writer.writeAll(" ]\n"); - }, - .unimplemented => |tag| { - const ty: Type = @enumFromInt(instruction.data); - if (true) { + }, + .alloca, + .@"alloca inalloca", + => |tag| { + const extra = + function.extraData(Function.Instruction.Alloca, instruction.data); + try writer.print(" %{} = {s} {%}{,%}{,}{,}\n", .{ + instruction_index.name(&function).fmt(self), + @tagName(tag), + extra.type.fmt(self), + extra.len.fmt(function_index, self), + extra.info.alignment, + extra.info.addr_space, + }); + }, + .arg => unreachable, + .block => { + block_incoming_len = instruction.data; + const name = instruction_index.name(&function); + if (@intFromEnum(instruction_index) > params_len) + try writer.writeByte('\n'); + try writer.print("{}:\n", .{name.fmt(self)}); + }, + .br => |tag| { + const target: Function.Block.Index = @enumFromInt(instruction.data); + try writer.print(" {s} {%}\n", .{ + @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", .{ + extra.cond.fmt(function_index, self), + extra.then.toInst(&function).fmt(function_index, self), + extra.@"else".toInst(&function).fmt(function_index, self), + }); + }, + .call, + .@"call fast", + .@"musttail call", + .@"musttail call fast", + .@"notail call", + .@"notail call fast", + .@"tail call", + .@"tail call fast", + => |tag| { + var extra = + function.extraDataTrail(Function.Instruction.Call, instruction.data); + const args = extra.trail.next(extra.data.args_len, Value, &function); try writer.writeAll(" "); - switch (ty) { - .none, .void => {}, + const ret_ty = extra.data.ty.functionReturn(self); + switch (ret_ty) { + .void => {}, else => try writer.print("%{} = ", .{ instruction_index.name(&function).fmt(self), }), + .none => unreachable, + } + try writer.print("{s}{}{}{} {%} {}(", .{ + @tagName(tag), + extra.data.info.call_conv, + extra.data.attributes.ret(self).fmt(self), + extra.data.callee.typeOf(function_index, self).pointerAddrSpace(self), + switch (extra.data.ty.functionKind(self)) { + .normal => ret_ty, + .vararg => extra.data.ty, + }.fmt(self), + extra.data.callee.fmt(function_index, self), + }); + for (0.., args) |arg_index, arg| { + if (arg_index > 0) try writer.writeAll(", "); + 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 writer.print("{s} {%}\n", .{ @tagName(tag), ty.fmt(self) }); - } else switch (ty) { - .none, .void => {}, - else => try writer.print(" %{} = load {%}, ptr undef\n", .{ + try writer.writeByte(')'); + const call_function_attributes = extra.data.attributes.func(self); + if (call_function_attributes != .none) try writer.print(" #{d}", .{ + (try attribute_groups.getOrPutValue( + self.gpa, + call_function_attributes, + {}, + )).index, + }); + try writer.writeByte('\n'); + }, + .extractelement => |tag| { + const extra = function.extraData( + Function.Instruction.ExtractElement, + instruction.data, + ); + try writer.print(" %{} = {s} {%}, {%}\n", .{ + instruction_index.name(&function).fmt(self), + @tagName(tag), + extra.val.fmt(function_index, self), + extra.index.fmt(function_index, self), + }); + }, + .extractvalue => |tag| { + var extra = function.extraDataTrail( + Function.Instruction.ExtractValue, + instruction.data, + ); + const indices = extra.trail.next(extra.data.indices_len, u32, &function); + try writer.print(" %{} = {s} {%}", .{ + instruction_index.name(&function).fmt(self), + @tagName(tag), + 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); + try writer.print(" {s}{}{}", .{ @tagName(tag), info.scope, info.ordering }); + }, + .fneg, + .@"fneg fast", + .ret, + => |tag| { + const val: Value = @enumFromInt(instruction.data); + try writer.print(" {s} {%}\n", .{ + @tagName(tag), + val.fmt(function_index, self), + }); + }, + .getelementptr, + .@"getelementptr inbounds", + => |tag| { + var extra = function.extraDataTrail( + Function.Instruction.GetElementPtr, + instruction.data, + ); + const indices = extra.trail.next(extra.data.indices_len, Value, &function); + try writer.print(" %{} = {s} {%}, {%}", .{ + instruction_index.name(&function).fmt(self), + @tagName(tag), + extra.data.type.fmt(self), + extra.data.base.fmt(function_index, self), + }); + 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", .{ + instruction_index.name(&function).fmt(self), + @tagName(tag), + extra.val.fmt(function_index, self), + extra.elem.fmt(function_index, self), + extra.index.fmt(function_index, self), + }); + }, + .insertvalue => |tag| { + var extra = function.extraDataTrail( + Function.Instruction.InsertValue, + instruction.data, + ); + const indices = extra.trail.next(extra.data.indices_len, u32, &function); + try writer.print(" %{} = {s} {%}, {%}", .{ + instruction_index.name(&function).fmt(self), + @tagName(tag), + extra.data.val.fmt(function_index, self), + extra.data.elem.fmt(function_index, self), + }); + for (indices) |index| try writer.print(", {d}", .{index}); + try writer.writeByte('\n'); + }, + .@"llvm.maxnum.", + .@"llvm.minnum.", + .@"llvm.sadd.sat.", + .@"llvm.smax.", + .@"llvm.smin.", + .@"llvm.smul.fix.sat.", + .@"llvm.sshl.sat.", + .@"llvm.ssub.sat.", + .@"llvm.uadd.sat.", + .@"llvm.umax.", + .@"llvm.umin.", + .@"llvm.umul.fix.sat.", + .@"llvm.ushl.sat.", + .@"llvm.usub.sat.", + => |tag| { + const extra = + function.extraData(Function.Instruction.Binary, instruction.data); + const ty = instruction_index.typeOf(function_index, self); + try writer.print(" %{} = call {%} @{s}{m}({%}, {%}{s})\n", .{ instruction_index.name(&function).fmt(self), ty.fmt(self), - }), - } - }, - .va_arg => |tag| { - const extra = function.extraData(Function.Instruction.VaArg, instruction.data); - try writer.print(" %{} = {s} {%}, {%}\n", .{ - instruction_index.name(&function).fmt(self), - @tagName(tag), - extra.list.fmt(function_index, self), - extra.type.fmt(self), - }); - }, + @tagName(tag), + ty.fmt(self), + extra.lhs.fmt(function_index, self), + extra.rhs.fmt(function_index, self), + switch (tag) { + .@"llvm.smul.fix.sat.", + .@"llvm.umul.fix.sat.", + => ", i32 0", + else => "", + }, + }); + }, + .load, + .@"load atomic", + .@"load atomic volatile", + .@"load volatile", + => |tag| { + const extra = + function.extraData(Function.Instruction.Load, instruction.data); + try writer.print(" %{} = {s} {%}, {%}{}{}{,}\n", .{ + instruction_index.name(&function).fmt(self), + @tagName(tag), + extra.type.fmt(self), + extra.ptr.fmt(function_index, self), + extra.info.scope, + extra.info.ordering, + extra.info.alignment, + }); + }, + .phi, + .@"phi fast", + => |tag| { + var extra = + function.extraDataTrail(Function.Instruction.Phi, instruction.data); + const vals = extra.trail.next(block_incoming_len, Value, &function); + const blocks = + extra.trail.next(block_incoming_len, Function.Block.Index, &function); + try writer.print(" %{} = {s} {%} ", .{ + instruction_index.name(&function).fmt(self), + @tagName(tag), + vals[0].typeOf(function_index, self).fmt(self), + }); + for (0.., vals, blocks) |incoming_index, incoming_val, incoming_block| { + if (incoming_index > 0) try writer.writeAll(", "); + try writer.print("[ {}, {} ]", .{ + incoming_val.fmt(function_index, self), + incoming_block.toInst(&function).fmt(function_index, self), + }); + } + try writer.writeByte('\n'); + }, + .@"ret void", + .@"unreachable", + => |tag| try writer.print(" {s}\n", .{@tagName(tag)}), + .select, + .@"select fast", + => |tag| { + const extra = + function.extraData(Function.Instruction.Select, instruction.data); + try writer.print(" %{} = {s} {%}, {%}, {%}\n", .{ + instruction_index.name(&function).fmt(self), + @tagName(tag), + extra.cond.fmt(function_index, self), + extra.lhs.fmt(function_index, self), + extra.rhs.fmt(function_index, self), + }); + }, + .shufflevector => |tag| { + const extra = function.extraData( + Function.Instruction.ShuffleVector, + instruction.data, + ); + try writer.print(" %{} = {s} {%}, {%}, {%}\n", .{ + instruction_index.name(&function).fmt(self), + @tagName(tag), + extra.lhs.fmt(function_index, self), + extra.rhs.fmt(function_index, self), + extra.mask.fmt(function_index, self), + }); + }, + .store, + .@"store atomic", + .@"store atomic volatile", + .@"store volatile", + => |tag| { + const extra = + function.extraData(Function.Instruction.Store, instruction.data); + try writer.print(" {s} {%}, {%}{}{}{,}\n", .{ + @tagName(tag), + extra.val.fmt(function_index, self), + extra.ptr.fmt(function_index, self), + extra.info.scope, + extra.info.ordering, + extra.info.alignment, + }); + }, + .@"switch" => |tag| { + var extra = + function.extraDataTrail(Function.Instruction.Switch, instruction.data); + const vals = extra.trail.next(extra.data.cases_len, Constant, &function); + const blocks = + extra.trail.next(extra.data.cases_len, Function.Block.Index, &function); + try writer.print(" {s} {%}, {%} [\n", .{ + @tagName(tag), + extra.data.val.fmt(function_index, self), + extra.data.default.toInst(&function).fmt(function_index, self), + }); + for (vals, blocks) |case_val, case_block| try writer.print( + " {%}, {%}\n", + .{ + case_val.fmt(self), + case_block.toInst(&function).fmt(function_index, self), + }, + ); + try writer.writeAll(" ]\n"); + }, + .unimplemented => |tag| { + const ty: Type = @enumFromInt(instruction.data); + if (true) { + try writer.writeAll(" "); + switch (ty) { + .none, .void => {}, + else => try writer.print("%{} = ", .{ + instruction_index.name(&function).fmt(self), + }), + } + try writer.print("{s} {%}\n", .{ @tagName(tag), ty.fmt(self) }); + } else switch (ty) { + .none, .void => {}, + else => try writer.print(" %{} = load {%}, ptr undef\n", .{ + instruction_index.name(&function).fmt(self), + ty.fmt(self), + }), + } + }, + .va_arg => |tag| { + const extra = + function.extraData(Function.Instruction.VaArg, instruction.data); + try writer.print(" %{} = {s} {%}, {%}\n", .{ + instruction_index.name(&function).fmt(self), + @tagName(tag), + extra.list.fmt(function_index, self), + extra.type.fmt(self), + }); + }, + } } + try writer.writeByte('}'); } - try writer.writeByte('}'); + try writer.writeByte('\n'); } - try writer.writeAll("\n\n"); + need_newline = true; } - for (0.., attribute_groups.keys()) |attribute_group_index, attribute_group| - try writer.print( - \\attributes #{d} = {{{#"} }} - \\ - , .{ attribute_group_index, attribute_group.fmt(self) }); + if (attribute_groups.count() > 0) { + if (need_newline) try writer.writeByte('\n') else need_newline = true; + for (0.., attribute_groups.keys()) |attribute_group_index, attribute_group| + try writer.print( + \\attributes #{d} = {{{#"} }} + \\ + , .{ attribute_group_index, attribute_group.fmt(self) }); + need_newline = true; + } } pub inline fn useLibLlvm(self: *const Builder) bool { @@ -7736,7 +7807,7 @@ pub inline fn useLibLlvm(self: *const Builder) bool { const NoExtra = struct {}; fn isValidIdentifier(id: []const u8) bool { - for (id, 0..) |character, index| switch (character) { + for (id, 0..) |byte, index| switch (byte) { '$', '-', '.', 'A'...'Z', '_', 'a'...'z' => {}, '0'...'9' => if (index == 0) return false, else => return false, @@ -7744,6 +7815,25 @@ fn isValidIdentifier(id: []const u8) bool { return true; } +const QuoteBehavior = enum { always_quote, quote_unless_valid_identifier }; +fn printEscapedString( + slice: []const u8, + quotes: QuoteBehavior, + writer: anytype, +) @TypeOf(writer).Error!void { + const need_quotes = switch (quotes) { + .always_quote => true, + .quote_unless_valid_identifier => !isValidIdentifier(slice), + }; + if (need_quotes) try writer.writeByte('"'); + for (slice) |byte| switch (byte) { + '\\' => try writer.writeAll("\\\\"), + ' '...'"' - 1, '"' + 1...'\\' - 1, '\\' + 1...'~' => try writer.writeByte(byte), + else => try writer.print("\\{X:0>2}", .{byte}), + }; + if (need_quotes) try writer.writeByte('"'); +} + 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); @@ -9318,6 +9408,8 @@ fn asmConstAssumeCapacity( assembly: String, constraints: String, ) Constant { + assert(ty.functionKind(self) == .normal); + const Key = struct { tag: Constant.Tag, extra: Constant.Asm }; const Adapter = struct { builder: *const Builder, diff --git a/src/codegen/llvm/bindings.zig b/src/codegen/llvm/bindings.zig index 7f0afee2af82..aee9cea384c3 100644 --- a/src/codegen/llvm/bindings.zig +++ b/src/codegen/llvm/bindings.zig @@ -540,7 +540,7 @@ pub const Module = opaque { pub const createDIBuilder = ZigLLVMCreateDIBuilder; extern fn ZigLLVMCreateDIBuilder(module: *Module, allow_unresolved: bool) *DIBuilder; - pub const setModuleInlineAsm2 = LLVMSetModuleInlineAsm2; + pub const setModuleInlineAsm = LLVMSetModuleInlineAsm2; extern fn LLVMSetModuleInlineAsm2(M: *Module, Asm: [*]const u8, Len: usize) void; pub const printModuleToFile = LLVMPrintModuleToFile;