1#!/usr/bin/env python3
2#
3# Copyright (c) 2024 Intel Corporation
4#
5# SPDX-License-Identifier: Apache-2.0
6
7"""
8Contains a class to describe data types used for
9dictionary logging.
10"""
11
12import struct
13
14
15class DataTypes:
16    """Class regarding data types, their alignments and sizes"""
17    INT = 0
18    UINT = 1
19    LONG = 2
20    ULONG = 3
21    LONG_LONG = 4
22    ULONG_LONG = 5
23    PTR = 6
24    DOUBLE = 7
25    LONG_DOUBLE = 8
26    NUM_TYPES = 9
27
28    def __init__(self, database):
29        self.database = database
30        self.data_types = {}
31
32        if database.is_tgt_64bit():
33            self.add_data_type(self.LONG, "q")
34            self.add_data_type(self.ULONG, "Q")
35            self.add_data_type(self.LONG_LONG, "q")
36            self.add_data_type(self.ULONG_LONG, "Q")
37            self.add_data_type(self.PTR, "Q")
38        else:
39            self.add_data_type(self.LONG, "i")
40            self.add_data_type(self.ULONG, "I")
41            self.add_data_type(self.LONG_LONG, "q")
42            self.add_data_type(self.ULONG_LONG, "Q")
43            self.add_data_type(self.PTR, "I")
44
45        self.add_data_type(self.INT, "i")
46        self.add_data_type(self.UINT, "I")
47        self.add_data_type(self.DOUBLE, "d")
48        self.add_data_type(self.LONG_DOUBLE, "d")
49
50
51    @staticmethod
52    def get_stack_min_align(arch, is_tgt_64bit):
53        '''
54        Correspond to the VA_STACK_ALIGN and VA_STACK_MIN_ALIGN
55        in cbprintf_internal.h. Note that there might be some
56	variations that is obtained via actually running through
57	the log parser.
58
59        Return a tuple where the first element is stack alignment
60        value. The second element is true if alignment needs to
61        be further refined according to data type, false if not.
62        '''
63        if arch == "arc":
64            if is_tgt_64bit:
65                need_further_align = True
66                stack_min_align = 8
67            else:
68                need_further_align = False
69                stack_min_align = 1
70
71        elif arch == "arm64":
72            need_further_align = True
73            stack_min_align = 8
74
75        elif arch == "sparc":
76            need_further_align = False
77            stack_min_align = 1
78
79        elif arch == "x86":
80            if is_tgt_64bit:
81                need_further_align = True
82                stack_min_align = 8
83            else:
84                need_further_align = False
85                stack_min_align = 1
86
87        elif arch == "riscv32e":
88            need_further_align = False
89            stack_min_align = 1
90
91        elif arch == "riscv":
92            need_further_align = True
93
94            if is_tgt_64bit:
95                stack_min_align = 8
96            else:
97                stack_min_align = 1
98
99        else:
100            need_further_align = True
101            stack_min_align = 1
102
103        return (stack_min_align, need_further_align)
104
105
106    @staticmethod
107    def get_data_type_align(data_type, is_tgt_64bit):
108        '''
109        Get the alignment for a particular data type.
110        '''
111        if data_type == DataTypes.LONG_LONG:
112            align = 8
113        elif data_type == DataTypes.LONG:
114            if is_tgt_64bit:
115                align = 8
116            else:
117                align = 4
118        else:
119            # va_list alignment is at least a integer
120            align = 4
121
122        return align
123
124
125    def add_data_type(self, data_type, fmt):
126        """Add one data type"""
127        if self.database.is_tgt_little_endian():
128            endianness = "<"
129        else:
130            endianness = ">"
131
132        formatter = endianness + fmt
133
134        self.data_types[data_type] = {}
135        self.data_types[data_type]['fmt'] = formatter
136
137        size = struct.calcsize(formatter)
138
139        if data_type == self.LONG_DOUBLE:
140            # Python doesn't have long double but we still
141            # need to skip correct number of bytes
142            size = 16
143
144        self.data_types[data_type]['sizeof'] = size
145
146        # Might need actual number for different architectures
147        # but these seem to work fine for now.
148        if self.database.is_tgt_64bit():
149            align = 8
150        else:
151            align = 4
152
153        # 'align' is used to "jump" over an argument so it has
154        # to be at least size of the data type.
155        align = max(align, size)
156        self.data_types[data_type]['align'] = align
157
158        # 'stack_align' should correspond to VA_STACK_ALIGN
159        # in cbprintf_internal.h
160        stack_align, need_more_align = DataTypes.get_stack_min_align(
161                                        self.database.get_arch(),
162                                        self.database.is_tgt_64bit())
163
164        if need_more_align:
165            stack_align = DataTypes.get_data_type_align(data_type,
166                                                        self.database.is_tgt_64bit())
167
168        self.data_types[data_type]['stack_align'] = stack_align
169
170
171    def get_sizeof(self, data_type):
172        """Get sizeof() of a data type"""
173        return self.data_types[data_type]['sizeof']
174
175
176    def get_alignment(self, data_type):
177        """Get the alignment of a data type"""
178        return self.data_types[data_type]['align']
179
180
181    def get_stack_alignment(self, data_type):
182        """Get the stack alignment of a data type"""
183        return self.data_types[data_type]['stack_align']
184
185
186    def get_formatter(self, data_type):
187        """Get the formatter for a data type"""
188        return self.data_types[data_type]['fmt']
189