Make the COFF streamer emit unwind info when processing a .seh_handlerdata
[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     case Win64EH::UOP_SaveNonVol:
31     case Win64EH::UOP_SaveXMM128:
32       count += 2;
33     case Win64EH::UOP_SaveNonVolBig:
34     case Win64EH::UOP_SaveXMM128Big:
35       count += 3;
36     case Win64EH::UOP_AllocLarge:
37       if (I->getSize() > 512*1024-8)
38         count += 3;
39       else
40         count += 2;
41     }
42   }
43   return count;
44 }
45
46 static void EmitUnwindCode(MCStreamer &streamer, MCWin64EHInstruction &inst) {
47   uint8_t b1, b2;
48   uint16_t w;
49   b2 = (inst.getOperation() & 0x0F) << 4;
50   switch (inst.getOperation()) {
51   case Win64EH::UOP_PushNonVol:
52     streamer.EmitIntValue(0, 1);
53     b2 |= inst.getRegister() & 0x0F;
54     streamer.EmitIntValue(b2, 1);
55     break;
56   case Win64EH::UOP_AllocLarge:
57     streamer.EmitIntValue(0, 1);
58     if (inst.getSize() > 512*1024-8) {
59       b2 |= 1;
60       streamer.EmitIntValue(b2, 1);
61       w = inst.getSize() & 0xFFF8;
62       streamer.EmitIntValue(w, 2);
63       w = inst.getSize() >> 16;
64     } else {
65       streamer.EmitIntValue(b2, 1);
66       w = inst.getSize() >> 3;
67     }
68     streamer.EmitIntValue(w, 2);
69     break;
70   case Win64EH::UOP_AllocSmall:
71     b2 |= (inst.getSize() >> 3) & 0x0F;
72     streamer.EmitIntValue(0, 1);
73     streamer.EmitIntValue(b2, 1);
74     break;
75   case Win64EH::UOP_SetFPReg:
76     b1 = inst.getOffset() & 0xF0;
77     streamer.EmitIntValue(b1, 1);
78     streamer.EmitIntValue(b2, 1);
79     break;
80   case Win64EH::UOP_SaveNonVol:
81   case Win64EH::UOP_SaveXMM128:
82     b2 |= inst.getRegister() & 0x0F;
83     streamer.EmitIntValue(0, 1);
84     streamer.EmitIntValue(b2, 1);
85     w = inst.getOffset() >> 3;
86     if (inst.getOperation() == Win64EH::UOP_SaveXMM128)
87       w >>= 1;
88     streamer.EmitIntValue(w, 2);
89     break;
90   case Win64EH::UOP_SaveNonVolBig:
91   case Win64EH::UOP_SaveXMM128Big:
92     b2 |= inst.getRegister() & 0x0F;
93     streamer.EmitIntValue(0, 1);
94     streamer.EmitIntValue(b2, 1);
95     if (inst.getOperation() == Win64EH::UOP_SaveXMM128Big)
96       w = inst.getOffset() & 0xFFF0;
97     else
98       w = inst.getOffset() & 0xFFF8;
99     streamer.EmitIntValue(w, 2);
100     w = inst.getOffset() >> 16;
101     streamer.EmitIntValue(w, 2);
102     break;
103   case Win64EH::UOP_PushMachFrame:
104     if (inst.isPushCodeFrame())
105       b2 |= 1;
106     streamer.EmitIntValue(0, 1);
107     streamer.EmitIntValue(b2, 1);
108     break;
109   }
110 }
111
112 static void EmitRuntimeFunction(MCStreamer &streamer,
113                                 MCWin64EHUnwindInfo *info) {
114   MCContext &context = streamer.getContext();
115
116   streamer.EmitValue(MCSymbolRefExpr::Create(info->Begin, context), 4);
117   streamer.EmitValue(MCSymbolRefExpr::Create(info->End, context), 4);
118   streamer.EmitValue(MCSymbolRefExpr::Create(info->Symbol, context), 4);
119 }
120
121 static void EmitUnwindInfo(MCStreamer &streamer, MCWin64EHUnwindInfo *info) {
122   // If this UNWIND_INFO already has a symbol, it's already been emitted.
123   if (info->Symbol) return;
124
125   MCContext &context = streamer.getContext();
126   // Upper 3 bits are the version number (currently 1).
127   uint8_t flags = 0x20;
128   info->Symbol = context.CreateTempSymbol();
129   streamer.EmitLabel(info->Symbol);
130
131   if (info->ChainedParent)
132     flags |= Win64EH::UNW_ChainInfo;
133   else {
134     if (info->HandlesUnwind)
135       flags |= Win64EH::UNW_TerminateHandler;
136     if (info->HandlesExceptions)
137       flags |= Win64EH::UNW_ExceptionHandler;
138   }
139   streamer.EmitIntValue(flags, 1);
140
141   // Build up the prolog size expression.
142   const MCExpr *prologSize = MCBinaryExpr::CreateSub(MCSymbolRefExpr::Create(
143                                                       info->PrologEnd, context),
144                                                      MCSymbolRefExpr::Create(
145                                                           info->Begin, context),
146                                                      context);
147   streamer.EmitAbsValue(prologSize, 1);
148
149   uint8_t numCodes = CountOfUnwindCodes(info->Instructions);
150   streamer.EmitIntValue(numCodes, 1);
151
152   uint8_t frame = 0;
153   if (info->LastFrameInst >= 0) {
154     MCWin64EHInstruction &frameInst = info->Instructions[info->LastFrameInst];
155     assert(frameInst.getOperation() == Win64EH::UOP_SetFPReg);
156     frame = ((frameInst.getRegister() & 0x0F) << 4) |
157             ((frameInst.getOffset() >> 4) & 0x0F);
158   }
159   streamer.EmitIntValue(frame, 1);
160
161   // Emit unwind instructions (in reverse order).
162   uint8_t numInst = info->Instructions.size();
163   for (uint8_t c = 0; c < numInst; ++c) {
164     MCWin64EHInstruction inst = info->Instructions.back();
165     info->Instructions.pop_back();
166     EmitUnwindCode(streamer, inst);
167   }
168
169   if (flags & Win64EH::UNW_ChainInfo)
170     EmitRuntimeFunction(streamer, info->ChainedParent);
171   else if (flags &(Win64EH::UNW_TerminateHandler|Win64EH::UNW_ExceptionHandler))
172     streamer.EmitValue(MCSymbolRefExpr::Create(info->ExceptionHandler, context),
173                        4);
174 }
175
176 void MCWin64EHUnwindEmitter::EmitUnwindInfo(MCStreamer &streamer,
177                                             MCWin64EHUnwindInfo *info) {
178   // Switch sections (the static function above is meant to be called from
179   // here and from Emit().
180   MCContext &context = streamer.getContext();
181   const TargetAsmInfo &asmInfo = context.getTargetAsmInfo();
182   const MCSection *xdataSect = asmInfo.getWin64EHTableSection();
183   streamer.SwitchSection(xdataSect);
184
185   llvm::EmitUnwindInfo(streamer, info);
186 }
187
188 } // End of namespace llvm
189