File: CodeX64.cpp

package info (click to toggle)
storm-lang 0.7.5-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 52,028 kB
  • sloc: ansic: 261,471; cpp: 140,432; 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 (110 lines) | stat: -rw-r--r-- 3,645 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
#include "stdafx.h"
#include "CodeX64.h"
#include "Gc.h"
#include "Core/GcCode.h"
#include "DwarfTable.h"
#include "Utils/Cache.h"

namespace storm {
	namespace x64 {

		// Will not work properly unless on a 64-bit machine.
#ifdef X64

		static inline bool singleInt(Word value) {
			const Long limit = Long(1) << Long(31);
			Long v(value);
			return v >= -limit && v < limit;
		}

		static inline void writeJump(void *code, const GcCodeRef &ref) {
			// We're dealing with 6 bytes of data. The pointer, located at 'now->offset' and two
			// bytes of op-codes located just before the pointer. For simplicity, we will read 2
			// additional bytes after the pointer so that we can manipulate an entire machine word
			// of 8 bytes when reading/writing to the machine code. This allows us to use atomic
			// instructions properly. We can always do this safely, as the GcCode object will always
			// be allocated directly after the code segment. Thereby, there will always be memory we
			// can legally access (even though we should not trash it).
			//
			// There are two variants of OP-codes used here, short and long. The short variants are
			// used when the target address is within 2GB of the end of the called pointer. The long
			// variant is used otherwise. Both variants need to be the same length, and we can not
			// use a 'nop' instruction to for padding (since then we could alter the instructions
			// after the 'nop' has been executed, but before the actual jump has been executed,
			// causing execution to resume in the middle of an instruction). Therefore, we pad the
			// short variant using a REX.W prefix.
			//
			// The variants are encoded as follows:
			//        short             long
			// call:  48 E8 <offset>    FF 15 <offset>
			// jmp:   48 E9 <offset>    FF 25 <offset>
			//
			// For the short variant, offset is the offset of the actual jump or call target. In the
			// long variant, the offset is the offset to an 8-byte location containing the jump
			// target. We will use this to refer to 'now->pointer'. Both offsets are relative to the
			// last byte of the offset.

			void *mem = ((byte *)code) + ref.offset - 2;
			// Read the current contents of the memory.
			size_t original = unalignedAtomicRead(*(size_t *)mem);

			// Find out if 'jmp' or 'call' was used.
			bool call = false;
			switch (original & 0xFFFF) {
			case 0xE848:
			case 0x15FF:
				call = true;
				break;
			case 0xE948:
			case 0x25FF:
				call = false;
				break;
			default:
				dbg_assert(false, L"Unknown machine code. Jump or call expected!");
				break;
			}

			// Add the uninteresting bytes from the original.
			size_t insert = original & (size_t(0xFFFF) << 48);
			size_t delta = size_t(ref.pointer) - (size_t(mem) + 6);
			if (singleInt(delta)) {
				// Use the short variant.
				insert |= (call ? 0xE848 : 0xE948);
				insert |= (delta & 0xFFFFFFFF) << 16;
			} else {
				// Use the long variant.

				// Compute the offset to '&pointer'.
				delta = size_t(&ref.pointer) - (size_t(mem) + 6);
				insert |= (call ? 0x15FF : 0x25FF);
				insert |= (delta & 0xFFFFFFFF) << 16;
			}

			// Write the value back to memory.
			unalignedAtomicWrite(*(size_t *)mem, insert);
			invalidateICache(mem, (byte *)mem + sizeof(size_t));
		}

#endif

		void writePtr(void *code, const GcCode *refs, Nat id) {
			const GcCodeRef &ref = refs->refs[id];

			switch (ref.kind) {
			case GcCodeRef::jump:
#ifdef X64
				writeJump(code, ref);
#endif
				break;
			default:
				dbg_assert(false, L"Only 'jump' is supported by this backend.");
				break;
			}
		}

		void finalize(void *code) {
			(void)code;
		}

	}
}