authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-07-17 15:10:56+02:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-07-25 06:34:00+02:00
log200b2e4ee108d388e0db2120aee38f83d1c7abdb
tree49139a306103997d18ed83624b3fec2c3cb13468
parentbf28a47cf23a60539f7cc6cb87309d1a7fa01c18
signature Commit is signed but in an unrecognized format.

llvm: correctly lower c-abi for Wasm target

When lowering the return type for Wasm if the calling convention is `C`, it now correctly lower it according to what clang does as specified in: https://github.com/WebAssembly/tool-conventions/blob/main/BasicCABI.md This makes use of the same logic as the Wasm backend, ensuring the generated code does not diverge in function signatures. When passing arguments accross the C-ABI for the Wasm target, we want slightly different behavior than x86_64. For instance: a struct with multiple fields must always be passed by reference, even if its ABI size fits in a single integer. However, we do pass larger integers such as 128bit by value, which LLVM will correctly lower to use double arguments instead.

2 files changed, 58 insertions(+), 28 deletions(-)

src/arch/wasm/abi.zig+27-28
...@@ -23,8 +23,6 @@ pub fn classifyType(ty: Type, target: Target) [2]Class {...@@ -23,8 +23,6 @@ pub fn classifyType(ty: Type, target: Target) [2]Class {
23 if (!ty.hasRuntimeBitsIgnoreComptime()) return none;23 if (!ty.hasRuntimeBitsIgnoreComptime()) return none;
24 switch (ty.zigTypeTag()) {24 switch (ty.zigTypeTag()) {
25 .Struct => {25 .Struct => {
26 // When the (maybe) scalar type exceeds max 'direct' integer size
27 if (ty.abiSize(target) > 8) return memory;
28 // When the struct type is non-scalar26 // When the struct type is non-scalar
29 if (ty.structFieldCount() > 1) return memory;27 if (ty.structFieldCount() > 1) return memory;
30 // When the struct's alignment is non-natural28 // When the struct's alignment is non-natural
...@@ -34,56 +32,57 @@ pub fn classifyType(ty: Type, target: Target) [2]Class {...@@ -34,56 +32,57 @@ pub fn classifyType(ty: Type, target: Target) [2]Class {
34 return memory;32 return memory;
35 }33 }
36 }34 }
37 if (field.ty.isInt() or field.ty.isAnyFloat()) {
38 return direct;
39 }
40 return classifyType(field.ty, target);35 return classifyType(field.ty, target);
41 },36 },
42 .Int, .Enum, .ErrorSet, .Vector => {37 .Int, .Enum, .ErrorSet, .Vector => {
43 const int_bits = ty.intInfo(target).bits;38 const int_bits = ty.intInfo(target).bits;
44 if (int_bits <= 64) return direct;39 if (int_bits <= 64) return direct;
45 if (int_bits > 64 and int_bits <= 128) return .{ .direct, .direct };40 if (int_bits <= 128) return .{ .direct, .direct };
46 return memory;41 return memory;
47 },42 },
48 .Float => {43 .Float => {
49 const float_bits = ty.floatBits(target);44 const float_bits = ty.floatBits(target);
50 if (float_bits <= 64) return direct;45 if (float_bits <= 64) return direct;
51 if (float_bits > 64 and float_bits <= 128) return .{ .direct, .direct };46 if (float_bits <= 128) return .{ .direct, .direct };
52 return memory;47 return memory;
53 },48 },
54 .Bool => return direct,49 .Bool => return direct,
55 .Array => return memory,50 .Array => return memory,
56 .ErrorUnion => {51 // .ErrorUnion => {
57 const has_tag = ty.errorUnionSet().hasRuntimeBitsIgnoreComptime();52 // const has_tag = ty.errorUnionSet().hasRuntimeBitsIgnoreComptime();
58 const has_pl = ty.errorUnionPayload().hasRuntimeBitsIgnoreComptime();53 // const has_pl = ty.errorUnionPayload().hasRuntimeBitsIgnoreComptime();
59 if (!has_pl) return direct;54 // if (!has_pl) return direct;
60 if (!has_tag) {55 // if (!has_tag) {
61 return classifyType(ty.errorUnionPayload(), target);56 // return classifyType(ty.errorUnionPayload(), target);
62 }57 // }
63 return memory;58 // return memory;
64 },59 // },
65 .Optional => {60 .Optional => {
66 if (ty.isPtrLikeOptional()) return direct;61 std.debug.assert(ty.isPtrLikeOptional());
67 var buf: Type.Payload.ElemType = undefined;62 return direct;
68 const pl_has_bits = ty.optionalChild(&buf).hasRuntimeBitsIgnoreComptime();63 // var buf: Type.Payload.ElemType = undefined;
69 if (!pl_has_bits) return direct;64 // const pl_has_bits = ty.optionalChild(&buf).hasRuntimeBitsIgnoreComptime();
70 return memory;65 // if (!pl_has_bits) return direct;
66 // return memory;
71 },67 },
72 .Pointer => {68 .Pointer => {
73 // Slices act like struct and will be passed by reference69 // // Slices act like struct and will be passed by reference
74 if (ty.isSlice()) return memory;70 // if (ty.isSlice()) return memory;
75 return direct;71 return direct;
76 },72 },
77 .Union => {73 .Union => {
78 const layout = ty.unionGetLayout(target);74 const layout = ty.unionGetLayout(target);
79 if (layout.payload_size == 0 and layout.tag_size != 0) {75 std.debug.assert(layout.tag_size == 0);
80 return classifyType(ty.unionTagTypeSafety().?, target);76 // if (layout.payload_size == 0 and layout.tag_size != 0) {
81 }77 // return classifyType(ty.unionTagType().?, target);
78 // }
82 if (ty.unionFields().count() > 1) return memory;79 if (ty.unionFields().count() > 1) return memory;
83 return classifyType(ty.unionFields().values()[0].ty, target);80 return classifyType(ty.unionFields().values()[0].ty, target);
84 },81 },
85 .AnyFrame, .Frame => return direct,82 // .AnyFrame, .Frame => return direct,
8683 .ErrorUnion,
84 .Frame,
85 .AnyFrame,
87 .NoReturn,86 .NoReturn,
88 .Void,87 .Void,
89 .Type,88 .Type,
src/codegen/llvm.zig+31
...@@ -22,6 +22,7 @@ const Type = @import("../type.zig").Type;...@@ -22,6 +22,7 @@ const Type = @import("../type.zig").Type;
22const LazySrcLoc = Module.LazySrcLoc;22const LazySrcLoc = Module.LazySrcLoc;
23const CType = @import("../type.zig").CType;23const CType = @import("../type.zig").CType;
24const x86_64_abi = @import("../arch/x86_64/abi.zig");24const x86_64_abi = @import("../arch/x86_64/abi.zig");
25const wasm_c_abi = @import("../arch/wasm/abi.zig");
2526
26const Error = error{ OutOfMemory, CodegenFail };27const Error = error{ OutOfMemory, CodegenFail };
2728
...@@ -9093,6 +9094,10 @@ fn firstParamSRet(fn_info: Type.Payload.Function.Data, target: std.Target) bool...@@ -9093,6 +9094,10 @@ fn firstParamSRet(fn_info: Type.Payload.Function.Data, target: std.Target) bool
9093 .windows => return x86_64_abi.classifyWindows(fn_info.return_type, target) == .memory,9094 .windows => return x86_64_abi.classifyWindows(fn_info.return_type, target) == .memory,
9094 else => return x86_64_abi.classifySystemV(fn_info.return_type, target)[0] == .memory,9095 else => return x86_64_abi.classifySystemV(fn_info.return_type, target)[0] == .memory,
9095 },9096 },
9097 .wasm32 => {
9098 const classes = wasm_c_abi.classifyType(fn_info.return_type, target);
9099 return classes[0] == .indirect;
9100 },
9096 else => return false, // TODO investigate C ABI for other architectures9101 else => return false, // TODO investigate C ABI for other architectures
9097 },9102 },
9098 else => return false,9103 else => return false,
...@@ -9197,6 +9202,20 @@ fn lowerFnRetTy(dg: *DeclGen, fn_info: Type.Payload.Function.Data) !*const llvm....@@ -9197,6 +9202,20 @@ fn lowerFnRetTy(dg: *DeclGen, fn_info: Type.Payload.Function.Data) !*const llvm.
9197 return dg.context.structType(&llvm_types_buffer, llvm_types_index, .False);9202 return dg.context.structType(&llvm_types_buffer, llvm_types_index, .False);
9198 },9203 },
9199 },9204 },
9205 .wasm32 => {
9206 if (is_scalar) {
9207 return dg.lowerType(fn_info.return_type);
9208 }
9209 const classes = wasm_c_abi.classifyType(fn_info.return_type, target);
9210 if (classes[0] == .indirect or classes[0] == .none) {
9211 return dg.context.voidType();
9212 }
9213
9214 assert(classes[0] == .direct and classes[1] == .none);
9215 const scalar_type = wasm_c_abi.scalarType(fn_info.return_type, target);
9216 const abi_size = scalar_type.abiSize(target);
9217 return dg.context.intType(@intCast(c_uint, abi_size * 8));
9218 },
9200 // TODO investigate C ABI for other architectures9219 // TODO investigate C ABI for other architectures
9201 else => return dg.lowerType(fn_info.return_type),9220 else => return dg.lowerType(fn_info.return_type),
9202 }9221 }
...@@ -9372,6 +9391,18 @@ const ParamTypeIterator = struct {...@@ -9372,6 +9391,18 @@ const ParamTypeIterator = struct {
9372 return .multiple_llvm_ints;9391 return .multiple_llvm_ints;
9373 },9392 },
9374 },9393 },
9394 .wasm32 => {
9395 it.zig_index += 1;
9396 it.llvm_index += 1;
9397 if (is_scalar) {
9398 return .byval;
9399 }
9400 const classes = wasm_c_abi.classifyType(ty, it.target);
9401 if (classes[0] == .indirect) {
9402 return .byref;
9403 }
9404 return .abi_sized_int;
9405 },
9375 // TODO investigate C ABI for other architectures9406 // TODO investigate C ABI for other architectures
9376 else => {9407 else => {
9377 it.zig_index += 1;9408 it.zig_index += 1;