From 703baa4cd662e55ba39ea14ae2f30209e7ff023b Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Wed, 1 Jul 2026 12:01:07 -0400 Subject: [PATCH] llvm: implement c abi for loongarch Closes #35798 --- CMakeLists.txt | 1 + src/codegen/llvm/FuncGen.zig | 86 ++++++++++++++++++++++ src/codegen/loongarch/abi.zig | 133 ++++++++++++++++++++++++++++++++++ 3 files changed, 220 insertions(+) create mode 100644 src/codegen/loongarch/abi.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index ad4d8bea72ee199d041ef28559b536de2f20f7e3..14df4e9b568d8fb6c5b924f6cd4e3c9778dc5e21 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -358,6 +358,7 @@ set(ZIG_STAGE2_SOURCES src/codegen/c/type/render_defs.zig src/codegen/llvm.zig src/codegen/llvm/bindings.zig + src/codegen/loongarch/abi.zig src/codegen/s390x/abi.zig src/crash_report.zig src/dev.zig diff --git a/src/codegen/llvm/FuncGen.zig b/src/codegen/llvm/FuncGen.zig index 30a188749337f24690130a4a36e9fbe7a4cb578b..84a7af8362e91e54003cdf82378ce1037b2f617f 100644 --- a/src/codegen/llvm/FuncGen.zig +++ b/src/codegen/llvm/FuncGen.zig @@ -7016,6 +7016,71 @@ const ParamTypeIterator = struct { .i64_array => |size| return .{ .i64_array = size }, } }, + .loongarch32_ilp32, .loongarch64_lp64 => switch (loongarch_c_abi.classifyType(ty, zcu)) { + .ignored => { + it.zig_index += 1; + return .no_bits; + }, + .gar, .far => { + it.zig_index += 1; + it.llvm_index += 1; + return .byval; + }, + .member => |member_ty| { + it.types_buffer[0..1].* = .{ + try it.object.lowerType(member_ty, .as_value), + }; + it.offsets_buffer[0..2].* = .{ 0, member_ty.abiSize(zcu) }; + it.types_len = 1; + it.zig_index += 1; + it.llvm_index += 1; + return .multiple_llvm_types; + }, + .member_pair => |member_tys| { + it.types_buffer[0..2].* = .{ + try it.object.lowerType(member_tys[0], .as_value), + try it.object.lowerType(member_tys[1], .as_value), + }; + const first_size = member_tys[0].abiSize(zcu); + const second_size = member_tys[0].abiSize(zcu); + it.offsets_buffer[0..3].* = .{ 0, first_size, first_size + second_size }; + it.types_len = 2; + it.zig_index += 1; + it.llvm_index += 2; + return .multiple_llvm_types; + }, + .memory_gar => { + switch (it.cc) { + else => unreachable, + .loongarch32_ilp32 => { + it.types_buffer[0..1].* = .{.i32}; + it.offsets_buffer[0..2].* = .{ 0, 4 }; + }, + .loongarch64_lp64 => { + it.types_buffer[0..1].* = .{.i64}; + it.offsets_buffer[0..2].* = .{ 0, 8 }; + }, + } + it.types_len = 1; + it.zig_index += 1; + it.llvm_index += 1; + return .multiple_llvm_types; + }, + .memory_gar_pair => { + it.zig_index += 1; + it.llvm_index += 1; + return switch (it.cc) { + else => unreachable, + .loongarch32_ilp32 => .{ .i32_array = 2 }, + .loongarch64_lp64 => .{ .i64_array = 2 }, + }; + }, + .address => { + it.zig_index += 1; + it.llvm_index += 1; + return .byref; + }, + }, .mips_o32 => { it.zig_index += 1; it.llvm_index += 1; @@ -7330,6 +7395,26 @@ pub fn fnReturnStrat(o: *Object, cc: std.lang.CallingConvention, ret_ty: Type) A .i32_array => |len| if (len == 1) .{ .mem_cast = .i32 } else .sret, .byval => .forceByVal(o, ret_ty), }, + .loongarch32_ilp32, .loongarch64_lp64 => switch (loongarch_c_abi.classifyType(ret_ty, zcu)) { + .ignored => .void, + .gar, .far => .by_val, + .member => |member_ty| .{ .mem_cast = try o.lowerType(member_ty, .as_value) }, + .member_pair => |member_tys| .{ .mem_cast = try o.builder.structType(.normal, &.{ + try o.lowerType(member_tys[0], .as_value), + try o.lowerType(member_tys[1], .as_value), + }) }, + .memory_gar => .{ .mem_cast = switch (cc) { + else => unreachable, + .loongarch32_ilp32 => .i32, + .loongarch64_lp64 => .i64, + } }, + .memory_gar_pair => .{ .mem_cast = try o.builder.arrayType(2, switch (cc) { + else => unreachable, + .loongarch32_ilp32 => .i32, + .loongarch64_lp64 => .i64, + }) }, + .address => .sret, + }, .mips_o32 => switch (mips_c_abi.classifyType(ret_ty, zcu, .ret)) { .memory, .i32_array => .sret, .byval => .forceByVal(o, ret_ty), @@ -8124,6 +8209,7 @@ const math = std.math; const aarch64_c_abi = @import("../aarch64/abi.zig"); const arm_c_abi = @import("../arm/abi.zig"); +const loongarch_c_abi = @import("../loongarch/abi.zig"); const mips_c_abi = @import("../mips/abi.zig"); const riscv_c_abi = @import("../riscv64/abi.zig"); const s390x_c_abi = @import("../s390x/abi.zig"); diff --git a/src/codegen/loongarch/abi.zig b/src/codegen/loongarch/abi.zig new file mode 100644 index 0000000000000000000000000000000000000000..09e42a7cb96731dbf0ee44b37ad74f0548a08c93 --- /dev/null +++ b/src/codegen/loongarch/abi.zig @@ -0,0 +1,133 @@ +const std = @import("std"); +const InternPool = @import("../../InternPool.zig"); +const Type = @import("../../Type.zig"); +const Zcu = @import("../../Zcu.zig"); + +pub const Class = union(enum) { + ignored, + gar, + far, + member: Type, + member_pair: [2]Type, + memory_gar, + memory_gar_pair, + address, + + fn combineMember(container_class: Class, member_class: Class, member_ty: Type) Class { + const second_member_ty = switch (member_class) { + .ignored => return container_class, + .gar, .far => member_ty, + .member => |second_member_ty| second_member_ty, + .member_pair, .memory_gar, .memory_gar_pair, .address => return .address, + }; + return switch (container_class) { + .ignored => .{ .member = second_member_ty }, + .gar, .far, .memory_gar, .memory_gar_pair => unreachable, + .member => |first_member_ty| .{ .member_pair = .{ first_member_ty, second_member_ty } }, + .member_pair, .address => .address, + }; + } +}; + +pub fn classifyType(ty: Type, zcu: *Zcu) Class { + return Classifier.init(zcu).classifyType(ty); +} + +const Classifier = struct { + zcu: *Zcu, + target: *const std.Target, + grlen: u8, + frlen: u8, + + fn init(zcu: *Zcu) Classifier { + const target = zcu.getTarget(); + return .{ + .zcu = zcu, + .target = target, + .grlen = switch (target.cpu.arch) { + else => unreachable, + .loongarch32 => 32, + .loongarch64 => 64, + }, + .frlen = if (target.cpu.has(.loongarch, .d)) + 64 + else if (target.cpu.has(.loongarch, .f)) + 32 + else + 0, + }; + } + + fn classifyType(c: Classifier, ty: Type) Class { + switch (ty.zigTypeTag(c.zcu)) { + .type, + .comptime_float, + .comptime_int, + .undefined, + .null, + .error_union, + .error_set, + .@"fn", + .@"opaque", + .frame, + .@"anyframe", + .enum_literal, + .spirv, + => unreachable, + .void, .noreturn => return .ignored, + .bool => return .gar, + .int, .@"enum" => { + const bits = ty.intInfo(c.zcu).bits; + if (bits == 0) return .ignored; + if (bits <= c.grlen) return .gar; + if (bits <= 2 * c.grlen) return .memory_gar_pair; + return .address; + }, + .float => { + const bits = ty.floatBits(c.target); + if (bits <= c.frlen) return .far; + if (bits <= c.grlen) return .gar; + if (bits <= 2 * c.grlen) return .memory_gar_pair; + return .address; + }, + .pointer, .optional => return .gar, + .array => { + var class: Class = .ignored; + const elem_ty = ty.childType(c.zcu); + const elem_class = c.classifyType(elem_ty); + for (0..std.math.lossyCast(usize, ty.arrayLen(c.zcu))) |_| { + class = class.combineMember(elem_class, elem_ty); + if (class == .address) break; + } + if (class != .address) return class; + }, + .@"struct" => switch (ty.containerLayout(c.zcu)) { + .auto => unreachable, + .@"extern" => { + var class: Class = .ignored; + var field_it: InternPool.LoadedStructType.RuntimeOrderIterator = if (c.zcu.typeToStruct(ty)) |loaded_struct| + loaded_struct.iterateRuntimeOrder(&c.zcu.intern_pool) + else + .{ .runtime_order = null, .fields_len = ty.structFieldCount(c.zcu), .next_index = 0 }; + while (field_it.next()) |field_index| { + const field_ty = ty.fieldType(field_index, c.zcu); + class = class.combineMember(c.classifyType(field_ty), field_ty); + if (class == .address) break; + } + if (class != .address) return class; + }, + .@"packed" => return c.classifyType(ty.backingIntType(c.zcu)), + }, + .@"union" => switch (ty.containerLayout(c.zcu)) { + .auto => unreachable, + .@"extern" => {}, + .@"packed" => return c.classifyType(ty.backingIntType(c.zcu)), + }, + .vector => {}, + } + const size = ty.abiSize(c.zcu); + if (size <= @divExact(c.grlen, 8)) return .memory_gar; + if (size <= @divExact(2 * c.grlen, 8)) return .memory_gar_pair; + return .address; + } +}; -- 2.54.0