File: Instr.h

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 (269 lines) | stat: -rw-r--r-- 11,681 bytes parent folder | download
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
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
#pragma once
#include "OpCode.h"
#include "Operand.h"
#include "CondFlag.h"
#include "TypeDesc.h"
#include "Core/Object.h"
#include "Core/EnginePtr.h"
#include "Utils/Bitmask.h"

namespace code {
	STORM_PKG(core.asm);

	/**
	 * Class representing an entire asm-instruction.
	 *
	 * Note: immutable class.
	 */
	class Instr : public Object {
		STORM_CLASS;
	public:
		// Create a no-op instruction.
		STORM_CTOR Instr();

		// Copy this instruction.
		Instr(const Instr &o);

		// Deep copy.
		virtual void STORM_FN deepCopy(CloneEnv *env);

		// Get the op-code.
		op::OpCode STORM_FN op() const { return iOp; }

		// Get the source operand.
		inline Operand STORM_FN src() const { return iSrc; }

		// Get the destination operand.
		inline Operand STORM_FN dest() const { return iDest; }

		// Get the mode of the destination operand. This determines how the instruction uses the
		// destination (e.g. read/write, read, or write).
		DestMode STORM_FN mode() const;

		// Get the maximum size of the operands.
		Size STORM_FN size() const;

		// Create an instruction with a different set of operands. Intended to be used by backends,
		// so performs no sanity checking.
		virtual Instr *STORM_FN alter(Operand dest, Operand src);

		// Create an instruction with a different source operand. Intended to be used by backends,
		// so performs no sanity checking.
		virtual Instr *STORM_FN alterSrc(Operand src);

		// Create an instruction with a different destination operand. Intended to be used by backends,
		// so performs no sanity checking.
		virtual Instr *STORM_FN alterDest(Operand dest);

		// To string.
		virtual void STORM_FN toS(StrBuf *to) const;

	protected:
		// Private constructor, used by 'instrXxx()' functions below.
		Instr(op::OpCode opCode, const Operand &dest, const Operand &src);

		// Op-code.
		op::OpCode iOp;

		// Src and dest fields.
		Operand iSrc;
		Operand iDest;

		// Creators.
		friend Instr *instr(EnginePtr e, op::OpCode op);
		friend Instr *instrSrc(EnginePtr e, op::OpCode op, Operand src);
		friend Instr *instrDest(EnginePtr e, op::OpCode op, Operand dest);
		friend Instr *instrDestSrc(EnginePtr e, op::OpCode op, Operand dest, Operand src);
		friend Instr *instrLoose(EnginePtr e, op::OpCode op, Operand dest, Operand src);
	};

	/**
	 * Additional information required for FnCall and FnRet operations. Contains an additional
	 * 'TypeDesc' describing the value being used in detail.
	 *
	 * TODO: If we wanted to, we could probably squeeze the extra pointer inside the Operand in a
	 * regular Instr somehow. I do not, however, think the effort is worth it.
	 */
	class TypeInstr : public Instr {
		STORM_CLASS;
	public:
		// Create.
		TypeInstr(op::OpCode opCode, const Operand &dest, const Operand &src, TypeDesc *type, Bool member);

		// The type of used in this instruction.
		TypeDesc *type;

		// Is this a call to a member function? Only used for 'fnCall' instructions.
		Bool member;

		// Alter various parts.
		virtual Instr *STORM_FN alter(Operand dest, Operand src);
		virtual Instr *STORM_FN alterSrc(Operand src);
		virtual Instr *STORM_FN alterDest(Operand dest);

		// Deep copy.
		virtual void STORM_FN deepCopy(CloneEnv *env);

		// To string.
		virtual void STORM_FN toS(StrBuf *to) const;
	};

	// Create an instruction without operands.
	Instr *STORM_FN instr(EnginePtr e, op::OpCode op);
	Instr *STORM_FN instrSrc(EnginePtr e, op::OpCode op, Operand src);
	Instr *STORM_FN instrDest(EnginePtr e, op::OpCode op, Operand dest);
	Instr *STORM_FN instrDestSrc(EnginePtr e, op::OpCode op, Operand dest, Operand src);

	// Create without checking the sanity of the parameters. Used for pseudo-instructions and predicates among others.
	Instr *STORM_FN instrLoose(EnginePtr e, op::OpCode op, Operand dest, Operand src);

	/**
	 * Create instructions:
	 */

	Instr *STORM_FN nop(EnginePtr e);
	Instr *STORM_FN mov(EnginePtr e, Operand to, Operand from);
	Instr *STORM_FN swap(EnginePtr e, Reg a, Operand b);
	Instr *STORM_FN jmp(EnginePtr e, Operand to);
	Instr *STORM_FN jmp(EnginePtr e, Label to, CondFlag cond);
	Instr *STORM_FN call(EnginePtr e, Operand to, Size resultSize);
	Instr *STORM_FN ret(EnginePtr e, Size resultSize); // Returns whatever is in eax register.

	// Note: Be very careful when using push and pop in code that performs function calls! Certain
	// architectures require the stack to be properly aligned when calling a function. The different
	// backends takes care of this for you, but they will not keep track of push and pop
	// instructions so that they can compensate for any additional instructions used.
	Instr *STORM_FN push(EnginePtr e, Operand v);
	Instr *STORM_FN pop(EnginePtr e, Operand to);
	Instr *STORM_FN pushFlags(EnginePtr e);
	Instr *STORM_FN popFlags(EnginePtr e);

	// This one has somewhat special semantics, when used with a reference as 'from', it instead
	// loads the RefSource referred to by the Ref.
	Instr *STORM_FN lea(EnginePtr e, Operand to, Operand from);

	// Set a byte to the result of an operation.
	Instr *STORM_FN setCond(EnginePtr e, Operand to, CondFlag cond);

	// Function calls. Works like this: fnParam(10), fnParam(20), fnCall(myFn). Labels placed between fnParam
	// and fnCall will effectively be before the first fnParam. All fnParams will be merged into the fnCall, so do
	// not place other instructions between. Parameters are entered "right to left". Ie, the above example
	// calls myFn(10, 20).
	// The variations ending in 'Ref' assumes that the source (or destination) is a pointer rather than
	// the target itself.
	Instr *STORM_FN fnParam(EnginePtr e, TypeDesc *type, Operand src);
	Instr *STORM_FN fnParamRef(EnginePtr e, TypeDesc *type, Operand src);
	Instr *STORM_FN fnCall(EnginePtr e, Operand call, Bool member); // no result
	Instr *STORM_FN fnCall(EnginePtr e, Operand call, Bool member, TypeDesc *result, Operand to);
	Instr *STORM_FN fnCallRef(EnginePtr e, Operand call, Bool member, TypeDesc *result, Operand to);

	// High-level function return. Examines the return type specified in the listing and handles the
	// return properly according to the current platform. Implies an 'epilog' instruction as well.
	Instr *STORM_FN fnRet(EnginePtr e, Operand src);
	Instr *STORM_FN fnRetRef(EnginePtr e, Operand src);
	Instr *STORM_FN fnRet(EnginePtr e);

	// Integer math (signed/unsigned)
	Instr *STORM_FN STORM_NAME(bor, or)(EnginePtr e, Operand dest, Operand src);
	Instr *STORM_FN STORM_NAME(band, and)(EnginePtr e, Operand dest, Operand src);
	Instr *STORM_FN STORM_NAME(bxor, xor)(EnginePtr e, Operand dest, Operand src);
	Instr *STORM_FN STORM_NAME(bnot, not)(EnginePtr e, Operand dest);
	Instr *STORM_FN test(EnginePtr e, Operand dest, Operand src);
	Instr *STORM_FN add(EnginePtr e, Operand dest, Operand src);
	Instr *STORM_FN adc(EnginePtr e, Operand dest, Operand src);
	Instr *STORM_FN sub(EnginePtr e, Operand dest, Operand src);
	Instr *STORM_FN sbb(EnginePtr e, Operand dest, Operand src);
	Instr *STORM_FN cmp(EnginePtr e, Operand dest, Operand src);
	Instr *STORM_FN mul(EnginePtr e, Operand dest, Operand src);

	// Signed math
	Instr *STORM_FN idiv(EnginePtr e, Operand dest, Operand src); // div a, b <=> a = a / b
	Instr *STORM_FN imod(EnginePtr e, Operand dest, Operand src);

	// Unsigned math
	Instr *STORM_FN udiv(EnginePtr e, Operand dest, Operand src);
	Instr *STORM_FN umod(EnginePtr e, Operand dest, Operand src);

	// Shifts. src.size() == 1
	Instr *STORM_FN shl(EnginePtr e, Operand dest, Operand src);
	Instr *STORM_FN shr(EnginePtr e, Operand dest, Operand src);
	Instr *STORM_FN sar(EnginePtr e, Operand dest, Operand src);

	// Resize operands. Works like a mov, but sign-extends if needed.
	Instr *STORM_FN icast(EnginePtr e, Operand dest, Operand src);
	Instr *STORM_FN ucast(EnginePtr e, Operand dest, Operand src);

	// Floating point math.
	Instr *STORM_FN fadd(EnginePtr e, Operand dest, Operand src);
	Instr *STORM_FN fsub(EnginePtr e, Operand dest, Operand src);
	Instr *STORM_FN fneg(EnginePtr e, Operand dest, Operand src);
	Instr *STORM_FN fmul(EnginePtr e, Operand dest, Operand src);
	Instr *STORM_FN fdiv(EnginePtr e, Operand dest, Operand src);
	Instr *STORM_FN fcmp(EnginePtr e, Operand dest, Operand src);
	Instr *STORM_FN fcast(EnginePtr e, Operand dest, Operand src);  // float -> float
	Instr *STORM_FN fcasti(EnginePtr e, Operand dest, Operand src); // float -> signed
	Instr *STORM_FN fcastu(EnginePtr e, Operand dest, Operand src); // float -> unsigned
	Instr *STORM_FN icastf(EnginePtr e, Operand dest, Operand src); // signed -> float
	Instr *STORM_FN ucastf(EnginePtr e, Operand dest, Operand src); // unsigned -> float

	// Compatibility with x86 in 32-bit mode.
	Instr *STORM_FN fstp(EnginePtr e, Operand dest);
	Instr *STORM_FN fld(EnginePtr e, Operand src);

	// Data.
	Instr *STORM_FN dat(EnginePtr e, Operand v);

	// Offset of a label.
	Instr *STORM_FN lblOffset(EnginePtr e, Label l);

	// Align to specific size.
	Instr *STORM_FN alignAs(EnginePtr e, Size v);
	Instr *STORM_FN align(EnginePtr e, Offset v);

	// Function prolog/epilog. The prolog will automatically initialize the root block, and the epilog
	// will automatically destroy all active blocks. The epilog does not count as closing a block, and
	// may therefore be used inside functions to implement a return statement. For example:
	// listing << prolog() << begin(block2) << epilog() << ret() << ... << end(block2) << ... << epilog() << ret();
	// Here: block2 is considered to continue all the way between begin() and end(), even though the block is
	// also ended within the epilog in the middle. The epilog only preserves ptrA register.
	Instr *STORM_FN prolog(EnginePtr e);
	Instr *STORM_FN epilog(EnginePtr e);

	// Shadow mov operation. Does not impact detection of dirty registers, so can be used to
	// save/restore registers without marking them as 'dirty'.
	Instr *STORM_FN shadowMov(EnginePtr e, Operand dest, Operand src);

	// Note that a register has been preserved on the stack. Intended to be used internally by backends.
	Instr *STORM_FN preserve(EnginePtr e, Operand dest, Reg reg);

	// Note that a register has been preserved by pushing it. Intended to be used internally by backends.
	Instr *STORM_FN preserve(EnginePtr e, Reg reg);

	// Node a source position.
	Instr *STORM_FN location(EnginePtr e, SrcPos pos);

	// Add metadata.
	Instr *STORM_FN meta(EnginePtr e, Object *data);
	Instr *STORM_FN meta(EnginePtr e, TObject *data);

	// Scope management. A scope is assumed to be open on all instructions between "begin(scope)" and "end(scope)".
	// Do not, for example, have all begin/end scopes in the end of the listing and jump forth and back between them.
	// That makes the exception handling fail to detect what to destroy. NOTE: Does not preserve registers!
	Instr *STORM_FN begin(EnginePtr e, Block block);
	Instr *STORM_FN end(EnginePtr e, Block block);

	// End zero or more blocks and then jump to 'label'. Ends block until the current topmost block
	// is 'block'. As is the case with 'epilog', this does not affect the current block at the
	// location just after the 'endJmp' instruction. As such, this pseudo-op is useful for early
	// block exits, such as 'continue' and 'break' statements.
	Instr *STORM_FN jmpBlock(EnginePtr e, Label to, Block block);

	// Activate destruction of a particular variable from here on in the listing.
	Instr *STORM_FN activate(EnginePtr e, Var var);

	// Segment override for the next instruction, not reliable for function calls or double memory accesses. Only
	// availiable on relevant architectures, for example X86.
	Instr *STORM_FN threadLocal(EnginePtr e);

	// Note: add a forwarder in InstrFwd as well!
}