llvm: implement c abi for loongarch

Closes #35798
This commit is contained in:
Jacob Young committed 2026-07-27 14:44:41 -04:00
1 parent 092f019be9
commit 703baa4cd6
3 files changed
+220

No files matched your search

+1
View File
@@ -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
+86
View File
@@ -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");
+133
View File
@@ -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;
}
};