authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2026-07-01 12:01:07-04:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2026-07-27 14:44:41-04:00
log703baa4cd662e55ba39ea14ae2f30209e7ff023b
tree91516ec30a9b798f3279d2538d9d498895deef1f
parent092f019be98b9cba5ecdc9a2b1a14ba107479a41

llvm: implement c abi for loongarch

Closes #35798

3 files changed, 220 insertions(+), 0 deletions(-)

CMakeLists.txt+1
...@@ -358,6 +358,7 @@ set(ZIG_STAGE2_SOURCES...@@ -358,6 +358,7 @@ set(ZIG_STAGE2_SOURCES
358 src/codegen/c/type/render_defs.zig358 src/codegen/c/type/render_defs.zig
359 src/codegen/llvm.zig359 src/codegen/llvm.zig
360 src/codegen/llvm/bindings.zig360 src/codegen/llvm/bindings.zig
361 src/codegen/loongarch/abi.zig
361 src/codegen/s390x/abi.zig362 src/codegen/s390x/abi.zig
362 src/crash_report.zig363 src/crash_report.zig
363 src/dev.zig364 src/dev.zig
src/codegen/llvm/FuncGen.zig+86
...@@ -7016,6 +7016,71 @@ const ParamTypeIterator = struct {...@@ -7016,6 +7016,71 @@ const ParamTypeIterator = struct {
7016 .i64_array => |size| return .{ .i64_array = size },7016 .i64_array => |size| return .{ .i64_array = size },
7017 }7017 }
7018 },7018 },
7019 .loongarch32_ilp32, .loongarch64_lp64 => switch (loongarch_c_abi.classifyType(ty, zcu)) {
7020 .ignored => {
7021 it.zig_index += 1;
7022 return .no_bits;
7023 },
7024 .gar, .far => {
7025 it.zig_index += 1;
7026 it.llvm_index += 1;
7027 return .byval;
7028 },
7029 .member => |member_ty| {
7030 it.types_buffer[0..1].* = .{
7031 try it.object.lowerType(member_ty, .as_value),
7032 };
7033 it.offsets_buffer[0..2].* = .{ 0, member_ty.abiSize(zcu) };
7034 it.types_len = 1;
7035 it.zig_index += 1;
7036 it.llvm_index += 1;
7037 return .multiple_llvm_types;
7038 },
7039 .member_pair => |member_tys| {
7040 it.types_buffer[0..2].* = .{
7041 try it.object.lowerType(member_tys[0], .as_value),
7042 try it.object.lowerType(member_tys[1], .as_value),
7043 };
7044 const first_size = member_tys[0].abiSize(zcu);
7045 const second_size = member_tys[0].abiSize(zcu);
7046 it.offsets_buffer[0..3].* = .{ 0, first_size, first_size + second_size };
7047 it.types_len = 2;
7048 it.zig_index += 1;
7049 it.llvm_index += 2;
7050 return .multiple_llvm_types;
7051 },
7052 .memory_gar => {
7053 switch (it.cc) {
7054 else => unreachable,
7055 .loongarch32_ilp32 => {
7056 it.types_buffer[0..1].* = .{.i32};
7057 it.offsets_buffer[0..2].* = .{ 0, 4 };
7058 },
7059 .loongarch64_lp64 => {
7060 it.types_buffer[0..1].* = .{.i64};
7061 it.offsets_buffer[0..2].* = .{ 0, 8 };
7062 },
7063 }
7064 it.types_len = 1;
7065 it.zig_index += 1;
7066 it.llvm_index += 1;
7067 return .multiple_llvm_types;
7068 },
7069 .memory_gar_pair => {
7070 it.zig_index += 1;
7071 it.llvm_index += 1;
7072 return switch (it.cc) {
7073 else => unreachable,
7074 .loongarch32_ilp32 => .{ .i32_array = 2 },
7075 .loongarch64_lp64 => .{ .i64_array = 2 },
7076 };
7077 },
7078 .address => {
7079 it.zig_index += 1;
7080 it.llvm_index += 1;
7081 return .byref;
7082 },
7083 },
7019 .mips_o32 => {7084 .mips_o32 => {
7020 it.zig_index += 1;7085 it.zig_index += 1;
7021 it.llvm_index += 1;7086 it.llvm_index += 1;
...@@ -7330,6 +7395,26 @@ pub fn fnReturnStrat(o: *Object, cc: std.lang.CallingConvention, ret_ty: Type) A...@@ -7330,6 +7395,26 @@ pub fn fnReturnStrat(o: *Object, cc: std.lang.CallingConvention, ret_ty: Type) A
7330 .i32_array => |len| if (len == 1) .{ .mem_cast = .i32 } else .sret,7395 .i32_array => |len| if (len == 1) .{ .mem_cast = .i32 } else .sret,
7331 .byval => .forceByVal(o, ret_ty),7396 .byval => .forceByVal(o, ret_ty),
7332 },7397 },
7398 .loongarch32_ilp32, .loongarch64_lp64 => switch (loongarch_c_abi.classifyType(ret_ty, zcu)) {
7399 .ignored => .void,
7400 .gar, .far => .by_val,
7401 .member => |member_ty| .{ .mem_cast = try o.lowerType(member_ty, .as_value) },
7402 .member_pair => |member_tys| .{ .mem_cast = try o.builder.structType(.normal, &.{
7403 try o.lowerType(member_tys[0], .as_value),
7404 try o.lowerType(member_tys[1], .as_value),
7405 }) },
7406 .memory_gar => .{ .mem_cast = switch (cc) {
7407 else => unreachable,
7408 .loongarch32_ilp32 => .i32,
7409 .loongarch64_lp64 => .i64,
7410 } },
7411 .memory_gar_pair => .{ .mem_cast = try o.builder.arrayType(2, switch (cc) {
7412 else => unreachable,
7413 .loongarch32_ilp32 => .i32,
7414 .loongarch64_lp64 => .i64,
7415 }) },
7416 .address => .sret,
7417 },
7333 .mips_o32 => switch (mips_c_abi.classifyType(ret_ty, zcu, .ret)) {7418 .mips_o32 => switch (mips_c_abi.classifyType(ret_ty, zcu, .ret)) {
7334 .memory, .i32_array => .sret,7419 .memory, .i32_array => .sret,
7335 .byval => .forceByVal(o, ret_ty),7420 .byval => .forceByVal(o, ret_ty),
...@@ -8124,6 +8209,7 @@ const math = std.math;...@@ -8124,6 +8209,7 @@ const math = std.math;
81248209
8125const aarch64_c_abi = @import("../aarch64/abi.zig");8210const aarch64_c_abi = @import("../aarch64/abi.zig");
8126const arm_c_abi = @import("../arm/abi.zig");8211const arm_c_abi = @import("../arm/abi.zig");
8212const loongarch_c_abi = @import("../loongarch/abi.zig");
8127const mips_c_abi = @import("../mips/abi.zig");8213const mips_c_abi = @import("../mips/abi.zig");
8128const riscv_c_abi = @import("../riscv64/abi.zig");8214const riscv_c_abi = @import("../riscv64/abi.zig");
8129const s390x_c_abi = @import("../s390x/abi.zig");8215const s390x_c_abi = @import("../s390x/abi.zig");
src/codegen/loongarch/abi.zig created+133
...@@ -0,0 +1,133 @@
1const std = @import("std");
2const InternPool = @import("../../InternPool.zig");
3const Type = @import("../../Type.zig");
4const Zcu = @import("../../Zcu.zig");
5
6pub const Class = union(enum) {
7 ignored,
8 gar,
9 far,
10 member: Type,
11 member_pair: [2]Type,
12 memory_gar,
13 memory_gar_pair,
14 address,
15
16 fn combineMember(container_class: Class, member_class: Class, member_ty: Type) Class {
17 const second_member_ty = switch (member_class) {
18 .ignored => return container_class,
19 .gar, .far => member_ty,
20 .member => |second_member_ty| second_member_ty,
21 .member_pair, .memory_gar, .memory_gar_pair, .address => return .address,
22 };
23 return switch (container_class) {
24 .ignored => .{ .member = second_member_ty },
25 .gar, .far, .memory_gar, .memory_gar_pair => unreachable,
26 .member => |first_member_ty| .{ .member_pair = .{ first_member_ty, second_member_ty } },
27 .member_pair, .address => .address,
28 };
29 }
30};
31
32pub fn classifyType(ty: Type, zcu: *Zcu) Class {
33 return Classifier.init(zcu).classifyType(ty);
34}
35
36const Classifier = struct {
37 zcu: *Zcu,
38 target: *const std.Target,
39 grlen: u8,
40 frlen: u8,
41
42 fn init(zcu: *Zcu) Classifier {
43 const target = zcu.getTarget();
44 return .{
45 .zcu = zcu,
46 .target = target,
47 .grlen = switch (target.cpu.arch) {
48 else => unreachable,
49 .loongarch32 => 32,
50 .loongarch64 => 64,
51 },
52 .frlen = if (target.cpu.has(.loongarch, .d))
53 64
54 else if (target.cpu.has(.loongarch, .f))
55 32
56 else
57 0,
58 };
59 }
60
61 fn classifyType(c: Classifier, ty: Type) Class {
62 switch (ty.zigTypeTag(c.zcu)) {
63 .type,
64 .comptime_float,
65 .comptime_int,
66 .undefined,
67 .null,
68 .error_union,
69 .error_set,
70 .@"fn",
71 .@"opaque",
72 .frame,
73 .@"anyframe",
74 .enum_literal,
75 .spirv,
76 => unreachable,
77 .void, .noreturn => return .ignored,
78 .bool => return .gar,
79 .int, .@"enum" => {
80 const bits = ty.intInfo(c.zcu).bits;
81 if (bits == 0) return .ignored;
82 if (bits <= c.grlen) return .gar;
83 if (bits <= 2 * c.grlen) return .memory_gar_pair;
84 return .address;
85 },
86 .float => {
87 const bits = ty.floatBits(c.target);
88 if (bits <= c.frlen) return .far;
89 if (bits <= c.grlen) return .gar;
90 if (bits <= 2 * c.grlen) return .memory_gar_pair;
91 return .address;
92 },
93 .pointer, .optional => return .gar,
94 .array => {
95 var class: Class = .ignored;
96 const elem_ty = ty.childType(c.zcu);
97 const elem_class = c.classifyType(elem_ty);
98 for (0..std.math.lossyCast(usize, ty.arrayLen(c.zcu))) |_| {
99 class = class.combineMember(elem_class, elem_ty);
100 if (class == .address) break;
101 }
102 if (class != .address) return class;
103 },
104 .@"struct" => switch (ty.containerLayout(c.zcu)) {
105 .auto => unreachable,
106 .@"extern" => {
107 var class: Class = .ignored;
108 var field_it: InternPool.LoadedStructType.RuntimeOrderIterator = if (c.zcu.typeToStruct(ty)) |loaded_struct|
109 loaded_struct.iterateRuntimeOrder(&c.zcu.intern_pool)
110 else
111 .{ .runtime_order = null, .fields_len = ty.structFieldCount(c.zcu), .next_index = 0 };
112 while (field_it.next()) |field_index| {
113 const field_ty = ty.fieldType(field_index, c.zcu);
114 class = class.combineMember(c.classifyType(field_ty), field_ty);
115 if (class == .address) break;
116 }
117 if (class != .address) return class;
118 },
119 .@"packed" => return c.classifyType(ty.backingIntType(c.zcu)),
120 },
121 .@"union" => switch (ty.containerLayout(c.zcu)) {
122 .auto => unreachable,
123 .@"extern" => {},
124 .@"packed" => return c.classifyType(ty.backingIntType(c.zcu)),
125 },
126 .vector => {},
127 }
128 const size = ty.abiSize(c.zcu);
129 if (size <= @divExact(c.grlen, 8)) return .memory_gar;
130 if (size <= @divExact(2 * c.grlen, 8)) return .memory_gar_pair;
131 return .address;
132 }
133};