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