Assorted fixes for Win64 EH unwind info emission:
[oota-llvm.git] / lib / MC / MCWin64EH.cpp
1 //===- lib/MC/MCWin64EH.cpp - MCWin64EH implementation --------------------===//
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 #include "llvm/MC/MCWin64EH.h"
11 #include "llvm/MC/MCStreamer.h"
12 #include "llvm/MC/MCContext.h"
13 #include "llvm/MC/MCExpr.h"
14 #include "llvm/Target/TargetAsmInfo.h"
15
16 namespace llvm {
17
18 // NOTE: All relocations generated here are 4-byte image-relative.
19
20 static uint8_t CountOfUnwindCodes(std::vector<MCWin64EHInstruction> &instArray){
21   uint8_t count = 0;
22   for (std::vector<MCWin64EHInstruction>::const_iterator I = instArray.begin(),
23        E = instArray.end(); I != E; ++I) {
24     switch (I->getOperation()) {
25     case Win64EH::UOP_PushNonVol:
26     case Win64EH::UOP_AllocSmall:
27     case Win64EH::UOP_SetFPReg:
28     case Win64EH::UOP_PushMachFrame:
29       count += 1;
30       break;
31     case Win64EH::UOP_SaveNonVol:
32     case Win64EH::UOP_SaveXMM128:
33       count += 2;
34       break;
35     case Win64EH::UOP_SaveNonVolBig:
36     case Win64EH::UOP_SaveXMM128Big:
37       count += 3;
38       break;
39     case Win64EH::UOP_AllocLarge:
40       if (I->getSize() > 512*1024-8)
41         count += 3;
42       else
43         count += 2;
44       break;
45     }
46   }
47   return count;
48 }
49
50 static void EmitAbsDifference(MCStreamer &streamer, MCSymbol *lhs,
51                               MCSymbol *rhs) {
52   MCContext &context = streamer.getContext();
53   const MCExpr *diff = MCBinaryExpr::CreateSub(MCSymbolRefExpr::Create(
54                                                                   lhs, context),
55                                                MCSymbolRefExpr::Create(
56                                                                   rhs, context),
57                                                context);
58   streamer.EmitAbsValue(diff, 1);
59
60 }
61
62 static void EmitUnwindCode(MCStreamer &streamer, MCSymbol *begin,
63                            MCWin64EHInstruction &inst) {
64   uint8_t b1, b2;
65   uint16_t w;
66   b2 = (inst.getOperation() & 0x0F);
67   switch (inst.getOperation()) {
68   case Win64EH::UOP_PushNonVol:
69     EmitAbsDifference(streamer, inst.getLabel(), begin);
70     b2 |= (inst.getRegister() & 0x0F) << 4;
71     streamer.EmitIntValue(b2, 1);
72     break;
73   case Win64EH::UOP_AllocLarge:
74     EmitAbsDifference(streamer, inst.getLabel(), begin);
75     if (inst.getSize() > 512*1024-8) {
76       b2 |= 0x10;
77       streamer.EmitIntValue(b2, 1);
78       w = inst.getSize() & 0xFFF8;
79       streamer.EmitIntValue(w, 2);
80       w = inst.getSize() >> 16;
81     } else {
82       streamer.EmitIntValue(b2, 1);
83       w = inst.getSize() >> 3;
84     }
85     streamer.EmitIntValue(w, 2);
86     break;
87   case Win64EH::UOP_AllocSmall:
88     b2 |= (((inst.getSize()-8) >> 3) & 0x0F) << 4;
89     EmitAbsDifference(streamer, inst.getLabel(), begin);
90     streamer.EmitIntValue(b2, 1);
91     break;
92   case Win64EH::UOP_SetFPReg:
93     b1 = inst.getOffset() & 0xF0;
94     streamer.EmitIntValue(b1, 1);
95     streamer.EmitIntValue(b2, 1);
96     break;
97   case Win64EH::UOP_SaveNonVol:
98   case Win64EH::UOP_SaveXMM128:
99     b2 |= (inst.getRegister() & 0x0F) << 4;
100     EmitAbsDifference(streamer, inst.getLabel(), begin);
101     streamer.EmitIntValue(b2, 1);
102     w = inst.getOffset() >> 3;
103     if (inst.getOperation() == Win64EH::UOP_SaveXMM128)
104       w >>= 1;
105     streamer.EmitIntValue(w, 2);
106     break;
107   case Win64EH::UOP_SaveNonVolBig:
108   case Win64EH::UOP_SaveXMM128Big:
109     b2 |= (inst.getRegister() & 0x0F) << 4;
110     EmitAbsDifference(streamer, inst.getLabel(), begin);
111     streamer.EmitIntValue(b2, 1);
112     if (inst.getOperation() == Win64EH::UOP_SaveXMM128Big)
113       w = inst.getOffset() & 0xFFF0;
114     else
115       w = inst.getOffset() & 0xFFF8;
116     streamer.EmitIntValue(w, 2);
117     w = inst.getOffset() >> 16;
118     streamer.EmitIntValue(w, 2);
119     break;
120   case Win64EH::UOP_PushMachFrame:
121     if (inst.isPushCodeFrame())
122       b2 |= 0x10;
123     EmitAbsDifference(streamer, inst.getLabel(), begin);
124     streamer.EmitIntValue(b2, 1);
125     break;
126   }
127 }
128
129 static void EmitRuntimeFunction(MCStreamer &streamer,
130                                 const MCWin64EHUnwindInfo *info) {
131   MCContext &context = streamer.getContext();
132
133   streamer.EmitValueToAlignment(4);
134   streamer.EmitValue(MCSymbolRefExpr::Create(info->Begin, context), 4);
135   streamer.EmitValue(MCSymbolRefExpr::Create(info->End, context), 4);
136   streamer.EmitValue(MCSymbolRefExpr::Create(info->Symbol, context), 4);
137 }
138
139 static void EmitUnwindInfo(MCStreamer &streamer, MCWin64EHUnwindInfo *info) {
140   // If this UNWIND_INFO already has a symbol, it's already been emitted.
141   if (info->Symbol) return;
142
143   MCContext &context = streamer.getContext();
144   streamer.EmitValueToAlignment(4);
145   // Upper 3 bits are the version number (currently 1).
146   uint8_t flags = 0x01;
147   info->Symbol = context.CreateTempSymbol();
148   streamer.EmitLabel(info->Symbol);
149
150   if (info->ChainedParent)
151     flags |= Win64EH::UNW_ChainInfo << 3;
152   else {
153     if (info->HandlesUnwind)
154       flags |= Win64EH::UNW_TerminateHandler << 3;
155     if (info->HandlesExceptions)
156       flags |= Win64EH::UNW_ExceptionHandler << 3;
157   }
158   streamer.EmitIntValue(flags, 1);
159
160   if (info->PrologEnd)
161     EmitAbsDifference(streamer, info->PrologEnd, info->Begin);
162   else
163     streamer.EmitIntValue(0, 1);
164
165   uint8_t numCodes = CountOfUnwindCodes(info->Instructions);
166   streamer.EmitIntValue(numCodes, 1);
167
168   uint8_t frame = 0;
169   if (info->LastFrameInst >= 0) {
170     MCWin64EHInstruction &frameInst = info->Instructions[info->LastFrameInst];
171     assert(frameInst.getOperation() == Win64EH::UOP_SetFPReg);
172     frame = (frameInst.getRegister() & 0x0F) |
173             (frameInst.getOffset() & 0xF0);
174   }
175   streamer.EmitIntValue(frame, 1);
176
177   // Emit unwind instructions (in reverse order).
178   uint8_t numInst = info->Instructions.size();
179   for (uint8_t c = 0; c < numInst; ++c) {
180     MCWin64EHInstruction inst = info->Instructions.back();
181     info->Instructions.pop_back();
182     EmitUnwindCode(streamer, info->Begin, inst);
183   }
184
185   if (flags & (Win64EH::UNW_ChainInfo << 3))
186     EmitRuntimeFunction(streamer, info->ChainedParent);
187   else if (flags &
188            ((Win64EH::UNW_TerminateHandler|Win64EH::UNW_ExceptionHandler) << 3))
189     streamer.EmitValue(MCSymbolRefExpr::Create(info->ExceptionHandler, context),
190                        4);
191   else if (numCodes < 2) {
192     // The minimum size of an UNWIND_INFO struct is 8 bytes. If we're not
193     // a chained unwind info, if there is no handler, and if there are fewer
194     // than 2 slots used in the unwind code array, we have to pad to 8 bytes.
195     if (numCodes == 1)
196       streamer.EmitIntValue(0, 2);
197     else
198       streamer.EmitIntValue(0, 4);
199   }
200 }
201
202 void MCWin64EHUnwindEmitter::EmitUnwindInfo(MCStreamer &streamer,
203                                             MCWin64EHUnwindInfo *info) {
204   // Switch sections (the static function above is meant to be called from
205   // here and from Emit().
206   MCContext &context = streamer.getContext();
207   const TargetAsmInfo &asmInfo = context.getTargetAsmInfo();
208   const MCSection *xdataSect = asmInfo.getWin64EHTableSection();
209   streamer.SwitchSection(xdataSect);
210
211   llvm::EmitUnwindInfo(streamer, info);
212 }
213
214 void MCWin64EHUnwindEmitter::Emit(MCStreamer &streamer) {
215   MCContext &context = streamer.getContext();
216   // Emit the unwind info structs first.
217   const TargetAsmInfo &asmInfo = context.getTargetAsmInfo();
218   const MCSection *xdataSect = asmInfo.getWin64EHTableSection();
219   streamer.SwitchSection(xdataSect);
220   for (unsigned i = 0; i < streamer.getNumW64UnwindInfos(); ++i)
221     llvm::EmitUnwindInfo(streamer, &streamer.getW64UnwindInfo(i));
222   // Now emit RUNTIME_FUNCTION entries.
223   const MCSection *pdataSect = asmInfo.getWin64EHFuncTableSection();
224   streamer.SwitchSection(pdataSect);
225   for (unsigned i = 0; i < streamer.getNumW64UnwindInfos(); ++i)
226     EmitRuntimeFunction(streamer, &streamer.getW64UnwindInfo(i));
227 }
228
229 } // End of namespace llvm
230