Don't use 'using std::error_code' in include/llvm.
[oota-llvm.git] / lib / Support / Windows / Memory.inc
1 //===- Win32/Memory.cpp - Win32 Memory Implementation -----------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // This file provides the Win32 specific implementation of various Memory
11 // management utilities
12 //
13 //===----------------------------------------------------------------------===//
14
15 #include "llvm/Support/DataTypes.h"
16 #include "llvm/Support/ErrorHandling.h"
17 #include "llvm/Support/Process.h"
18 #include "llvm/Support/WindowsError.h"
19
20 // The Windows.h header must be the last one included.
21 #include "WindowsSupport.h"
22 using std::error_code;
23
24 namespace {
25
26 DWORD getWindowsProtectionFlags(unsigned Flags) {
27   switch (Flags) {
28   // Contrary to what you might expect, the Windows page protection flags
29   // are not a bitwise combination of RWX values
30   case llvm::sys::Memory::MF_READ:
31     return PAGE_READONLY;
32   case llvm::sys::Memory::MF_WRITE:
33     // Note: PAGE_WRITE is not supported by VirtualProtect
34     return PAGE_READWRITE;
35   case llvm::sys::Memory::MF_READ|llvm::sys::Memory::MF_WRITE:
36     return PAGE_READWRITE;
37   case llvm::sys::Memory::MF_READ|llvm::sys::Memory::MF_EXEC:
38     return PAGE_EXECUTE_READ;
39   case llvm::sys::Memory::MF_READ |
40          llvm::sys::Memory::MF_WRITE |
41          llvm::sys::Memory::MF_EXEC:
42     return PAGE_EXECUTE_READWRITE;
43   case llvm::sys::Memory::MF_EXEC:
44     return PAGE_EXECUTE;
45   default:
46     llvm_unreachable("Illegal memory protection flag specified!");
47   }
48   // Provide a default return value as required by some compilers.
49   return PAGE_NOACCESS;
50 }
51
52 size_t getAllocationGranularity() {
53   SYSTEM_INFO  Info;
54   ::GetSystemInfo(&Info);
55   if (Info.dwPageSize > Info.dwAllocationGranularity)
56     return Info.dwPageSize;
57   else
58     return Info.dwAllocationGranularity;
59 }
60
61 } // namespace
62
63 namespace llvm {
64 namespace sys {
65
66 //===----------------------------------------------------------------------===//
67 //=== WARNING: Implementation here must contain only Win32 specific code
68 //===          and must not be UNIX code
69 //===----------------------------------------------------------------------===//
70
71 MemoryBlock Memory::allocateMappedMemory(size_t NumBytes,
72                                          const MemoryBlock *const NearBlock,
73                                          unsigned Flags,
74                                          error_code &EC) {
75   EC = error_code();
76   if (NumBytes == 0)
77     return MemoryBlock();
78
79   // While we'd be happy to allocate single pages, the Windows allocation
80   // granularity may be larger than a single page (in practice, it is 64K)
81   // so mapping less than that will create an unreachable fragment of memory.
82   static const size_t Granularity = getAllocationGranularity();
83   const size_t NumBlocks = (NumBytes+Granularity-1)/Granularity;
84
85   uintptr_t Start = NearBlock ? reinterpret_cast<uintptr_t>(NearBlock->base()) +
86                                 NearBlock->size()
87                            : 0;
88
89   // If the requested address is not aligned to the allocation granularity,
90   // round up to get beyond NearBlock. VirtualAlloc would have rounded down.
91   if (Start && Start % Granularity != 0)
92     Start += Granularity - Start % Granularity;
93
94   DWORD Protect = getWindowsProtectionFlags(Flags);
95
96   void *PA = ::VirtualAlloc(reinterpret_cast<void*>(Start),
97                             NumBlocks*Granularity,
98                             MEM_RESERVE | MEM_COMMIT, Protect);
99   if (PA == NULL) {
100     if (NearBlock) {
101       // Try again without the NearBlock hint
102       return allocateMappedMemory(NumBytes, NULL, Flags, EC);
103     }
104     EC = mapWindowsError(::GetLastError());
105     return MemoryBlock();
106   }
107
108   MemoryBlock Result;
109   Result.Address = PA;
110   Result.Size = NumBlocks*Granularity;
111
112   if (Flags & MF_EXEC)
113     Memory::InvalidateInstructionCache(Result.Address, Result.Size);
114
115   return Result;
116 }
117
118 error_code Memory::releaseMappedMemory(MemoryBlock &M) {
119   if (M.Address == 0 || M.Size == 0)
120     return error_code();
121
122   if (!VirtualFree(M.Address, 0, MEM_RELEASE))
123     return mapWindowsError(::GetLastError());
124
125   M.Address = 0;
126   M.Size = 0;
127
128   return error_code();
129 }
130
131 error_code Memory::protectMappedMemory(const MemoryBlock &M,
132                                        unsigned Flags) {
133   if (M.Address == 0 || M.Size == 0)
134     return error_code();
135
136   DWORD Protect = getWindowsProtectionFlags(Flags);
137
138   DWORD OldFlags;
139   if (!VirtualProtect(M.Address, M.Size, Protect, &OldFlags))
140     return mapWindowsError(::GetLastError());
141
142   if (Flags & MF_EXEC)
143     Memory::InvalidateInstructionCache(M.Address, M.Size);
144
145   return error_code();
146 }
147
148 /// InvalidateInstructionCache - Before the JIT can run a block of code
149 /// that has been emitted it must invalidate the instruction cache on some
150 /// platforms.
151 void Memory::InvalidateInstructionCache(
152     const void *Addr, size_t Len) {
153   FlushInstructionCache(GetCurrentProcess(), Addr, Len);
154 }
155
156
157 MemoryBlock Memory::AllocateRWX(size_t NumBytes,
158                                 const MemoryBlock *NearBlock,
159                                 std::string *ErrMsg) {
160   MemoryBlock MB;
161   error_code EC;
162   MB = allocateMappedMemory(NumBytes, NearBlock,
163                             MF_READ|MF_WRITE|MF_EXEC, EC);
164   if (EC != error_code() && ErrMsg) {
165     MakeErrMsg(ErrMsg, EC.message());
166   }
167   return MB;
168 }
169
170 bool Memory::ReleaseRWX(MemoryBlock &M, std::string *ErrMsg) {
171   error_code EC = releaseMappedMemory(M);
172   if (EC == error_code())
173     return false;
174   MakeErrMsg(ErrMsg, EC.message());
175   return true;
176 }
177
178 static DWORD getProtection(const void *addr) {
179   MEMORY_BASIC_INFORMATION info;
180   if (sizeof(info) == ::VirtualQuery(addr, &info, sizeof(info))) {
181     return info.Protect;
182   }
183   return 0;
184 }
185
186 bool Memory::setWritable(MemoryBlock &M, std::string *ErrMsg) {
187   if (!setRangeWritable(M.Address, M.Size)) {
188     return MakeErrMsg(ErrMsg, "Cannot set memory to writeable: ");
189   }
190   return true;
191 }
192
193 bool Memory::setExecutable(MemoryBlock &M, std::string *ErrMsg) {
194   if (!setRangeExecutable(M.Address, M.Size)) {
195     return MakeErrMsg(ErrMsg, "Cannot set memory to executable: ");
196   }
197   return true;
198 }
199
200 bool Memory::setRangeWritable(const void *Addr, size_t Size) {
201   DWORD prot = getProtection(Addr);
202   if (!prot)
203     return false;
204
205   if (prot == PAGE_EXECUTE || prot == PAGE_EXECUTE_READ) {
206     prot = PAGE_EXECUTE_READWRITE;
207   } else if (prot == PAGE_NOACCESS || prot == PAGE_READONLY) {
208     prot = PAGE_READWRITE;
209   }
210
211   DWORD oldProt;
212   Memory::InvalidateInstructionCache(Addr, Size);
213   return ::VirtualProtect(const_cast<LPVOID>(Addr), Size, prot, &oldProt)
214             == TRUE;
215 }
216
217 bool Memory::setRangeExecutable(const void *Addr, size_t Size) {
218   DWORD prot = getProtection(Addr);
219   if (!prot)
220     return false;
221
222   if (prot == PAGE_NOACCESS) {
223     prot = PAGE_EXECUTE;
224   } else if (prot == PAGE_READONLY) {
225     prot = PAGE_EXECUTE_READ;
226   } else if (prot == PAGE_READWRITE) {
227     prot = PAGE_EXECUTE_READWRITE;
228   }
229
230   DWORD oldProt;
231   Memory::InvalidateInstructionCache(Addr, Size);
232   return ::VirtualProtect(const_cast<LPVOID>(Addr), Size, prot, &oldProt)
233             == TRUE;
234 }
235
236 } // namespace sys
237 } // namespace llvm