authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-09-03 15:47:58+03:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-09-04 18:46:56+03:00
log9ce841a0f04be0aed70d69abad7e4aacca754e3c
tree28758fc2b259402adfaf1c2010a4f44008345de9
parentae3a5ff7f962cba1eb57423e37ffaf94dc394152

stage2 llvm: implement aarch64 C ABI

... at least enough to pass all the current tests.

4 files changed, 158 insertions(+), 1 deletions(-)

src/arch/aarch64/abi.zig+63
......@@ -3,6 +3,69 @@ const builtin = @import("builtin");
33const bits = @import("bits.zig");
44const Register = bits.Register;
55const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager;
6const Type = @import("../../type.zig").Type;
7
8pub const Class = enum { memory, integer, none, float_array };
9
10pub fn classifyType(ty: Type, target: std.Target) [2]Class {
11 if (!ty.hasRuntimeBitsIgnoreComptime()) return .{ .none, .none };
12 switch (ty.zigTypeTag()) {
13 .Struct => {
14 if (ty.containerLayout() == .Packed) return .{ .integer, .none };
15
16 if (ty.structFieldCount() <= 4) {
17 const fields = ty.structFields();
18 var float_size: ?u64 = null;
19 for (fields.values()) |field| {
20 if (field.ty.zigTypeTag() != .Float) break;
21 const field_size = field.ty.bitSize(target);
22 const prev_size = float_size orelse {
23 float_size = field_size;
24 continue;
25 };
26 if (field_size != prev_size) break;
27 } else {
28 return .{ .float_array, .none };
29 }
30 }
31 const bit_size = ty.bitSize(target);
32 if (bit_size > 128) return .{ .memory, .none };
33 if (bit_size > 64) return .{ .integer, .integer };
34 return .{ .integer, .none };
35 },
36 .Union => {
37 const bit_size = ty.bitSize(target);
38 if (bit_size > 128) return .{ .memory, .none };
39 if (bit_size > 64) return .{ .integer, .integer };
40 return .{ .integer, .none };
41 },
42 .Int, .Enum, .ErrorSet, .Vector, .Float, .Bool => return .{ .integer, .none },
43 .Array => return .{ .memory, .none },
44 .Optional => {
45 std.debug.assert(ty.isPtrLikeOptional());
46 return .{ .integer, .none };
47 },
48 .Pointer => {
49 std.debug.assert(!ty.isSlice());
50 return .{ .integer, .none };
51 },
52 .ErrorUnion,
53 .Frame,
54 .AnyFrame,
55 .NoReturn,
56 .Void,
57 .Type,
58 .ComptimeFloat,
59 .ComptimeInt,
60 .Undefined,
61 .Null,
62 .BoundFn,
63 .Fn,
64 .Opaque,
65 .EnumLiteral,
66 => unreachable,
67 }
68}
669
770const callee_preserved_regs_impl = if (builtin.os.tag.isDarwin()) struct {
871 pub const callee_preserved_regs = [_]Register{
src/codegen/llvm.zig+91
......@@ -23,6 +23,7 @@ const LazySrcLoc = Module.LazySrcLoc;
2323const CType = @import("../type.zig").CType;
2424const x86_64_abi = @import("../arch/x86_64/abi.zig");
2525const wasm_c_abi = @import("../arch/wasm/abi.zig");
26const aarch64_c_abi = @import("../arch/aarch64/abi.zig");
2627
2728const Error = error{ OutOfMemory, CodegenFail };
2829
......@@ -1086,6 +1087,26 @@ pub const Object = struct {
10861087 try args.ensureUnusedCapacity(1);
10871088 args.appendAssumeCapacity(casted);
10881089 },
1090 .float_array => {
1091 const param_ty = fn_info.param_types[it.zig_index - 1];
1092 const param_llvm_ty = try dg.lowerType(param_ty);
1093 const param = llvm_func.getParam(llvm_arg_i);
1094 llvm_arg_i += 1;
1095
1096 const alignment = param_ty.abiAlignment(target);
1097 const arg_ptr = buildAllocaInner(builder, llvm_func, false, param_llvm_ty);
1098 arg_ptr.setAlignment(alignment);
1099 const casted_ptr = builder.buildBitCast(arg_ptr, param.typeOf().pointerType(0), "");
1100 _ = builder.buildStore(param, casted_ptr);
1101
1102 if (isByRef(param_ty)) {
1103 try args.append(arg_ptr);
1104 } else {
1105 const load_inst = builder.buildLoad(arg_ptr, "");
1106 load_inst.setAlignment(alignment);
1107 try args.append(load_inst);
1108 }
1109 },
10891110 };
10901111 }
10911112
......@@ -3070,6 +3091,13 @@ pub const DeclGen = struct {
30703091 .as_u16 => {
30713092 try llvm_params.append(dg.context.intType(16));
30723093 },
3094 .float_array => {
3095 const param_ty = fn_info.param_types[it.zig_index - 1];
3096 const float_ty = try dg.lowerType(param_ty.structFieldType(0));
3097 const field_count = @intCast(c_uint, param_ty.structFieldCount());
3098 const arr_ty = float_ty.arrayType(field_count);
3099 try llvm_params.append(arr_ty);
3100 },
30733101 };
30743102
30753103 return llvm.functionType(
......@@ -4621,6 +4649,27 @@ pub const FuncGen = struct {
46214649 const casted = self.builder.buildBitCast(llvm_arg, self.dg.context.intType(16), "");
46224650 try llvm_args.append(casted);
46234651 },
4652 .float_array => {
4653 const arg = args[it.zig_index - 1];
4654 const arg_ty = self.air.typeOf(arg);
4655 var llvm_arg = try self.resolveInst(arg);
4656 if (!isByRef(arg_ty)) {
4657 const p = self.buildAlloca(llvm_arg.typeOf());
4658 const store_inst = self.builder.buildStore(llvm_arg, p);
4659 store_inst.setAlignment(arg_ty.abiAlignment(target));
4660 llvm_arg = store_inst;
4661 }
4662
4663 const float_ty = try self.dg.lowerType(arg_ty.structFieldType(0));
4664 const field_count = @intCast(u32, arg_ty.structFieldCount());
4665 const arr_ty = float_ty.arrayType(field_count);
4666
4667 const casted = self.builder.buildBitCast(llvm_arg, arr_ty.pointerType(0), "");
4668 const alignment = arg_ty.abiAlignment(target);
4669 const load_inst = self.builder.buildLoad(casted, "");
4670 load_inst.setAlignment(alignment);
4671 try llvm_args.append(load_inst);
4672 },
46244673 };
46254674
46264675 const call = self.builder.buildCall(
......@@ -9644,6 +9693,7 @@ fn firstParamSRet(fn_info: Type.Payload.Function.Data, target: std.Target) bool
96449693 else => return x86_64_abi.classifySystemV(fn_info.return_type, target)[0] == .memory,
96459694 },
96469695 .wasm32 => return wasm_c_abi.classifyType(fn_info.return_type, target)[0] == .indirect,
9696 .aarch64, .aarch64_be => return aarch64_c_abi.classifyType(fn_info.return_type, target)[0] == .memory,
96479697 else => return false, // TODO investigate C ABI for other architectures
96489698 },
96499699 else => return false,
......@@ -9753,6 +9803,24 @@ fn lowerFnRetTy(dg: *DeclGen, fn_info: Type.Payload.Function.Data) !*const llvm.
97539803 const abi_size = scalar_type.abiSize(target);
97549804 return dg.context.intType(@intCast(c_uint, abi_size * 8));
97559805 },
9806 .aarch64, .aarch64_be => {
9807 if (is_scalar) {
9808 return dg.lowerType(fn_info.return_type);
9809 }
9810 const classes = aarch64_c_abi.classifyType(fn_info.return_type, target);
9811 if (classes[0] == .memory or classes[0] == .none) {
9812 return dg.context.voidType();
9813 }
9814 if (classes[0] == .float_array) {
9815 return dg.lowerType(fn_info.return_type);
9816 }
9817 if (classes[1] == .none) {
9818 const bit_size = fn_info.return_type.bitSize(target);
9819 return dg.context.intType(@intCast(c_uint, bit_size));
9820 }
9821
9822 return dg.context.intType(64).arrayType(2);
9823 },
97569824 // TODO investigate C ABI for other architectures
97579825 else => return dg.lowerType(fn_info.return_type),
97589826 }
......@@ -9780,6 +9848,7 @@ const ParamTypeIterator = struct {
97809848 multiple_llvm_float,
97819849 slice,
97829850 as_u16,
9851 float_array,
97839852 };
97849853
97859854 pub fn next(it: *ParamTypeIterator) ?Lowering {
......@@ -9945,6 +10014,28 @@ const ParamTypeIterator = struct {
994510014 }
994610015 return .abi_sized_int;
994710016 },
10017 .aarch64, .aarch64_be => {
10018 it.zig_index += 1;
10019 it.llvm_index += 1;
10020 if (is_scalar) {
10021 return .byval;
10022 }
10023 const classes = aarch64_c_abi.classifyType(ty, it.target);
10024 if (classes[0] == .memory) {
10025 return .byref;
10026 }
10027 if (classes[0] == .float_array) {
10028 return .float_array;
10029 }
10030 if (classes[1] == .none) {
10031 it.llvm_types_len = 1;
10032 } else {
10033 it.llvm_types_len = 2;
10034 }
10035 it.llvm_types_buffer[0] = 64;
10036 it.llvm_types_buffer[1] = 64;
10037 return .multiple_llvm_ints;
10038 },
994810039 // TODO investigate C ABI for other architectures
994910040 else => {
995010041 it.zig_index += 1;
test/c_abi/main.zig+1-1
......@@ -110,7 +110,7 @@ test "C ABI floats" {
110110}
111111
112112test "C ABI long double" {
113 if (!builtin.cpu.arch.isWasm()) return error.SkipZigTest;
113 if (!builtin.cpu.arch.isWasm() and !builtin.cpu.arch.isAARCH64()) return error.SkipZigTest;
114114 c_long_double(12.34);
115115}
116116
test/standalone.zig+3
......@@ -50,6 +50,9 @@ pub fn addCases(cases: *tests.StandaloneContext) void {
5050 cases.addBuildFile("test/c_abi/build.zig", .{});
5151 }
5252 }
53 if (builtin.cpu.arch.isAARCH64() and builtin.zig_backend == .stage2_llvm) {
54 cases.addBuildFile("test/c_abi/build.zig", .{});
55 }
5356 // C ABI tests only pass for the Wasm target when using stage2
5457 cases.addBuildFile("test/c_abi/build_wasm.zig", .{
5558 .requires_stage2 = true,