authorgravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-04-23 20:45:10+02:00
committergravatar for luuk@degram.devLuuk de Gram <luuk@degram.dev> 2022-04-26 12:20:27+02:00
logcb49af6c9a6b790ec341cb081d1fec381d44e9a4
tree91cd9c20684b33a3c43eb8393596910d664db02b
parentab658e32bdfa234b6c33f0718cac7a23fbd4074f

wasm: Initial C-ABI implementation

This implements the C-ABI convention as specified by: https://github.com/WebAssembly/tool-conventions/blob/main/BasicCABI.md While not an official specification, it's the ABI that is output by clang/LLVM. As we use LLVM to compile compiler-rt, and want to integrate with C-libraries, we follow the same convention when the calling convention results in 'C'.

1 files changed, 102 insertions(+), 0 deletions(-)

src/arch/wasm/abi.zig created+102
...@@ -0,0 +1,102 @@
1//! Classifies Zig types to follow the C-ABI for Wasm.
2//! The convention for Wasm's C-ABI can be found at the tool-conventions repo:
3//! https://github.com/WebAssembly/tool-conventions/blob/main/BasicCABI.md
4//! When not targeting the C-ABI, Zig is allowed to do derail from this convention.
5//! Note: Above mentioned document is not an official specification, therefore called a convention.
6
7const std = @import("std");
8const Type = @import("../../type.zig").Type;
9const Target = std.Target;
10
11/// Defines how to pass a type as part of a function signature,
12/// both for parameters as well as return values.
13pub const Class = enum { direct, indirect, none };
14
15const none: [2]Class = .{ .none, .none };
16const memory: [2]Class = .{ .indirect, .none };
17const direct: [2]Class = .{ .direct, .none };
18
19/// Classifies a given Zig type to determine how they must be passed
20/// or returned as value within a wasm function.
21/// When all elements result in `.none`, no value must be passed in or returned.
22pub fn classifyType(ty: Type, target: Target) [2]Class {
23 if (!ty.hasRuntimeBitsIgnoreComptime()) return none;
24 switch (ty.zigTypeTag()) {
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-scalar
29 if (ty.structFieldCount() > 1) return memory;
30 // When the struct's alignment is non-natural
31 const field = ty.structFields().values()[0];
32 if (field.abi_align != 0) {
33 if (field.abi_align > field.ty.abiAlignment(target)) {
34 return memory;
35 }
36 }
37 if (field.ty.isInt() or field.ty.isAnyFloat()) {
38 return direct;
39 }
40 return classifyType(field.ty, target);
41 },
42 .Int, .Enum, .ErrorSet, .Vector => {
43 const int_bits = ty.intInfo(target).bits;
44 if (int_bits <= 64) return direct;
45 if (int_bits > 64 and int_bits <= 128) return .{ .direct, .direct };
46 return memory;
47 },
48 .Float => {
49 const float_bits = ty.floatBits(target);
50 if (float_bits <= 64) return direct;
51 if (float_bits > 64 and float_bits <= 128) return .{ .direct, .direct };
52 return memory;
53 },
54 .Bool => return direct,
55 .ErrorUnion => {
56 const has_tag = ty.errorUnionSet().hasRuntimeBitsIgnoreComptime();
57 const has_pl = ty.errorUnionPayload().hasRuntimeBitsIgnoreComptime();
58 if (!has_pl) return direct;
59 if (!has_tag) {
60 return classifyType(ty.errorUnionPayload(), target);
61 }
62 return memory;
63 },
64 .Optional => {
65 if (ty.isPtrLikeOptional()) return direct;
66 var buf: Type.Payload.ElemType = undefined;
67 const pl_has_bits = ty.optionalChild(&buf).hasRuntimeBitsIgnoreComptime();
68 if (!pl_has_bits) return direct;
69 return memory;
70 },
71 .Pointer => {
72 // Slices act like struct and will be passed by reference
73 if (ty.isSlice()) return memory;
74 return direct;
75 },
76 .Array => {
77 if (ty.arrayLen() == 1) return direct;
78 return memory;
79 },
80 .Union => {
81 const layout = ty.unionGetLayout(target);
82 if (layout.payload_size == 0 and layout.tag_size != 0) {
83 return classifyType(ty.unionTagType().?, target);
84 }
85 return classifyType(ty.errorUnionPayload(), target);
86 },
87 .AnyFrame, .Frame => return direct,
88
89 .NoReturn,
90 .Void,
91 .Type,
92 .ComptimeFloat,
93 .ComptimeInt,
94 .Undefined,
95 .Null,
96 .BoundFn,
97 .Fn,
98 .Opaque,
99 .EnumLiteral,
100 => unreachable,
101 }
102}