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