aboutsummaryrefslogtreecommitdiff
path: root/src/codegen/wasm/abi.zig
blob: a1fa8126491def2d9e2717faa0ccedcd2ee24805 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
//! Classifies Zig types to follow the C-ABI for Wasm.
//! The convention for Wasm's C-ABI can be found at the tool-conventions repo:
//! https://github.com/WebAssembly/tool-conventions/blob/main/BasicCABI.md
//! When not targeting the C-ABI, Zig is allowed to do derail from this convention.
//! Note: Above mentioned document is not an official specification, therefore called a convention.

const std = @import("std");
const Target = std.Target;
const assert = std.debug.assert;

const Type = @import("../../Type.zig");
const Zcu = @import("../../Zcu.zig");

/// Defines how to pass a type as part of a function signature,
/// both for parameters as well as return values.
pub const Class = union(enum) {
    direct: Type,
    indirect,
};

/// Classifies a given Zig type to determine how they must be passed
/// or returned as value within a wasm function.
pub fn classifyType(ty: Type, zcu: *const Zcu) Class {
    const ip = &zcu.intern_pool;
    assert(ty.hasRuntimeBitsIgnoreComptime(zcu));
    switch (ty.zigTypeTag(zcu)) {
        .int, .@"enum", .error_set => return .{ .direct = ty },
        .float => return .{ .direct = ty },
        .bool => return .{ .direct = ty },
        .vector => return .{ .direct = ty },
        .array => return .indirect,
        .optional => {
            assert(ty.isPtrLikeOptional(zcu));
            return .{ .direct = ty };
        },
        .pointer => {
            assert(!ty.isSlice(zcu));
            return .{ .direct = ty };
        },
        .@"struct" => {
            const struct_type = zcu.typeToStruct(ty).?;
            if (struct_type.layout == .@"packed") {
                return .{ .direct = ty };
            }
            if (struct_type.field_types.len > 1) {
                // The struct type is non-scalar.
                return .indirect;
            }
            const field_ty = Type.fromInterned(struct_type.field_types.get(ip)[0]);
            const explicit_align = struct_type.fieldAlign(ip, 0);
            if (explicit_align != .none) {
                if (explicit_align.compareStrict(.gt, field_ty.abiAlignment(zcu)))
                    return .indirect;
            }
            return classifyType(field_ty, zcu);
        },
        .@"union" => {
            const union_obj = zcu.typeToUnion(ty).?;
            if (union_obj.flagsUnordered(ip).layout == .@"packed") {
                return .{ .direct = ty };
            }
            const layout = ty.unionGetLayout(zcu);
            assert(layout.tag_size == 0);
            if (union_obj.field_types.len > 1) return .indirect;
            const first_field_ty = Type.fromInterned(union_obj.field_types.get(ip)[0]);
            return classifyType(first_field_ty, zcu);
        },
        .error_union,
        .frame,
        .@"anyframe",
        .noreturn,
        .void,
        .type,
        .comptime_float,
        .comptime_int,
        .undefined,
        .null,
        .@"fn",
        .@"opaque",
        .enum_literal,
        => unreachable,
    }
}

pub fn lowerAsDoubleI64(scalar_ty: Type, zcu: *const Zcu) bool {
    return scalar_ty.bitSize(zcu) > 64;
}