File: Params.cpp

package info (click to toggle)
storm-lang 0.7.4-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 52,004 kB
  • sloc: ansic: 261,462; cpp: 140,405; sh: 14,891; perl: 9,846; python: 2,525; lisp: 2,504; asm: 860; makefile: 678; pascal: 70; java: 52; xml: 37; awk: 12
file content (203 lines) | stat: -rw-r--r-- 5,579 bytes parent folder | download | duplicates (2)
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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
#include "stdafx.h"
#include "Params.h"
#include "Asm.h"
#include "Utils/Bitwise.h"

namespace code {
	namespace arm64 {

		static Size roundSize(Size sz) {
			if (sz.size64() == 1)
				return sz;
			else if (sz.size64() <= 4)
				return Size::sInt;
			else
				return sz.alignedAs(Size::sWord);
		}

		Params::Params() : code::Params(8, 8, 8, 16) {}

		void Params::addPrimitive(Nat id, Primitive p) {
			switch (p.kind()) {
			case primitive::none:
				// Nothing to do!
				break;
			case primitive::pointer:
			case primitive::integer:
				addInt(Param(id, p, true));
				break;
			case primitive::real:
				addReal(Param(id, p, true));
				break;
			default:
				dbg_assert(false, L"Unknown primitive type!");
			}
		}

		void Params::addComplex(Nat id, ComplexDesc *desc) {
			// The complex types are actually simple. We just add a pointer to our parameter list!
			addInt(Param(id, desc->size(), true, 0, true));
		}

		// Check if the parameter is a uniform float struct.
		static Bool uniformFp(SimpleDesc *type) {
			GcArray<Primitive> *layout = type->v;
			if (layout->count <= 0)
				return false;
			if (layout->v[0].kind() != primitive::real)
				return false;
			if (layout->v[0].offset().v64() != 0)
				return false;

			// Check for this size.
			Nat size = layout->v[0].size().size64();

			// Others should be evenly distributed.
			for (Nat i = 1; i < layout->count; i++) {
				if (layout->v[i].kind() != primitive::real)
					return false;
				if (layout->v[i].size().size64() != size)
					return false;
				if (Nat(layout->v[i].offset().v64()) != i * size)
					return false;
			}

			return true;
		}

		void Params::addSimple(Nat id, SimpleDesc *type) {
			// Here, we need to check the type to see if we shall pass parts of it in registers.
			// The rules are roughly:
			// - If the struct is a uniform FP struct that fits in 4 or fewer registers, pass it in the
			//   corresponding fp registers.
			// - If the struct is larger than 2 words, pass it on the stack, as if it was "complex".
			// - If the parameter has a natural alignment of >= 16 bytes, "pad" to the next even register.
			//   (we support no such types at the moment)
			// - If registers are full, pass it on the stack.
			// - If all members are floats: pass in fp register. Otherwise, int register.

			// Uniform FP struct (HFA)?
			if (uniformFp(type)) {
				Nat regs = Nat(type->v->count);
				if (regs <= 4 && hasReal(regs)) {
					// Pass it in the next fp registers.
					for (Nat i = 0; i < regs; i++) {
						const Primitive &p = type->v->v[i];
						addReal(Param(id, p.size(), true, p.offset().v64(), false));
					}

				} else {
					// Pass it on the stack.
					addStack(Param(id, type->size(), true, 0, false));
				}

				return;
			}

			// Now, we know that we are dealing with a type that should be passed in integer registers.

			Nat size = type->size().size64();
			if (size > 16) {
				// Too large: pass on the stack, replace it with a pointer to the data.
				addInt(Param(id, roundSize(type->size()), true, 0, true));
				return;
			}

			// Two registers?
			if (size > 8) {
				// Check if enough space.
				if (hasInt(2)) {
					addInt(Param(id, Size::sLong, true, 0, false));
					addInt(Param(id, Size::sLong, true, 8, false));
					return;
				}
			} else {
				if (hasInt(1)) {
					addInt(Param(id, roundSize(type->size()), true, 0, false));
					return;
				}
			}

			// Fallback: Push it on the stack.
			addStack(Param(id, type->size(), true, 0, false));
		}

		void Params::resultPrimitive(Primitive p) {
			switch (p.kind()) {
			case primitive::none:
				resultData = Result::empty();
				break;
			case primitive::pointer:
			case primitive::integer:
				resultData = Result::inRegister(engine(), asSize(ptrr(0), p.size()));
				break;
			case primitive::real:
				resultData = Result::inRegister(engine(), asSize(dr(0), p.size()));
				break;
			default:
				dbg_assert(false, L"Unknown primitive type!");
			}
		}

		void Params::resultComplex(ComplexDesc *) {
			// Always returned in memory. Always in x8.
			resultData = Result::inMemory(ptrr(8));
		}

		void Params::resultSimple(SimpleDesc *type) {
			// Similar to 'addSimple' above, but don't have to handle cases with not enough register space.

			// Uniform FP struct (HFA)?
			if (uniformFp(type)) {
				Nat regs = Nat(type->v->count);
				if (regs <= 4) {
					resultData = Result::inRegisters(engine(), regs);
					for (Nat i = 0; i < regs; i++) {
						const Primitive &p = type->v->v[i];
						resultData.putRegister(asSize(dr(i), p.size()), p.offset().v64());
					}
					return;
				}
			} else {
				// Integer!
				Nat size = type->size().size64();
				if (size == 0) {
					// Zero registers.
					resultData = Result::empty();
					return;
				} else if (size <= 8) {
					// One register:
					Reg r = asSize(ptrr(0), roundSize(type->size()));
					resultData = Result::inRegister(engine(), r);
					return;
				} else if (size <= 16) {
					// Two registers:
					resultData = Result::inRegisters(engine(), 2);
					resultData.putRegister(xr(0), 0);
					resultData.putRegister(xr(1), 8);
					return;
				}
			}

			// Pass it in memory:
			resultData = Result::inMemory(ptrr(8));
		}

		Reg Params::registerSrc(Nat n) const {
			const Nat intRegs = 8;
			if (n < intRegs)
				return ptrr(n);
			else
				return dr(n - intRegs);
		}

		Params *layoutParams(TypeDesc *result, Array<TypeDesc *> *types) {
			Params *p = new (types) Params();
			p->result(result);
			for (Nat i = 0; i < types->count(); i++)
				p->add(i, types->at(i));
			return p;
		}

	}
}